Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

480 lines
9.6 KiB

  1. //-----------------------------------------------------------------------------
  2. // File: flexcombobox.cpp
  3. //
  4. // Desc: Implements a combo box control similar to Windows combo box.
  5. // CFlexComboBox is derived from CFlexWnd. It is used by the page
  6. // for player list and genre list. When the combo box is open,
  7. // CFlexComboBox uses a CFlexListBox for the list window.
  8. //
  9. // Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
  10. //-----------------------------------------------------------------------------
  11. #include "common.hpp"
  12. CFlexComboBox::CFlexComboBox() :
  13. m_nTextHeight(-1),
  14. m_hWndNotify(NULL),
  15. m_rgbText(RGB(255,255,255)),
  16. m_rgbBk(RGB(0,0,0)),
  17. m_rgbSelText(RGB(0,0,255)),
  18. m_rgbSelBk(RGB(0,0,0)),
  19. m_rgbFill(RGB(0,0,255)),
  20. m_rgbLine(RGB(0,255,255)),
  21. m_dwFlags(0),
  22. m_dwListBoxFlags(0),
  23. m_bInSelMode(FALSE),
  24. m_nSBWidth(11),
  25. m_hFont(NULL),
  26. m_eCurState(FCBS_CLOSED),
  27. m_OldSel(-1)
  28. {
  29. }
  30. CFlexComboBox::~CFlexComboBox()
  31. {
  32. }
  33. CFlexComboBox *CreateFlexComboBox(FLEXCOMBOBOXCREATESTRUCT *pcs)
  34. {
  35. CFlexComboBox *psb = new CFlexComboBox;
  36. if (psb && psb->Create(pcs))
  37. return psb;
  38. delete psb;
  39. return NULL;
  40. }
  41. BOOL CFlexComboBox::Create(FLEXCOMBOBOXCREATESTRUCT *pcs)
  42. {
  43. if (this == NULL)
  44. return FALSE;
  45. if (pcs == NULL)
  46. return FALSE;
  47. if (pcs->dwSize != sizeof(FLEXCOMBOBOXCREATESTRUCT))
  48. return FALSE;
  49. m_hWndNotify = pcs->hWndNotify ? pcs->hWndNotify : pcs->hWndParent;
  50. m_dwFlags = pcs->dwFlags;
  51. m_dwListBoxFlags = pcs->dwListBoxFlags;
  52. SetFont(pcs->hFont);
  53. SetColors(pcs->rgbText, pcs->rgbBk, pcs->rgbSelText, pcs->rgbSelBk, pcs->rgbFill, pcs->rgbLine);
  54. m_nSBWidth = pcs->nSBWidth;
  55. m_rect = pcs->rect;
  56. if (!CFlexWnd::Create(pcs->hWndParent, GetRect(pcs->rect), pcs->bVisible))
  57. return FALSE;
  58. //@@BEGIN_MSINTERNAL
  59. // TODO: make sure that creation sends no notifications.
  60. // all initial notifications should be sent here.
  61. //@@END_MSINTERNAL
  62. return TRUE;
  63. }
  64. void CFlexComboBox::OnPaint(HDC hDC)
  65. {
  66. HDC hBDC = NULL, hODC = NULL;
  67. CBitmap *pbm = NULL;
  68. if (!InRenderMode())
  69. {
  70. hODC = hDC;
  71. pbm = CBitmap::Create(GetClientSize(), RGB(0,0,0), hDC);
  72. if (pbm != NULL)
  73. {
  74. hBDC = pbm->BeginPaintInto();
  75. if (hBDC != NULL)
  76. {
  77. hDC = hBDC;
  78. }
  79. }
  80. }
  81. InternalPaint(hDC);
  82. if (!InRenderMode())
  83. {
  84. if (pbm != NULL)
  85. {
  86. if (hBDC != NULL)
  87. {
  88. pbm->EndPaintInto(hBDC);
  89. pbm->Draw(hODC);
  90. }
  91. delete pbm;
  92. }
  93. }
  94. }
  95. void CFlexComboBox::SetSel(int i)
  96. {
  97. m_ListBox.SelectAndShowSingleItem(i, TRUE);
  98. }
  99. int CFlexComboBox::GetSel()
  100. {
  101. return m_ListBox.GetSel();
  102. }
  103. LPCTSTR CFlexComboBox::GetText()
  104. {
  105. return m_ListBox.GetSelText();
  106. }
  107. void CFlexComboBox::InternalPaint(HDC hDC)
  108. {
  109. HGDIOBJ hPen = (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbLine);
  110. if (hPen != NULL)
  111. {
  112. HGDIOBJ hOldPen = SelectObject(hDC, hPen);
  113. HGDIOBJ hBrush = (HGDIOBJ)CreateSolidBrush(m_rgbBk);
  114. if (hBrush != NULL)
  115. {
  116. HGDIOBJ hOldBrush = SelectObject(hDC, hBrush);
  117. RECT rect = {0,0,0,0};
  118. GetClientRect(&rect);
  119. Rectangle(hDC, rect.left, rect.top, rect.right, rect.bottom);
  120. RECT arect = rect;
  121. arect.left = arect.right - (arect.bottom - arect.top);
  122. // If we are read-only, only draw the text in gray. No border, no arrow.
  123. if (!GetReadOnly())
  124. {
  125. MoveToEx(hDC, arect.left, arect.top, NULL);
  126. LineTo(hDC, arect.left, arect.bottom);
  127. }
  128. rect.left++;
  129. rect.top++;
  130. rect.right = arect.left;
  131. rect.bottom--;
  132. SetTextColor(hDC, m_rgbText);
  133. SetBkMode(hDC, TRANSPARENT);
  134. LPTSTR lpText = (LPTSTR)GetText();
  135. if (lpText)
  136. {
  137. DrawText(hDC, lpText, -1, &rect, DT_NOPREFIX);
  138. }
  139. SelectObject(hDC, hOldBrush);
  140. DeleteObject(hBrush);
  141. if (!GetReadOnly())
  142. {
  143. hBrush = (HGDIOBJ)CreateSolidBrush(m_rgbFill);
  144. if (hBrush != NULL)
  145. {
  146. SelectObject(hDC, hBrush);
  147. InflateRect(&arect, -3, -3);
  148. DrawArrow(hDC, arect, TRUE, FALSE);
  149. SelectObject(hDC, hOldBrush);
  150. DeleteObject(hBrush);
  151. }
  152. }
  153. }
  154. SelectObject(hDC, hOldPen);
  155. DeleteObject(hPen);
  156. }
  157. }
  158. int CFlexComboBox::AddString(LPCTSTR str)
  159. {
  160. return m_ListBox.AddString(str);
  161. }
  162. LRESULT CFlexComboBox::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  163. {
  164. RECT wrect = {-1, -1, -1, -1};
  165. POINT point = {-1, -1};
  166. BOOL bWithin = FALSE;
  167. switch (msg)
  168. {
  169. case WM_SIZE:
  170. Invalidate();
  171. SetRect();
  172. return 0;
  173. case WM_FLEXLISTBOX:
  174. assert(lParam == (LPARAM)(LPVOID)&m_ListBox);
  175. switch (wParam)
  176. {
  177. case FLBN_FINALSEL:
  178. StateEvent(FCBSE_UPLIST);
  179. break;
  180. case FLBN_CANCEL:
  181. StateEvent(FCBSE_DOWNOFF);
  182. break;
  183. }
  184. return 0;
  185. // make sure flexwnd doesn't do ANYTHING with our mouse messages
  186. case WM_MOUSEMOVE:
  187. // We initialize the tooltip to current selection text if the selected text is too long to fit.
  188. RECT rect;
  189. GetClientRect(&rect);
  190. rect.right = rect.right - (rect.bottom - rect.top);
  191. rect.left++;
  192. rect.top++;
  193. rect.bottom--;
  194. RECT ResultRect;
  195. ResultRect = rect;
  196. HDC hDC;
  197. hDC = CreateCompatibleDC(NULL);
  198. if (hDC)
  199. {
  200. LPTSTR lpText = (LPTSTR)GetText();
  201. if (lpText)
  202. {
  203. DrawText(hDC, lpText, -1, &ResultRect, DT_NOPREFIX|DT_CALCRECT);
  204. }
  205. DeleteDC(hDC);
  206. }
  207. if (rect.right < ResultRect.right || rect.bottom < ResultRect.bottom)
  208. {
  209. CFlexWnd::s_ToolTip.SetToolTipParent(GetParent(m_hWnd));
  210. TOOLTIPINITPARAM ttip;
  211. ttip.hWndParent = GetParent(m_hWnd);
  212. ttip.iSBWidth = 0;
  213. ttip.dwID = 0;
  214. ttip.hWndNotify = m_hWnd;
  215. ttip.tszCaption = m_ListBox.GetSelText();
  216. CFlexToolTip::UpdateToolTipParam(ttip);
  217. }
  218. Notify(FCBN_MOUSEOVER);
  219. case WM_LBUTTONUP:
  220. case WM_LBUTTONDOWN:
  221. case WM_RBUTTONUP:
  222. case WM_RBUTTONDOWN:
  223. case WM_LBUTTONDBLCLK:
  224. case WM_RBUTTONDBLCLK:
  225. if (msg == WM_LBUTTONDOWN)
  226. {
  227. HWND hWndParent;
  228. hWndParent = GetParent(hWnd);
  229. SendMessage(hWndParent, WM_UNHIGHLIGHT, 0, 0); // Send click message to page to unhighlight callout
  230. }
  231. GetClientRect(&wrect);
  232. point.x = int(LOWORD(lParam));
  233. point.y = int(HIWORD(lParam));
  234. bWithin = PtInRect(&wrect, point);
  235. break;
  236. case WM_TIMER:
  237. case WM_CAPTURECHANGED:
  238. break;
  239. default:
  240. return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  241. }
  242. switch (msg)
  243. {
  244. case WM_LBUTTONDOWN:
  245. if (!GetReadOnly())
  246. StateEvent(bWithin ? FCBSE_DOWN : FCBSE_DOWNOFF);
  247. break;
  248. case WM_LBUTTONUP:
  249. if (!GetReadOnly())
  250. StateEvent(bWithin ? FCBSE_UPBOX : FCBSE_UPOFF);
  251. break;
  252. }
  253. return 0;
  254. }
  255. RECT CFlexComboBox::GetListBoxRect()
  256. {
  257. HWND hParent = GetParent(m_hWnd);
  258. RECT rect;
  259. GetClientRect(&rect);
  260. BOOL bRet = ClientToScreen(m_hWnd, &rect);
  261. BOOL bRet2 = ScreenToClient(hParent, &rect);
  262. RECT lrect = m_rect;
  263. lrect.top = rect.bottom;
  264. lrect.right -= 12; // UNDONE: remove this line when the clipping is working properly (scroll bars don't appear above other windows)
  265. return lrect;
  266. }
  267. void CFlexComboBox::DoSel()
  268. {
  269. if (m_bInSelMode)
  270. return;
  271. if (m_hWnd == NULL)
  272. return;
  273. FLEXLISTBOXCREATESTRUCT cs;
  274. cs.dwSize = sizeof(FLEXLISTBOXCREATESTRUCT);
  275. cs.dwFlags = m_dwListBoxFlags;
  276. cs.hWndParent = GetParent(m_hWnd);
  277. cs.hWndNotify = m_hWnd;
  278. cs.bVisible = FALSE;
  279. cs.rect = GetListBoxRect();
  280. cs.hFont = m_hFont;
  281. cs.rgbText = m_rgbText;
  282. cs.rgbBk = m_rgbBk;
  283. cs.rgbSelText = m_rgbSelText;
  284. cs.rgbSelBk = m_rgbSelBk;
  285. cs.rgbFill = m_rgbFill;
  286. cs.rgbLine = m_rgbLine;
  287. cs.nSBWidth = m_nSBWidth;
  288. m_OldSel = m_ListBox.GetSel();
  289. m_bInSelMode = m_ListBox.Create(&cs);
  290. if (m_bInSelMode)
  291. SetWindowPos(m_ListBox.m_hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
  292. }
  293. void CFlexComboBox::Notify(int code)
  294. {
  295. if (!m_hWndNotify)
  296. return;
  297. SendMessage(m_hWndNotify, WM_FLEXCOMBOBOX,
  298. (WPARAM)code, (LPARAM)(LPVOID)this);
  299. }
  300. RECT CFlexComboBox::GetRect(const RECT &rect)
  301. {
  302. int h = GetTextHeight(m_hFont);
  303. RECT ret = {rect.left, rect.top, rect.right, rect.top + h + 2};
  304. return ret;
  305. }
  306. RECT CFlexComboBox::GetRect()
  307. {
  308. RECT rect;
  309. GetClientRect(&rect);
  310. return GetRect(rect);
  311. }
  312. void CFlexComboBox::SetFont(HFONT hFont)
  313. {
  314. m_hFont = hFont;
  315. if (m_hWnd == NULL)
  316. return;
  317. Invalidate();
  318. SetRect();
  319. }
  320. void CFlexComboBox::SetRect()
  321. {
  322. if (m_hWnd == NULL)
  323. return;
  324. RECT rect = GetRect();
  325. SetWindowPos(m_hWnd, NULL, rect.left, rect.top, rect.right, rect.bottom, SWP_NOZORDER | SWP_NOMOVE);
  326. }
  327. void CFlexComboBox::SetColors(COLORREF text, COLORREF bk, COLORREF seltext, COLORREF selbk, COLORREF fill, COLORREF line)
  328. {
  329. m_rgbText = text;
  330. m_rgbBk = bk;
  331. m_rgbSelText = seltext;
  332. m_rgbSelBk = selbk;
  333. m_rgbFill = fill;
  334. m_rgbLine = line;
  335. Invalidate();
  336. }
  337. void CFlexComboBox::StateEvent(FCBSTATEEVENT e)
  338. {
  339. if (e == FCBSE_DOWNOFF)
  340. {
  341. SetState(FCBS_CANCEL);
  342. return;
  343. }
  344. switch (m_eCurState)
  345. {
  346. case FCBS_CLOSED:
  347. if (e == FCBSE_DOWN)
  348. SetState(FCBS_OPENDOWN);
  349. break;
  350. case FCBS_OPENDOWN:
  351. switch (e)
  352. {
  353. case FCBSE_UPLIST:
  354. SetState(FCBS_SELECT);
  355. break;
  356. case FCBSE_UPBOX:
  357. SetState(FCBS_OPENUP);
  358. break;
  359. case FCBSE_UPOFF:
  360. SetState(FCBS_CANCEL);
  361. break;
  362. }
  363. case FCBS_OPENUP:
  364. if (e == FCBSE_DOWN)
  365. SetState(FCBS_OPENDOWN);
  366. break;
  367. default:
  368. assert(0);
  369. return;
  370. }
  371. }
  372. void CFlexComboBox::SetState(FCBSTATE s)
  373. {
  374. FCBSTATE eOldState = m_eCurState;
  375. m_eCurState = s;
  376. switch (s)
  377. {
  378. case FCBS_OPENUP:
  379. if (eOldState == FCBS_CLOSED)
  380. DoSel();
  381. return;
  382. case FCBS_OPENDOWN:
  383. if (eOldState == FCBS_CLOSED)
  384. DoSel();
  385. m_ListBox.StartSel();
  386. return;
  387. case FCBS_CANCEL:
  388. m_ListBox.SetSel(m_OldSel);
  389. CFlexWnd::s_ToolTip.SetEnable(FALSE);
  390. SetState(FCBS_CLOSED);
  391. return;
  392. case FCBS_SELECT:
  393. CFlexWnd::s_ToolTip.SetEnable(FALSE);
  394. Invalidate();
  395. Notify(FCBN_SELCHANGE);
  396. SetState(FCBS_CLOSED);
  397. return;
  398. case FCBS_CLOSED:
  399. if (eOldState != FCBS_CLOSED)
  400. m_ListBox.Destroy();
  401. m_bInSelMode = FALSE;
  402. Invalidate();
  403. return;
  404. }
  405. }