ResizableComboLBox.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. // ResizableComboLBox.cpp : implementation file
  2. //
  3. /////////////////////////////////////////////////////////////////////////////
  4. //
  5. // This file is part of ResizableLib
  6. // https://github.com/ppescher/resizablelib
  7. //
  8. // Copyright (C) 2000-2015 by Paolo Messina
  9. // mailto:ppescher@hotmail.com
  10. //
  11. // The contents of this file are subject to the Artistic License 2.0
  12. // http://opensource.org/licenses/Artistic-2.0
  13. //
  14. // If you find this code useful, credits would be nice!
  15. //
  16. /////////////////////////////////////////////////////////////////////////////
  17. #include "stdafx.h"
  18. #include "ResizableComboLBox.h"
  19. #include "ResizableComboBox.h"
  20. #ifdef _DEBUG
  21. #define new DEBUG_NEW
  22. #undef THIS_FILE
  23. static char THIS_FILE[] = __FILE__;
  24. #endif
  25. /////////////////////////////////////////////////////////////////////////////
  26. // CResizableComboLBox
  27. CResizableComboLBox::CResizableComboLBox()
  28. {
  29. m_dwAddToStyle = WS_THICKFRAME;
  30. m_dwAddToStyleEx = 0;//WS_EX_CLIENTEDGE;
  31. m_bSizing = FALSE;
  32. }
  33. CResizableComboLBox::~CResizableComboLBox()
  34. {
  35. }
  36. BEGIN_MESSAGE_MAP(CResizableComboLBox, CWnd)
  37. //{{AFX_MSG_MAP(CResizableComboLBox)
  38. ON_WM_MOUSEMOVE()
  39. ON_WM_LBUTTONDOWN()
  40. ON_WM_LBUTTONUP()
  41. ON_WM_NCHITTEST()
  42. ON_WM_CAPTURECHANGED()
  43. ON_WM_WINDOWPOSCHANGING()
  44. ON_WM_WINDOWPOSCHANGED()
  45. //}}AFX_MSG_MAP
  46. END_MESSAGE_MAP()
  47. /////////////////////////////////////////////////////////////////////////////
  48. // CResizableComboLBox message handlers
  49. void CResizableComboLBox::PreSubclassWindow()
  50. {
  51. CWnd::PreSubclassWindow();
  52. InitializeControl();
  53. }
  54. BOOL CResizableComboLBox::IsRTL()
  55. {
  56. return (GetExStyle() & WS_EX_LAYOUTRTL);
  57. }
  58. void CResizableComboLBox::InitializeControl()
  59. {
  60. CRect rect;
  61. m_pOwnerCombo->GetWindowRect(&rect);
  62. m_sizeAfterSizing.cx = rect.Width();
  63. m_sizeAfterSizing.cy = -rect.Height();
  64. m_pOwnerCombo->GetDroppedControlRect(&rect);
  65. m_sizeAfterSizing.cy += rect.Height();
  66. m_sizeMin.cy = m_sizeAfterSizing.cy-2;
  67. // change window's style
  68. ModifyStyleEx(0, m_dwAddToStyleEx);
  69. ModifyStyle(0, m_dwAddToStyle, SWP_FRAMECHANGED);
  70. // count hscroll if present
  71. if (GetStyle() & WS_HSCROLL)
  72. m_sizeAfterSizing.cy += GetSystemMetrics(SM_CYHSCROLL);
  73. SetWindowPos(NULL, 0, 0, m_sizeAfterSizing.cx, m_sizeAfterSizing.cy,
  74. SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE);
  75. }
  76. void CResizableComboLBox::OnMouseMove(UINT nFlags, CPoint point)
  77. {
  78. CPoint pt = point;
  79. MapWindowPoints(NULL, &pt, 1); // to screen coord
  80. if (!m_bSizing)
  81. {
  82. // since mouse is captured we need to change the cursor manually
  83. LRESULT ht = SendMessage(WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y));
  84. SendMessage(WM_SETCURSOR, (WPARAM)m_hWnd, MAKELPARAM(ht, WM_MOUSEMOVE));
  85. CWnd::OnMouseMove(nFlags, point);
  86. return;
  87. }
  88. // during resize
  89. CRect rect = m_rcBeforeSizing;
  90. CSize relMove = pt - m_ptBeforeSizing;
  91. switch (m_nHitTest)
  92. {
  93. case HTBOTTOM:
  94. rect.bottom += relMove.cy;
  95. break;
  96. case HTBOTTOMRIGHT:
  97. rect.bottom += relMove.cy;
  98. rect.right += relMove.cx;
  99. break;
  100. case HTRIGHT:
  101. rect.right += relMove.cx;
  102. break;
  103. case HTBOTTOMLEFT:
  104. rect.bottom += relMove.cy;
  105. rect.left += relMove.cx;
  106. break;
  107. case HTLEFT:
  108. rect.left += relMove.cx;
  109. break;
  110. }
  111. // move window (if right-aligned it needs refresh)
  112. UINT nCopyFlag = (GetExStyle() & WS_EX_RIGHT) ? SWP_NOCOPYBITS : 0;
  113. SetWindowPos(NULL, rect.left, rect.top, rect.Width(), rect.Height(),
  114. SWP_NOACTIVATE|SWP_NOZORDER|nCopyFlag);
  115. }
  116. void CResizableComboLBox::OnLButtonDown(UINT nFlags, CPoint point)
  117. {
  118. CPoint pt = point;
  119. MapWindowPoints(NULL, &pt, 1); // to screen coord
  120. LRESULT ht = SendMessage(WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y));
  121. if (ht == HTBOTTOM || ht == HTRIGHT || ht == HTBOTTOMRIGHT
  122. || ht == HTLEFT || ht == HTBOTTOMLEFT)
  123. {
  124. // start resizing
  125. m_bSizing = TRUE;
  126. m_nHitTest = ht;
  127. GetWindowRect(&m_rcBeforeSizing);
  128. m_ptBeforeSizing = pt;
  129. }
  130. else
  131. CWnd::OnLButtonDown(nFlags, point);
  132. }
  133. void CResizableComboLBox::OnLButtonUp(UINT nFlags, CPoint point)
  134. {
  135. CWnd::OnLButtonUp(nFlags, point);
  136. EndSizing();
  137. }
  138. LRESULT CResizableComboLBox::OnNcHitTest(CPoint point)
  139. {
  140. CRect rcClient;
  141. GetClientRect(&rcClient);
  142. MapWindowPoints(NULL, &rcClient);
  143. // ask for default hit-test value
  144. UINT ht = CWnd::OnNcHitTest(point);
  145. // disable improper resizing (based on layout setting)
  146. switch (ht)
  147. {
  148. case HTTOPRIGHT:
  149. if (!IsRTL() && point.y > rcClient.top)
  150. ht = HTRIGHT;
  151. else
  152. ht = HTBORDER;
  153. break;
  154. case HTTOPLEFT:
  155. if (IsRTL() && point.y > rcClient.top)
  156. ht = HTLEFT;
  157. else
  158. ht = HTBORDER;
  159. break;
  160. case HTBOTTOMLEFT:
  161. if (!IsRTL() && point.y > rcClient.bottom)
  162. ht = HTBOTTOM;
  163. else if (!IsRTL())
  164. ht = HTBORDER;
  165. break;
  166. case HTBOTTOMRIGHT:
  167. if (IsRTL() && point.y > rcClient.bottom)
  168. ht = HTBOTTOM;
  169. else if (IsRTL())
  170. ht = HTBORDER;
  171. break;
  172. case HTLEFT:
  173. if (!IsRTL())
  174. ht = HTBORDER;
  175. break;
  176. case HTRIGHT:
  177. if (IsRTL())
  178. ht = HTBORDER;
  179. break;
  180. case HTTOP:
  181. ht = HTBORDER;
  182. }
  183. return ht;
  184. }
  185. void CResizableComboLBox::OnCaptureChanged(CWnd *pWnd)
  186. {
  187. EndSizing();
  188. CWnd::OnCaptureChanged(pWnd);
  189. }
  190. void CResizableComboLBox::EndSizing()
  191. {
  192. m_bSizing = FALSE;
  193. CRect rect;
  194. GetWindowRect(&rect);
  195. m_sizeAfterSizing = rect.Size();
  196. }
  197. void CResizableComboLBox::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos)
  198. {
  199. if (!m_bSizing)
  200. {
  201. // restore the size when the drop-down list becomes visible
  202. lpwndpos->cx = m_sizeAfterSizing.cx;
  203. lpwndpos->cy = m_sizeAfterSizing.cy;
  204. }
  205. ApplyLimitsToPos(lpwndpos);
  206. CWnd::OnWindowPosChanging(lpwndpos);
  207. }
  208. void CResizableComboLBox::OnWindowPosChanged(WINDOWPOS FAR* lpwndpos)
  209. {
  210. // default implementation sends a WM_SIZE message
  211. // that can change the size again to force integral height
  212. // since we do that manually during resize, we should also
  213. // update the horizontal scrollbar
  214. SendMessage(WM_HSCROLL, SB_ENDSCROLL, 0);
  215. GetWindowRect(&m_pOwnerCombo->m_rectDropDown);
  216. ::MapWindowPoints(NULL, m_pOwnerCombo->GetSafeHwnd(),
  217. (LPPOINT)&m_pOwnerCombo->m_rectDropDown, 2);
  218. CWnd::OnWindowPosChanged(lpwndpos);
  219. }
  220. void CResizableComboLBox::ApplyLimitsToPos(WINDOWPOS* lpwndpos)
  221. {
  222. //TRACE(">H w(%d)\n", lpwndpos->cy);
  223. // to adjust horizontally, use window rect
  224. // min width can't be less than combo's
  225. CRect rect;
  226. m_pOwnerCombo->GetWindowRect(&rect);
  227. m_sizeMin.cx = rect.Width();
  228. // apply horizontal limits
  229. if (lpwndpos->cx < m_sizeMin.cx)
  230. lpwndpos->cx = m_sizeMin.cx;
  231. // fix horizontal alignment
  232. rect = CRect(0, 0, lpwndpos->cx, lpwndpos->cy);
  233. m_pOwnerCombo->MapWindowPoints(NULL, &rect);
  234. lpwndpos->x = rect.left;
  235. // to adjust vertically, use client rect
  236. // get client rect
  237. rect = CRect(CPoint(lpwndpos->x, lpwndpos->y),
  238. CSize(lpwndpos->cx, lpwndpos->cy));
  239. SendMessage(WM_NCCALCSIZE, FALSE, (LPARAM)&rect);
  240. CSize sizeClient = rect.Size();
  241. // apply vertical limits
  242. if (sizeClient.cy < m_sizeMin.cy)
  243. sizeClient.cy = m_sizeMin.cy;
  244. //TRACE(">H c(%d)\n", sizeClient.cy);
  245. // adjust height, if needed
  246. sizeClient.cy = m_pOwnerCombo->MakeIntegralHeight(sizeClient.cy);
  247. //TRACE(">H c(%d)\n", sizeClient.cy);
  248. // back to window rect
  249. rect = CRect(0, 0, 1, sizeClient.cy);
  250. DWORD dwStyle = GetStyle();
  251. ::AdjustWindowRectEx(&rect, dwStyle, FALSE, GetExStyle());
  252. lpwndpos->cy = rect.Height();
  253. if (dwStyle & WS_HSCROLL)
  254. lpwndpos->cy += GetSystemMetrics(SM_CYHSCROLL);
  255. //TRACE("H c(%d) w(%d)\n", sizeClient.cy, lpwndpos->cy);
  256. }