windows-nt/Source/XPSP1/NT/multimedia/directx/dinput/diconfig/flexcombobox.cpp
2020-09-26 16:20:57 +08:00

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;
}
}