Leaked source code of windows server 2003
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.

592 lines
14 KiB

  1. //-----------------------------------------------------------------------------
  2. // File: flexlistbox.cpp
  3. //
  4. // Desc: Implements a list box control that can display a list of text strings,
  5. // each can be selected by mouse. The class CFlexListBox is derived from
  6. // CFlexWnd. It is used by the class CFlexComboBox when it needs to
  7. // expand to show the list of choices.
  8. //
  9. // Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
  10. //-----------------------------------------------------------------------------
  11. #include "common.hpp"
  12. CFlexListBox::CFlexListBox() :
  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_nTopIndex(0),
  23. m_nSBWidth(11),
  24. m_hFont(NULL),
  25. m_bOpenClick(FALSE),
  26. m_bDragging(FALSE),
  27. m_bCapture(FALSE),
  28. m_nSelItem(-1),
  29. m_bVertSB(FALSE)
  30. {
  31. }
  32. CFlexListBox::~CFlexListBox()
  33. {
  34. }
  35. CFlexListBox *CreateFlexListBox(FLEXLISTBOXCREATESTRUCT *pcs)
  36. {
  37. CFlexListBox *psb = new CFlexListBox;
  38. if (psb && psb->Create(pcs))
  39. return psb;
  40. delete psb;
  41. return NULL;
  42. }
  43. BOOL CFlexListBox::CreateForSingleSel(FLEXLISTBOXCREATESTRUCT *pcs)
  44. {
  45. if (!Create(pcs))
  46. return FALSE;
  47. StartSel();
  48. return TRUE;
  49. }
  50. BOOL CFlexListBox::Create(FLEXLISTBOXCREATESTRUCT *pcs)
  51. {
  52. if (this == NULL)
  53. return FALSE;
  54. if (m_hWnd)
  55. Destroy();
  56. if (pcs == NULL)
  57. return FALSE;
  58. if (pcs->dwSize != sizeof(FLEXLISTBOXCREATESTRUCT))
  59. return FALSE;
  60. m_hWndNotify = pcs->hWndNotify ? pcs->hWndNotify : pcs->hWndParent;
  61. m_dwFlags = pcs->dwFlags;
  62. SetFont(pcs->hFont);
  63. SetColors(pcs->rgbText, pcs->rgbBk, pcs->rgbSelText, pcs->rgbSelBk, pcs->rgbFill, pcs->rgbLine);
  64. m_VertSB.SetColors(pcs->rgbBk, pcs->rgbFill, pcs->rgbLine);
  65. m_nSBWidth = pcs->nSBWidth;
  66. if (!CFlexWnd::Create(pcs->hWndParent, pcs->rect, FALSE))
  67. return FALSE;
  68. FLEXSCROLLBARCREATESTRUCT sbcs;
  69. sbcs.dwSize = sizeof(FLEXSCROLLBARCREATESTRUCT);
  70. sbcs.dwFlags = FSBF_VERT;
  71. sbcs.min = 0;
  72. sbcs.max = 3;
  73. sbcs.page = 1;
  74. sbcs.pos = 1;
  75. sbcs.hWndParent = m_hWnd;
  76. sbcs.hWndNotify = m_hWnd;
  77. RECT rect = {0, 0, 1, 1};
  78. sbcs.rect = rect;
  79. sbcs.bVisible = FALSE;
  80. m_VertSB.Create(&sbcs);
  81. Calc();
  82. // show if we want it shown
  83. if (pcs->bVisible)
  84. ShowWindow(m_hWnd, SW_SHOW);
  85. if (m_bVertSB)
  86. SetVertSB();
  87. // TODO: make sure that creation sends no notifications.
  88. // all initial notifications should be sent here.
  89. return TRUE;
  90. }
  91. void CFlexListBox::Calc()
  92. {
  93. // handle getting text height
  94. if (m_nTextHeight == -1)
  95. {
  96. m_nTextHeight = GetTextHeight(m_hFont);
  97. Invalidate();
  98. assert(m_nTextHeight != -1);
  99. }
  100. // don't do the rest unless we've been created
  101. if (m_hWnd == NULL)
  102. return;
  103. // handle integral height
  104. int iUsedHeight = m_ItemArray.GetSize() * m_nTextHeight;
  105. // If more than max height, use the max height
  106. if (iUsedHeight > g_UserNamesRect.bottom - g_UserNamesRect.top)
  107. iUsedHeight = g_UserNamesRect.bottom - g_UserNamesRect.top;
  108. SIZE client = GetClientSize();
  109. int fit = iUsedHeight / m_nTextHeight;
  110. if (fit < 1)
  111. fit = 1;
  112. int setheight = (m_dwFlags & FLBF_INTEGRALHEIGHT) ? fit * m_nTextHeight : iUsedHeight;
  113. if (setheight != client.cy)
  114. SetWindowPos(m_hWnd, NULL, 0, 0, client.cx, setheight,
  115. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
  116. // handle scroll bar
  117. SetVertSB(m_ItemArray.GetSize() > fit);
  118. }
  119. void CFlexListBox::SetFont(HFONT hFont)
  120. {
  121. m_hFont = hFont;
  122. m_nTextHeight = -1;
  123. Calc();
  124. }
  125. void CFlexListBox::OnPaint(HDC hDC)
  126. {
  127. HDC hBDC = NULL, hODC = NULL;
  128. CBitmap *pbm = NULL;
  129. if (!InRenderMode())
  130. {
  131. hODC = hDC;
  132. pbm = CBitmap::Create(GetClientSize(), RGB(0,0,0), hDC);
  133. if (pbm != NULL)
  134. {
  135. hBDC = pbm->BeginPaintInto();
  136. if (hBDC != NULL)
  137. {
  138. hDC = hBDC;
  139. }
  140. }
  141. }
  142. InternalPaint(hDC);
  143. if (!InRenderMode())
  144. {
  145. if (pbm != NULL)
  146. {
  147. if (hBDC != NULL)
  148. {
  149. pbm->EndPaintInto(hBDC);
  150. pbm->Draw(hODC);
  151. }
  152. delete pbm;
  153. }
  154. }
  155. }
  156. void CFlexListBox::InternalPaint(HDC hDC)
  157. {
  158. if (m_nTextHeight == -1)
  159. return;
  160. SIZE client = GetClientSize();
  161. RECT rc = {0,0,0,0};
  162. GetClientRect(&rc);
  163. HGDIOBJ hPen, hOldPen, hBrush, hOldBrush;
  164. hPen= (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbBk);
  165. if (hPen != NULL)
  166. {
  167. hOldPen = SelectObject(hDC, hPen);
  168. hBrush = CreateSolidBrush(m_rgbBk);
  169. if (hBrush != NULL)
  170. {
  171. hOldBrush = SelectObject(hDC, hBrush);
  172. Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom); // Paint entire window black first.
  173. if (!m_bVertSB)
  174. m_nTopIndex = 0;
  175. int iLastY;
  176. for (int at = 0, i = m_nTopIndex; at < client.cy; i++, at += m_nTextHeight)
  177. {
  178. RECT rect = {0, at, client.cx, at + m_nTextHeight};
  179. if (i < m_ItemArray.GetSize())
  180. {
  181. BOOL bSel = m_ItemArray[i].bSelected;
  182. SetTextColor(hDC, bSel ? m_rgbSelText : m_rgbText);
  183. SetBkColor(hDC, bSel ? m_rgbSelBk : m_rgbBk);
  184. DrawText(hDC, m_ItemArray[i].GetText(), -1, &rect, DT_NOPREFIX);
  185. iLastY = at + m_nTextHeight;
  186. }
  187. }
  188. SelectObject(hDC, hOldBrush);
  189. DeleteObject(hBrush);
  190. }
  191. SelectObject(hDC, hOldPen);
  192. DeleteObject(hPen);
  193. }
  194. // Draw an outline around the box
  195. hPen = (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbLine);
  196. if (hPen != NULL)
  197. {
  198. hOldPen = SelectObject(hDC, hPen);
  199. hOldBrush = SelectObject(hDC, GetStockObject(NULL_BRUSH));
  200. Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
  201. SelectObject(hDC, hOldPen);
  202. DeleteObject(hPen);
  203. }
  204. }
  205. int CFlexListBox::AddString(LPCTSTR str)
  206. {
  207. int newIndex = m_ItemArray.GetSize();
  208. m_ItemArray.SetSize(newIndex + 1);
  209. FLEXLISTBOXITEM &i = m_ItemArray[newIndex];
  210. i.SetText(str);
  211. i.bSelected = FALSE;
  212. SetSBValues();
  213. Calc();
  214. Invalidate();
  215. return newIndex;
  216. }
  217. void CFlexListBox::StartSel()
  218. {
  219. if (m_bDragging)
  220. return;
  221. SetTimer(m_hWnd, 5, 200, NULL);
  222. m_bOpenClick = TRUE; // Initial click on the combobox
  223. m_bDragging = TRUE;
  224. m_bCapture = TRUE;
  225. SetCapture();
  226. }
  227. void CFlexListBox::OnWheel(POINT point, WPARAM wParam)
  228. {
  229. if (!m_bVertSB) return;
  230. int nPage = MulDiv(m_VertSB.GetPage(), 9, 10) >> 1; // Half a page at a time
  231. if ((int)wParam >= 0)
  232. m_VertSB.AdjustPos(-nPage);
  233. else
  234. m_VertSB.AdjustPos(nPage);
  235. m_nTopIndex = m_VertSB.GetPos();
  236. if (m_nTopIndex < 0)
  237. m_nTopIndex = 0;
  238. Invalidate();
  239. }
  240. LRESULT CFlexListBox::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  241. {
  242. // first handle scroll bar messages
  243. switch (msg)
  244. {
  245. case WM_FLEXVSCROLL:
  246. int code = (int)wParam;
  247. CFlexScrollBar *pSB = (CFlexScrollBar *)lParam;
  248. if (!pSB)
  249. return 0;
  250. int nLine = 1;
  251. int nPage = MulDiv(pSB->GetPage(), 9, 10);
  252. switch (code)
  253. {
  254. case SB_LINEUP: pSB->AdjustPos(-nLine); break;
  255. case SB_LINEDOWN: pSB->AdjustPos(nLine); break;
  256. case SB_PAGEUP: pSB->AdjustPos(-nPage); break;
  257. case SB_PAGEDOWN: pSB->AdjustPos(nPage); break;
  258. case SB_THUMBTRACK: pSB->SetPos(pSB->GetThumbPos()); break;
  259. case SB_ENDSCROLL:
  260. SetCapture(); // Recapture after the scroll bar releases the capture.
  261. break;
  262. }
  263. switch (msg)
  264. {
  265. case WM_FLEXVSCROLL:
  266. m_nTopIndex = pSB->GetPos();
  267. if (m_nTopIndex < 0)
  268. m_nTopIndex = 0;
  269. break;
  270. }
  271. Invalidate();
  272. return 0;
  273. }
  274. // now non-scrolly input
  275. switch (msg)
  276. {
  277. case WM_SIZE:
  278. SetSBValues();
  279. Calc();
  280. Invalidate();
  281. return 0;
  282. // make sure flexwnd doesn't do ANYTHING with our mouse messages
  283. case WM_MOUSEMOVE:
  284. case WM_LBUTTONUP:
  285. case WM_LBUTTONDOWN:
  286. case WM_RBUTTONUP:
  287. case WM_RBUTTONDOWN:
  288. case WM_LBUTTONDBLCLK:
  289. case WM_RBUTTONDBLCLK:
  290. {
  291. POINT point = {int(signed short(LOWORD(lParam))), int(signed short(HIWORD(lParam)))};
  292. m_point = point;
  293. }
  294. case WM_TIMER:
  295. case WM_CAPTURECHANGED:
  296. break;
  297. default:
  298. return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  299. }
  300. switch (msg)
  301. {
  302. case WM_LBUTTONDOWN:
  303. // Check if we clicked the scroll bar area. If so, send the click to the scroll bar.
  304. RECT rc;
  305. m_VertSB.GetClientRect(&rc);
  306. ClientToScreen(m_VertSB.m_hWnd, &rc);
  307. ScreenToClient(m_hWnd, &rc);
  308. if (PtInRect(&rc, m_point))
  309. {
  310. POINT point = {int(signed short(LOWORD(lParam))), int(signed short(HIWORD(lParam)))};
  311. ClientToScreen(m_hWnd, &point);
  312. ScreenToClient(m_VertSB.m_hWnd, &point);
  313. PostMessage(m_VertSB.m_hWnd, WM_LBUTTONDOWN, wParam, point.x + (point.y << 16)); // This will make it lose capture.
  314. } else
  315. StartSel();
  316. break;
  317. case WM_MOUSEMOVE:
  318. case WM_LBUTTONUP:
  319. if (!m_bDragging)
  320. break;
  321. if (m_nTextHeight == -1)
  322. break;
  323. case WM_TIMER:
  324. if (m_bDragging || msg != WM_TIMER)
  325. {
  326. int adj = m_point.y < 0 ? -1 : 0;
  327. SelectAndShowSingleItem(adj + m_point.y / m_nTextHeight + m_nTopIndex, msg != WM_MOUSEMOVE);
  328. Notify(FLBN_SEL);
  329. }
  330. // Check if the mouse cursor is within the listbox rectangle. If not, don't show the tooltip.
  331. if (msg == WM_MOUSEMOVE)
  332. {
  333. RECT rect;
  334. GetClientRect(&rect);
  335. POINT pt;
  336. GetCursorPos(&pt);
  337. ScreenToClient(m_hWnd, &pt);
  338. if (!PtInRect(&rect, pt))
  339. {
  340. CFlexWnd::s_ToolTip.SetToolTipParent(NULL);
  341. CFlexWnd::s_ToolTip.SetEnable(FALSE);
  342. }
  343. }
  344. break;
  345. }
  346. switch (msg)
  347. {
  348. case WM_CAPTURECHANGED:
  349. if ((HWND)lParam == m_VertSB.m_hWnd) // If the scroll bar is getting the capture, we do not clean up.
  350. break;
  351. case WM_LBUTTONUP:
  352. if (m_bOpenClick)
  353. {
  354. m_bOpenClick = FALSE; // If this is the result of clicking the combobox window, don't release capture.
  355. break;
  356. }
  357. if (m_bCapture)
  358. {
  359. m_bCapture = FALSE;
  360. KillTimer(m_hWnd, 5);
  361. ReleaseCapture();
  362. m_bDragging = FALSE;
  363. BOOL bCancel = TRUE;
  364. if (msg == WM_LBUTTONUP)
  365. {
  366. RECT wrect;
  367. GetClientRect(&wrect);
  368. if (PtInRect(&wrect, m_point))
  369. bCancel = FALSE;
  370. }
  371. Notify(bCancel ? FLBN_CANCEL : FLBN_FINALSEL);
  372. }
  373. break;
  374. }
  375. return 0;
  376. }
  377. void CFlexListBox::SelectAndShowSingleItem(int i, BOOL bScroll)
  378. {
  379. int nItems = m_ItemArray.GetSize();
  380. if (nItems < 1)
  381. {
  382. m_nSelItem = i; // We have to update m_nSelItem even if there is no items because the username combobox
  383. // is not initialized when there is only 1 user. Selection of user 0 is assumed.
  384. return;
  385. }
  386. if (i < 0)
  387. i = 0;
  388. if (i >= nItems)
  389. i = nItems - 1;
  390. if (m_nSelItem >= 0 && m_nSelItem < nItems)
  391. m_ItemArray[m_nSelItem].bSelected = FALSE;
  392. m_nSelItem = i;
  393. m_ItemArray[m_nSelItem].bSelected = TRUE;
  394. if (bScroll)
  395. {
  396. SIZE client = GetClientSize();
  397. int nBottomIndex = m_nTopIndex + client.cy / m_nTextHeight - 1;
  398. if (m_nSelItem < m_nTopIndex)
  399. m_nTopIndex = m_nSelItem;
  400. assert(m_nTopIndex >= 0);
  401. if (m_nSelItem > nBottomIndex)
  402. {
  403. m_nTopIndex += m_nSelItem - nBottomIndex + 1;
  404. nBottomIndex = m_nSelItem + 1;
  405. }
  406. if (nBottomIndex > nItems - 1)
  407. m_nTopIndex -= nBottomIndex - nItems + 1;
  408. if (m_nTopIndex < 0)
  409. m_nTopIndex = 0;
  410. if (m_nTopIndex >= nItems)
  411. m_nTopIndex = nItems - 1;
  412. assert(m_nTopIndex >= 0 && m_nTopIndex < nItems);
  413. }
  414. if (m_bVertSB)
  415. SetSBValues();
  416. SIZE client = GetClientSize();
  417. int nBottomIndex = m_nTopIndex + client.cy / m_nTextHeight - 1;
  418. int iToolTipIndex = m_nSelItem;
  419. // Make sure that we don't display tooltip for items outside the listbox window
  420. if (iToolTipIndex > nBottomIndex)
  421. iToolTipIndex = nBottomIndex;
  422. if (iToolTipIndex < m_nTopIndex)
  423. iToolTipIndex = m_nTopIndex;
  424. // Create and initialize a tooltip if the text is too long to fit.
  425. RECT rect = {0, 0, client.cx, m_nTextHeight};
  426. RECT ResultRect = rect;
  427. HDC hDC = CreateCompatibleDC(NULL);
  428. if (hDC)
  429. {
  430. DrawText(hDC, m_ItemArray[iToolTipIndex].GetText(), -1, &ResultRect, DT_NOPREFIX|DT_CALCRECT);
  431. DeleteDC(hDC);
  432. }
  433. if (ResultRect.right > rect.right)
  434. {
  435. TOOLTIPINITPARAM ttip;
  436. ttip.hWndParent = GetParent(m_hWnd);
  437. ttip.iSBWidth = m_nSBWidth;
  438. ttip.dwID = iToolTipIndex;
  439. ttip.hWndNotify = m_hWnd;
  440. ttip.tszCaption = GetSelText();
  441. CFlexToolTip::UpdateToolTipParam(ttip);
  442. }
  443. Invalidate();
  444. }
  445. void CFlexListBox::Notify(int code)
  446. {
  447. if (!m_hWndNotify)
  448. return;
  449. SendMessage(m_hWndNotify, WM_FLEXLISTBOX,
  450. (WPARAM)code, (LPARAM)(LPVOID)this);
  451. }
  452. void CFlexListBox::SetColors(COLORREF text, COLORREF bk, COLORREF seltext, COLORREF selbk, COLORREF fill, COLORREF line)
  453. {
  454. m_rgbText = text;
  455. m_rgbBk = bk;
  456. m_rgbSelText = seltext;
  457. m_rgbSelBk = selbk;
  458. m_rgbFill = fill;
  459. m_rgbLine = line;
  460. Invalidate();
  461. }
  462. void CFlexListBox::SetVertSB(BOOL bSet)
  463. {
  464. if (bEq(bSet, m_bVertSB))
  465. return;
  466. m_bVertSB = bSet;
  467. if (m_hWnd == NULL)
  468. return;
  469. SetVertSB();
  470. }
  471. void CFlexListBox::SetVertSB()
  472. {
  473. if (m_bVertSB)
  474. {
  475. SetSBValues();
  476. SIZE client = GetClientSize();
  477. SetWindowPos(m_VertSB.m_hWnd, NULL, client.cx - m_nSBWidth - 1, 0, m_nSBWidth, client.cy - 1, SWP_NOZORDER);
  478. }
  479. ShowWindow(m_VertSB.m_hWnd, m_bVertSB ? SW_SHOW : SW_HIDE);
  480. }
  481. void CFlexListBox::SetSBValues()
  482. {
  483. if (m_hWnd == NULL)
  484. return;
  485. SIZE client = GetClientSize();
  486. int fit = client.cy / m_nTextHeight;
  487. m_VertSB.SetValues(0, m_ItemArray.GetSize(), fit, m_nTopIndex);
  488. }
  489. LPCTSTR CFlexListBox::GetSelText()
  490. {
  491. if (m_nSelItem < 0 || m_nSelItem >= m_ItemArray.GetSize())
  492. return NULL;
  493. return m_ItemArray[m_nSelItem].GetText();
  494. }
  495. int CFlexListBox::GetSel()
  496. {
  497. return m_nSelItem;
  498. }