Выбрать главу

protected:

 const int m_nNoIndex; // Пустой индекс

 CPoint m_LastMouseMovePoint; // Последние координаты курсора мыши

 BOOL m_bMouseCaptured; // Захвачена ли мышь?

 CTitleTip m_TitleTip; // Показываемый элемент TitleTip

 // Этот метод должен быть переопределен элементом "список" с пользовательской отрисовкой.

 virtual int GetIdealItemRect(int nIndex, LPRECT lpRect);

 void AdjustTitleTip(int nNewIndex);

 void CaptureMouse();

 BOOL IsAppActive();

 // Generated message map functions

protected:

 //{{AFX_MSG(CTitleTipListBox)

 afx_msg void OnMouseMove(UINT nFlags, CPoint point);

 afx_msg void OnSelchange();

 afx_msg void OnKillFocus(CWnd* pNewWnd);

 afx_msg void OnDestroy();

 afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

 afx_msg void OnLButtonUp(UINT nFlags, CPoint point);

 //}}AFX_MSG

 afx_msg LONG OnContentChanged(UINT, LONG);

 DECLARE_MESSAGE_MAP()

};

#endif // __TITLETIPLISTBOX_H__

/////////////////////////////////////////////////////////////////////////////

// TitleTipListBox.cpp : implementation file

//

#include "stdafx.h"

#include "TitleTipListBox.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE static char THIS_FILE[] = __FILE__;

#endif

/////////////////////////////////////////////////////////////////////////////

// CTitleTipListBox

CTitleTipListBox::CTitleTipListBox() : m_LastMouseMovePoint(0, 0) , m_nNoIndex(-1) {

 m_bMouseCaptured = FALSE;

}

CTitleTipListBox::~CTitleTipListBox() {

 ASSERT(!m_bMouseCaptured);

}

int CTitleTipListBox::GetIdealItemRect(int nIndex, LPRECT lpRect) {

 // Вычислить размеры идеальной строки. Размеры зависят

 // от длины строки. Это работает только для обычных элементов

 // "список"(без пользовательской отрисовки)

 ASSERT(lpRect);

 ASSERT(nIndex >= 0);

 DWORD dwStyle = GetStyle();

 int nStatus = GetItemRect(nIndex, lpRect);

 if (nStatus != LB_ERR && !(dwStyle & LBS_OWNERDRAWFIXED) && !(dwStyle & LBS_OWNERDRAWVARIABLE)) {

  CString strItem;

  GetText(nIndex, strItem);

  if (!strItem.IsEmpty()) {

   // Вычислить длину идеального текста.

   CClientDC DC(this);

   CFont* pOldFont = DC.SelectObject(GetFont());

   CSize ItemSize = DC.GetTextExtent(strItem);

   DC.SelectObject(pOldFont);

   // Взять максимум от обычной ширины и идеальной ширины.

   const int cxEdgeSpace = 2;

   lpRect->right = max(lpRect->right, lpRect->left + ItemSize.cx + (cxEdgeSpace * 2));

  }

 } else {

  TRACE("Owner-draw listbox detected – override CTitleTipListBox::GetIdeaItemRect()\n");

 }

 return nStatus;

}

void CTitleTipListBox::AdjustTitleTip(int nNewIndex) {

 if (!::IsWindow(m_TitleTip.m_hWnd)) {

  VERIFY(m_TitleTip.Create(this));

 }

 if (nNewIndex == m_nNoIndex) {

  m_TitleTip.Hide();

 } else {

  CRect IdealItemRect;

  GetIdealItemRect(nNewIndex, IdealItemRect);

  CRect ItemRect;

  GetItemRect(nNewIndex, ItemRect);

  if (ItemRect == IdealItemRect) {

   m_TitleTip.Hide();

  } else {

   // Поправить координаты рядом с краем экрана.

   ClientToScreen(IdealItemRect);

   int nScreenWidth = ::GetSystemMetrics(SM_CXFULLSCREEN);

   if (IdealItemRect.right > nScreenWidth) {

    IdealItemRect.OffsetRect(nScreenWidth – IdealItemRect.right, 0);

   }

   if (IdealItemRect.left < 0) {

    IdealItemRect.OffsetRect(-IdealItemRect.left, 0);

   }

   m_TitleTip.Show(IdealItemRect, nNewIndex);

  }

 }

 if (m_TitleTip.IsWindowVisible()) {

  // Удостовериться, что мышь захвачена, чтобы отследить

  // момент отключения подсказки.

  if (!m_bMouseCaptured && GetCapture() != this) {

   CaptureMouse();

  }

 } else {

  // Подсказка невидима, поэтому освободить мышь.

  if (m_bMouseCaptured) {

   VERIFY(ReleaseCapture());

   m_bMouseCaptured = FALSE;

  }

 }

}

void CTitleTipListBox::CaptureMouse() {

 ASSERT(!m_bMouseCaptured);

 CPoint Point;

 VERIFY(GetCursorPos(&Point));

 ScreenToClient(&Point);

 m_LastMouseMovePoint = Point;

 SetCapture();

 m_bMouseCaptured = TRUE;

}

/////////////////////////////////////////////////////////////////////////////

// CTitleTipListBox message handlers

LONG CTitleTipListBox::OnContentChanged(UINT, LONG) {

 // Turn off title tip.

 AdjustTitleTip(m_nNoIndex);

 return Default();

}

void CTitleTipListBox::OnMouseMove(UINT nFlags, CPoint point) {

 if (point != m_LastMouseMovePoint && IsAppActive()) {

  m_LastMouseMovePoint = point;

  int nIndexHit = m_nNoIndex;

  CRect ClientRect;

  GetClientRect(ClientRect);

  if (ClientRect.PtInRect(point)) {

   // Hit test.

   for (int n = 0; nIndexHit == m_nNoIndex && n < GetCount(); n++) {

    CRect ItemRect;

    GetItemRect(n, ItemRect);

    if (ItemRect.PtInRect(point)) {

     nIndexHit = n;

    }

   }

  }

  AdjustTitleTip(nIndexHit);

 }

 CListBox::OnMouseMove(nFlags, point);

}

void CTitleTipListBox::OnSelchange() {