481 lines
9.6 KiB
C++
481 lines
9.6 KiB
C++
|
//-----------------------------------------------------------------------------
|
||
|
// File: flexcombobox.cpp
|
||
|
//
|
||
|
// Desc: Implements a combo box control similar to Windows combo box.
|
||
|
// CFlexComboBox is derived from CFlexWnd. It is used by the page
|
||
|
// for player list and genre list. When the combo box is open,
|
||
|
// CFlexComboBox uses a CFlexListBox for the list window.
|
||
|
//
|
||
|
// Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
#include "common.hpp"
|
||
|
|
||
|
|
||
|
CFlexComboBox::CFlexComboBox() :
|
||
|
m_nTextHeight(-1),
|
||
|
m_hWndNotify(NULL),
|
||
|
m_rgbText(RGB(255,255,255)),
|
||
|
m_rgbBk(RGB(0,0,0)),
|
||
|
m_rgbSelText(RGB(0,0,255)),
|
||
|
m_rgbSelBk(RGB(0,0,0)),
|
||
|
m_rgbFill(RGB(0,0,255)),
|
||
|
m_rgbLine(RGB(0,255,255)),
|
||
|
m_dwFlags(0),
|
||
|
m_dwListBoxFlags(0),
|
||
|
m_bInSelMode(FALSE),
|
||
|
m_nSBWidth(11),
|
||
|
m_hFont(NULL),
|
||
|
m_eCurState(FCBS_CLOSED),
|
||
|
m_OldSel(-1)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CFlexComboBox::~CFlexComboBox()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CFlexComboBox *CreateFlexComboBox(FLEXCOMBOBOXCREATESTRUCT *pcs)
|
||
|
{
|
||
|
CFlexComboBox *psb = new CFlexComboBox;
|
||
|
|
||
|
if (psb && psb->Create(pcs))
|
||
|
return psb;
|
||
|
|
||
|
delete psb;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
BOOL CFlexComboBox::Create(FLEXCOMBOBOXCREATESTRUCT *pcs)
|
||
|
{
|
||
|
if (this == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
if (pcs == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
if (pcs->dwSize != sizeof(FLEXCOMBOBOXCREATESTRUCT))
|
||
|
return FALSE;
|
||
|
|
||
|
m_hWndNotify = pcs->hWndNotify ? pcs->hWndNotify : pcs->hWndParent;
|
||
|
|
||
|
m_dwFlags = pcs->dwFlags;
|
||
|
m_dwListBoxFlags = pcs->dwListBoxFlags;
|
||
|
|
||
|
SetFont(pcs->hFont);
|
||
|
SetColors(pcs->rgbText, pcs->rgbBk, pcs->rgbSelText, pcs->rgbSelBk, pcs->rgbFill, pcs->rgbLine);
|
||
|
m_nSBWidth = pcs->nSBWidth;
|
||
|
m_rect = pcs->rect;
|
||
|
|
||
|
if (!CFlexWnd::Create(pcs->hWndParent, GetRect(pcs->rect), pcs->bVisible))
|
||
|
return FALSE;
|
||
|
|
||
|
//@@BEGIN_MSINTERNAL
|
||
|
// TODO: make sure that creation sends no notifications.
|
||
|
// all initial notifications should be sent here.
|
||
|
//@@END_MSINTERNAL
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void CFlexComboBox::OnPaint(HDC hDC)
|
||
|
{
|
||
|
HDC hBDC = NULL, hODC = NULL;
|
||
|
CBitmap *pbm = NULL;
|
||
|
|
||
|
if (!InRenderMode())
|
||
|
{
|
||
|
hODC = hDC;
|
||
|
pbm = CBitmap::Create(GetClientSize(), RGB(0,0,0), hDC);
|
||
|
if (pbm != NULL)
|
||
|
{
|
||
|
hBDC = pbm->BeginPaintInto();
|
||
|
if (hBDC != NULL)
|
||
|
{
|
||
|
hDC = hBDC;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
InternalPaint(hDC);
|
||
|
|
||
|
if (!InRenderMode())
|
||
|
{
|
||
|
if (pbm != NULL)
|
||
|
{
|
||
|
if (hBDC != NULL)
|
||
|
{
|
||
|
pbm->EndPaintInto(hBDC);
|
||
|
pbm->Draw(hODC);
|
||
|
}
|
||
|
delete pbm;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CFlexComboBox::SetSel(int i)
|
||
|
{
|
||
|
m_ListBox.SelectAndShowSingleItem(i, TRUE);
|
||
|
}
|
||
|
|
||
|
int CFlexComboBox::GetSel()
|
||
|
{
|
||
|
return m_ListBox.GetSel();
|
||
|
}
|
||
|
|
||
|
LPCTSTR CFlexComboBox::GetText()
|
||
|
{
|
||
|
return m_ListBox.GetSelText();
|
||
|
}
|
||
|
|
||
|
void CFlexComboBox::InternalPaint(HDC hDC)
|
||
|
{
|
||
|
HGDIOBJ hPen = (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbLine);
|
||
|
if (hPen != NULL)
|
||
|
{
|
||
|
HGDIOBJ hOldPen = SelectObject(hDC, hPen);
|
||
|
|
||
|
HGDIOBJ hBrush = (HGDIOBJ)CreateSolidBrush(m_rgbBk);
|
||
|
if (hBrush != NULL)
|
||
|
{
|
||
|
HGDIOBJ hOldBrush = SelectObject(hDC, hBrush);
|
||
|
|
||
|
RECT rect = {0,0,0,0};
|
||
|
GetClientRect(&rect);
|
||
|
Rectangle(hDC, rect.left, rect.top, rect.right, rect.bottom);
|
||
|
|
||
|
RECT arect = rect;
|
||
|
|
||
|
arect.left = arect.right - (arect.bottom - arect.top);
|
||
|
|
||
|
// If we are read-only, only draw the text in gray. No border, no arrow.
|
||
|
if (!GetReadOnly())
|
||
|
{
|
||
|
MoveToEx(hDC, arect.left, arect.top, NULL);
|
||
|
LineTo(hDC, arect.left, arect.bottom);
|
||
|
}
|
||
|
|
||
|
rect.left++;
|
||
|
rect.top++;
|
||
|
rect.right = arect.left;
|
||
|
rect.bottom--;
|
||
|
|
||
|
SetTextColor(hDC, m_rgbText);
|
||
|
SetBkMode(hDC, TRANSPARENT);
|
||
|
|
||
|
LPTSTR lpText = (LPTSTR)GetText();
|
||
|
if (lpText)
|
||
|
{
|
||
|
DrawText(hDC, lpText, -1, &rect, DT_NOPREFIX);
|
||
|
}
|
||
|
|
||
|
SelectObject(hDC, hOldBrush);
|
||
|
DeleteObject(hBrush);
|
||
|
|
||
|
if (!GetReadOnly())
|
||
|
{
|
||
|
hBrush = (HGDIOBJ)CreateSolidBrush(m_rgbFill);
|
||
|
if (hBrush != NULL)
|
||
|
{
|
||
|
SelectObject(hDC, hBrush);
|
||
|
|
||
|
InflateRect(&arect, -3, -3);
|
||
|
DrawArrow(hDC, arect, TRUE, FALSE);
|
||
|
|
||
|
SelectObject(hDC, hOldBrush);
|
||
|
DeleteObject(hBrush);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SelectObject(hDC, hOldPen);
|
||
|
DeleteObject(hPen);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int CFlexComboBox::AddString(LPCTSTR str)
|
||
|
{
|
||
|
return m_ListBox.AddString(str);
|
||
|
}
|
||
|
|
||
|
LRESULT CFlexComboBox::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
RECT wrect = {-1, -1, -1, -1};
|
||
|
POINT point = {-1, -1};
|
||
|
BOOL bWithin = FALSE;
|
||
|
|
||
|
switch (msg)
|
||
|
{
|
||
|
case WM_SIZE:
|
||
|
Invalidate();
|
||
|
SetRect();
|
||
|
return 0;
|
||
|
|
||
|
case WM_FLEXLISTBOX:
|
||
|
assert(lParam == (LPARAM)(LPVOID)&m_ListBox);
|
||
|
switch (wParam)
|
||
|
{
|
||
|
case FLBN_FINALSEL:
|
||
|
StateEvent(FCBSE_UPLIST);
|
||
|
break;
|
||
|
case FLBN_CANCEL:
|
||
|
StateEvent(FCBSE_DOWNOFF);
|
||
|
break;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
// make sure flexwnd doesn't do ANYTHING with our mouse messages
|
||
|
case WM_MOUSEMOVE:
|
||
|
// We initialize the tooltip to current selection text if the selected text is too long to fit.
|
||
|
RECT rect;
|
||
|
GetClientRect(&rect);
|
||
|
rect.right = rect.right - (rect.bottom - rect.top);
|
||
|
rect.left++;
|
||
|
rect.top++;
|
||
|
rect.bottom--;
|
||
|
RECT ResultRect;
|
||
|
ResultRect = rect;
|
||
|
HDC hDC;
|
||
|
hDC = CreateCompatibleDC(NULL);
|
||
|
if (hDC)
|
||
|
{
|
||
|
LPTSTR lpText = (LPTSTR)GetText();
|
||
|
if (lpText)
|
||
|
{
|
||
|
DrawText(hDC, lpText, -1, &ResultRect, DT_NOPREFIX|DT_CALCRECT);
|
||
|
}
|
||
|
DeleteDC(hDC);
|
||
|
}
|
||
|
if (rect.right < ResultRect.right || rect.bottom < ResultRect.bottom)
|
||
|
{
|
||
|
CFlexWnd::s_ToolTip.SetToolTipParent(GetParent(m_hWnd));
|
||
|
TOOLTIPINITPARAM ttip;
|
||
|
ttip.hWndParent = GetParent(m_hWnd);
|
||
|
ttip.iSBWidth = 0;
|
||
|
ttip.dwID = 0;
|
||
|
ttip.hWndNotify = m_hWnd;
|
||
|
ttip.tszCaption = m_ListBox.GetSelText();
|
||
|
CFlexToolTip::UpdateToolTipParam(ttip);
|
||
|
}
|
||
|
Notify(FCBN_MOUSEOVER);
|
||
|
|
||
|
case WM_LBUTTONUP:
|
||
|
case WM_LBUTTONDOWN:
|
||
|
case WM_RBUTTONUP:
|
||
|
case WM_RBUTTONDOWN:
|
||
|
case WM_LBUTTONDBLCLK:
|
||
|
case WM_RBUTTONDBLCLK:
|
||
|
if (msg == WM_LBUTTONDOWN)
|
||
|
{
|
||
|
HWND hWndParent;
|
||
|
hWndParent = GetParent(hWnd);
|
||
|
SendMessage(hWndParent, WM_UNHIGHLIGHT, 0, 0); // Send click message to page to unhighlight callout
|
||
|
}
|
||
|
GetClientRect(&wrect);
|
||
|
point.x = int(LOWORD(lParam));
|
||
|
point.y = int(HIWORD(lParam));
|
||
|
bWithin = PtInRect(&wrect, point);
|
||
|
break;
|
||
|
case WM_TIMER:
|
||
|
case WM_CAPTURECHANGED:
|
||
|
break;
|
||
|
default:
|
||
|
return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
switch (msg)
|
||
|
{
|
||
|
case WM_LBUTTONDOWN:
|
||
|
if (!GetReadOnly())
|
||
|
StateEvent(bWithin ? FCBSE_DOWN : FCBSE_DOWNOFF);
|
||
|
break;
|
||
|
|
||
|
case WM_LBUTTONUP:
|
||
|
if (!GetReadOnly())
|
||
|
StateEvent(bWithin ? FCBSE_UPBOX : FCBSE_UPOFF);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
RECT CFlexComboBox::GetListBoxRect()
|
||
|
{
|
||
|
HWND hParent = GetParent(m_hWnd);
|
||
|
RECT rect;
|
||
|
GetClientRect(&rect);
|
||
|
BOOL bRet = ClientToScreen(m_hWnd, &rect);
|
||
|
BOOL bRet2 = ScreenToClient(hParent, &rect);
|
||
|
|
||
|
RECT lrect = m_rect;
|
||
|
lrect.top = rect.bottom;
|
||
|
lrect.right -= 12; // UNDONE: remove this line when the clipping is working properly (scroll bars don't appear above other windows)
|
||
|
|
||
|
return lrect;
|
||
|
}
|
||
|
|
||
|
void CFlexComboBox::DoSel()
|
||
|
{
|
||
|
if (m_bInSelMode)
|
||
|
return;
|
||
|
|
||
|
if (m_hWnd == NULL)
|
||
|
return;
|
||
|
|
||
|
FLEXLISTBOXCREATESTRUCT cs;
|
||
|
cs.dwSize = sizeof(FLEXLISTBOXCREATESTRUCT);
|
||
|
cs.dwFlags = m_dwListBoxFlags;
|
||
|
cs.hWndParent = GetParent(m_hWnd);
|
||
|
cs.hWndNotify = m_hWnd;
|
||
|
cs.bVisible = FALSE;
|
||
|
cs.rect = GetListBoxRect();
|
||
|
cs.hFont = m_hFont;
|
||
|
cs.rgbText = m_rgbText;
|
||
|
cs.rgbBk = m_rgbBk;
|
||
|
cs.rgbSelText = m_rgbSelText;
|
||
|
cs.rgbSelBk = m_rgbSelBk;
|
||
|
cs.rgbFill = m_rgbFill;
|
||
|
cs.rgbLine = m_rgbLine;
|
||
|
cs.nSBWidth = m_nSBWidth;
|
||
|
|
||
|
m_OldSel = m_ListBox.GetSel();
|
||
|
|
||
|
m_bInSelMode = m_ListBox.Create(&cs);
|
||
|
if (m_bInSelMode)
|
||
|
SetWindowPos(m_ListBox.m_hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
||
|
}
|
||
|
|
||
|
void CFlexComboBox::Notify(int code)
|
||
|
{
|
||
|
if (!m_hWndNotify)
|
||
|
return;
|
||
|
|
||
|
SendMessage(m_hWndNotify, WM_FLEXCOMBOBOX,
|
||
|
(WPARAM)code, (LPARAM)(LPVOID)this);
|
||
|
}
|
||
|
|
||
|
RECT CFlexComboBox::GetRect(const RECT &rect)
|
||
|
{
|
||
|
int h = GetTextHeight(m_hFont);
|
||
|
RECT ret = {rect.left, rect.top, rect.right, rect.top + h + 2};
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
RECT CFlexComboBox::GetRect()
|
||
|
{
|
||
|
RECT rect;
|
||
|
GetClientRect(&rect);
|
||
|
return GetRect(rect);
|
||
|
}
|
||
|
|
||
|
void CFlexComboBox::SetFont(HFONT hFont)
|
||
|
{
|
||
|
m_hFont = hFont;
|
||
|
|
||
|
if (m_hWnd == NULL)
|
||
|
return;
|
||
|
|
||
|
Invalidate();
|
||
|
SetRect();
|
||
|
}
|
||
|
|
||
|
void CFlexComboBox::SetRect()
|
||
|
{
|
||
|
if (m_hWnd == NULL)
|
||
|
return;
|
||
|
|
||
|
RECT rect = GetRect();
|
||
|
SetWindowPos(m_hWnd, NULL, rect.left, rect.top, rect.right, rect.bottom, SWP_NOZORDER | SWP_NOMOVE);
|
||
|
}
|
||
|
|
||
|
void CFlexComboBox::SetColors(COLORREF text, COLORREF bk, COLORREF seltext, COLORREF selbk, COLORREF fill, COLORREF line)
|
||
|
{
|
||
|
m_rgbText = text;
|
||
|
m_rgbBk = bk;
|
||
|
m_rgbSelText = seltext;
|
||
|
m_rgbSelBk = selbk;
|
||
|
m_rgbFill = fill;
|
||
|
m_rgbLine = line;
|
||
|
Invalidate();
|
||
|
}
|
||
|
|
||
|
void CFlexComboBox::StateEvent(FCBSTATEEVENT e)
|
||
|
{
|
||
|
if (e == FCBSE_DOWNOFF)
|
||
|
{
|
||
|
SetState(FCBS_CANCEL);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch (m_eCurState)
|
||
|
{
|
||
|
case FCBS_CLOSED:
|
||
|
if (e == FCBSE_DOWN)
|
||
|
SetState(FCBS_OPENDOWN);
|
||
|
break;
|
||
|
|
||
|
case FCBS_OPENDOWN:
|
||
|
switch (e)
|
||
|
{
|
||
|
case FCBSE_UPLIST:
|
||
|
SetState(FCBS_SELECT);
|
||
|
break;
|
||
|
case FCBSE_UPBOX:
|
||
|
SetState(FCBS_OPENUP);
|
||
|
break;
|
||
|
case FCBSE_UPOFF:
|
||
|
SetState(FCBS_CANCEL);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case FCBS_OPENUP:
|
||
|
if (e == FCBSE_DOWN)
|
||
|
SetState(FCBS_OPENDOWN);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
assert(0);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CFlexComboBox::SetState(FCBSTATE s)
|
||
|
{
|
||
|
FCBSTATE eOldState = m_eCurState;
|
||
|
m_eCurState = s;
|
||
|
|
||
|
switch (s)
|
||
|
{
|
||
|
case FCBS_OPENUP:
|
||
|
if (eOldState == FCBS_CLOSED)
|
||
|
DoSel();
|
||
|
return;
|
||
|
|
||
|
case FCBS_OPENDOWN:
|
||
|
if (eOldState == FCBS_CLOSED)
|
||
|
DoSel();
|
||
|
m_ListBox.StartSel();
|
||
|
return;
|
||
|
|
||
|
case FCBS_CANCEL:
|
||
|
m_ListBox.SetSel(m_OldSel);
|
||
|
CFlexWnd::s_ToolTip.SetEnable(FALSE);
|
||
|
SetState(FCBS_CLOSED);
|
||
|
return;
|
||
|
|
||
|
case FCBS_SELECT:
|
||
|
CFlexWnd::s_ToolTip.SetEnable(FALSE);
|
||
|
Invalidate();
|
||
|
Notify(FCBN_SELCHANGE);
|
||
|
SetState(FCBS_CLOSED);
|
||
|
return;
|
||
|
|
||
|
case FCBS_CLOSED:
|
||
|
if (eOldState != FCBS_CLOSED)
|
||
|
m_ListBox.Destroy();
|
||
|
m_bInSelMode = FALSE;
|
||
|
Invalidate();
|
||
|
return;
|
||
|
}
|
||
|
}
|