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.

624 lines
13 KiB

  1. //-----------------------------------------------------------------------------
  2. // File: flexwnd.cpp
  3. //
  4. // Desc: CFlexWnd is a generic class that encapsulates the functionalities
  5. // of a window. All other window classes are derived from CFlexWnd.
  6. //
  7. // Child classes can have different behavior by overriding the
  8. // overridable message handlers (OnXXX members).
  9. //
  10. // Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
  11. //-----------------------------------------------------------------------------
  12. #include "common.hpp"
  13. #include "typeinfo.h"
  14. BOOL CFlexWnd::sm_bWndClassRegistered = FALSE;
  15. WNDCLASSEX CFlexWnd::sm_WndClass;
  16. LPCTSTR CFlexWnd::sm_tszWndClassName = _T("Microsoft.CFlexWnd.WndClassName");
  17. HINSTANCE CFlexWnd::sm_hInstance = NULL;
  18. CFlexToolTip CFlexWnd::s_ToolTip; // Shared tooltip window object
  19. DWORD CFlexWnd::s_dwLastMouseMove; // Last GetTickCount() that we have a WM_MOUSEMOVE
  20. HWND CFlexWnd::s_hWndLastMouseMove; // Last window handle of WM_MOUSEMOVE
  21. LPARAM CFlexWnd::s_PointLastMouseMove; // Last point of WM_MOUSEMOVE
  22. HWND CFlexWnd::s_CurrPageHwnd; // For unhighlighting callouts when a click is made outside of a callout
  23. int NewID()
  24. {
  25. static int i = 0;
  26. return ++i;
  27. }
  28. CFlexWnd::CFlexWnd() : m_nID(NewID()),
  29. m_hWnd(m_privhWnd), m_privhWnd(NULL), m_hRenderInto(NULL),
  30. m_bIsDialog(FALSE), m_bRender(FALSE),
  31. m_bReadOnly(FALSE)
  32. {
  33. }
  34. CFlexWnd::~CFlexWnd()
  35. {
  36. Destroy();
  37. }
  38. void CFlexWnd::Destroy()
  39. {
  40. if (m_hWnd != NULL)
  41. DestroyWindow(m_hWnd);
  42. assert(m_privhWnd == NULL);
  43. }
  44. BOOL CFlexWnd::IsDialog()
  45. {
  46. return HasWnd() && m_bIsDialog;
  47. }
  48. void CFlexWnd::OnRender(BOOL bInternalCall)
  49. {
  50. // if parent is flexwnd and both are in render mode, pass to parent
  51. if (!m_hWnd)
  52. return;
  53. HWND hParent = GetParent(m_hWnd);
  54. if (!hParent)
  55. return;
  56. CFlexWnd *pParent = GetFlexWnd(hParent);
  57. if (!pParent)
  58. return;
  59. if (pParent->InRenderMode() && InRenderMode())
  60. pParent->OnRender(TRUE);
  61. }
  62. BOOL CFlexWnd::OnEraseBkgnd(HDC hDC)
  63. {
  64. if (InRenderMode())
  65. return TRUE;
  66. /* if (IsDialog())
  67. return FALSE;*/
  68. return TRUE;
  69. }
  70. struct GETFLEXWNDSTRUCT {
  71. int cbSize;
  72. BOOL bFlexWnd;
  73. CFlexWnd *pFlexWnd;
  74. };
  75. // This function takes a HWND and returns a pointer to CFlexWnd if the HWND is a window
  76. // created by the UI.
  77. CFlexWnd *CFlexWnd::GetFlexWnd(HWND hWnd)
  78. {
  79. if (hWnd == NULL)
  80. return NULL;
  81. GETFLEXWNDSTRUCT gfws;
  82. gfws.cbSize = sizeof(gfws);
  83. gfws.bFlexWnd = FALSE;
  84. gfws.pFlexWnd = NULL;
  85. SendMessage(hWnd, WM_GETFLEXWND, 0, (LPARAM)(LPVOID)(FAR GETFLEXWNDSTRUCT *)&gfws);
  86. if (gfws.bFlexWnd)
  87. return gfws.pFlexWnd;
  88. else
  89. return NULL;
  90. }
  91. // Basic window proc. It simply forward interesting messages to the appropriate handlers (OnXXX).
  92. // If child class defines this function, it should pass unhandled messages to CFlexWnd.
  93. LRESULT CFlexWnd::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  94. {
  95. switch (msg)
  96. {
  97. case WM_GETFLEXWND:
  98. {
  99. if ((LPVOID)lParam == NULL)
  100. break;
  101. GETFLEXWNDSTRUCT &gfws = *((FAR GETFLEXWNDSTRUCT *)(LPVOID)lParam);
  102. switch (gfws.cbSize)
  103. {
  104. case sizeof(GETFLEXWNDSTRUCT):
  105. gfws.bFlexWnd = TRUE;
  106. gfws.pFlexWnd = this;
  107. return 0;
  108. default:
  109. assert(0);
  110. break;
  111. }
  112. break;
  113. }
  114. case WM_CREATE:
  115. {
  116. LPCREATESTRUCT lpCreateStruct = (LPCREATESTRUCT)lParam;
  117. LRESULT lr = OnCreate(lpCreateStruct);
  118. if (lr != -1)
  119. OnInit();
  120. return lr;
  121. }
  122. case WM_INITDIALOG:
  123. {
  124. BOOL b = OnInitDialog();
  125. OnInit();
  126. return b;
  127. }
  128. case WM_TIMER:
  129. OnTimer((UINT)wParam);
  130. return 0;
  131. case WM_ERASEBKGND:
  132. return OnEraseBkgnd((HDC)wParam);
  133. case WM_PAINT:
  134. {
  135. // Check the update rectangle. If we don't have it, exit immediately.
  136. if (typeid(*this) == typeid(CDeviceView) && !GetUpdateRect(m_hWnd, NULL, FALSE))
  137. return 0;
  138. PAINTSTRUCT ps;
  139. HDC hDC = BeginPaint(hWnd, &ps);
  140. if (InRenderMode())
  141. OnRender(TRUE);
  142. else
  143. DoOnPaint(hDC);
  144. EndPaint(hWnd, &ps);
  145. return 0;
  146. }
  147. case WM_COMMAND:
  148. {
  149. WORD wNotifyCode = HIWORD(wParam);
  150. WORD wID = LOWORD(wParam);
  151. HWND hWnd = (HWND)lParam;
  152. return OnCommand(wNotifyCode, wID, hWnd);
  153. }
  154. case WM_NOTIFY:
  155. return OnNotify(wParam, lParam);
  156. case WM_MOUSEMOVE:
  157. case WM_LBUTTONDOWN:
  158. case WM_RBUTTONDOWN:
  159. case WM_LBUTTONDBLCLK:
  160. case WM_MOUSEWHEEL:
  161. {
  162. POINT point = {int(LOWORD(lParam)), int(HIWORD(lParam))};
  163. switch (msg)
  164. {
  165. case WM_MOUSEMOVE: OnMouseOver(point, wParam); break;
  166. case WM_LBUTTONDOWN: OnClick(point, wParam, TRUE); break;
  167. case WM_RBUTTONDOWN: OnClick(point, wParam, FALSE); break;
  168. case WM_LBUTTONDBLCLK: OnDoubleClick(point, wParam, TRUE); break;
  169. case WM_MOUSEWHEEL:
  170. {
  171. // Send wheel msg to the window beneath the cursor
  172. HWND hWnd = WindowFromPoint(point);
  173. CFlexWnd *pWnd = NULL;
  174. if (hWnd)
  175. {
  176. pWnd = GetFlexWnd(hWnd);
  177. if (pWnd)
  178. pWnd->OnWheel(point, wParam);
  179. else
  180. return DefWindowProc(hWnd, msg, wParam, lParam);
  181. }
  182. break;
  183. }
  184. }
  185. return 0;
  186. }
  187. case WM_DESTROY:
  188. OnDestroy();
  189. m_privhWnd = NULL;
  190. return 0;
  191. }
  192. if (!m_bIsDialog)
  193. return DefWindowProc(hWnd, msg, wParam, lParam);
  194. else
  195. return 0;
  196. }
  197. //@@BEGIN_MSINTERNAL
  198. // TODO: better control id thingy
  199. //@@END_MSINTERNAL
  200. static HMENU windex = 0;
  201. BOOL CFlexWnd::EndDialog(int n)
  202. {
  203. if (!m_bIsDialog || m_hWnd == NULL)
  204. {
  205. assert(0);
  206. return FALSE;
  207. }
  208. return ::EndDialog(m_hWnd, n);
  209. }
  210. int CFlexWnd::DoModal(HWND hParent, int nTemplate, HINSTANCE hInst)
  211. {
  212. return DoModal(hParent, MAKEINTRESOURCE(nTemplate), hInst);
  213. }
  214. HWND CFlexWnd::DoModeless(HWND hParent, int nTemplate, HINSTANCE hInst)
  215. {
  216. return DoModeless(hParent, MAKEINTRESOURCE(nTemplate), hInst);
  217. }
  218. int CFlexWnd::DoModal(HWND hParent, LPCTSTR lpTemplate, HINSTANCE hInst)
  219. {
  220. if (m_hWnd != NULL)
  221. {
  222. assert(0);
  223. return -1;
  224. }
  225. if (hInst == NULL)
  226. hInst = CFlexWnd::sm_hInstance;
  227. return (int)DialogBoxParam(hInst, lpTemplate, hParent,
  228. (DLGPROC)__BaseFlexWndDialogProc, (LPARAM)(void *)this);
  229. }
  230. HWND CFlexWnd::DoModeless(HWND hParent, LPCTSTR lpTemplate, HINSTANCE hInst)
  231. {
  232. if (m_hWnd != NULL)
  233. {
  234. assert(0);
  235. return NULL;
  236. }
  237. if (hInst == NULL)
  238. hInst = CFlexWnd::sm_hInstance;
  239. return CreateDialogParam(hInst, lpTemplate, hParent,
  240. (DLGPROC)__BaseFlexWndDialogProc, (LPARAM)(void *)this);
  241. }
  242. HWND CFlexWnd::Create(HWND hParent, const RECT &rect, BOOL bVisible)
  243. {
  244. ++(*(LPBYTE*)&windex);
  245. return Create(hParent, _T("(unnamed)"), 0,
  246. WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_EX_NOPARENTNOTIFY | (bVisible ? WS_VISIBLE : 0),
  247. rect, windex);
  248. }
  249. HWND CFlexWnd::Create(HWND hParent, LPCTSTR tszName, DWORD dwExStyle, DWORD dwStyle, const RECT &rect, HMENU hMenu)
  250. {
  251. HWND hWnd = NULL;
  252. if (m_hWnd != NULL)
  253. {
  254. assert(0);
  255. return hWnd;
  256. }
  257. if (hMenu == NULL && (dwStyle & WS_CHILD))
  258. {
  259. ++(*(LPBYTE*)&windex);
  260. hMenu = windex;
  261. }
  262. hWnd = CreateWindowEx(
  263. dwExStyle,
  264. CFlexWnd::sm_tszWndClassName,
  265. tszName,
  266. dwStyle,
  267. rect.left, rect.top,
  268. rect.right - rect.left, rect.bottom - rect.top,
  269. hParent,
  270. hMenu,
  271. CFlexWnd::sm_hInstance,
  272. (void *)this);
  273. assert(m_hWnd == hWnd);
  274. return hWnd;
  275. }
  276. void CFlexWnd::SetHWND(HWND hWnd)
  277. {
  278. assert(m_hWnd == NULL && hWnd != NULL);
  279. m_privhWnd = hWnd;
  280. assert(m_hWnd == m_privhWnd);
  281. InitFlexWnd();
  282. }
  283. void CFlexWnd::InitFlexWnd()
  284. {
  285. if (!HasWnd())
  286. return;
  287. HWND hParent = GetParent(m_hWnd);
  288. CFlexWnd *pParent = GetFlexWnd(hParent);
  289. if (pParent && pParent->InRenderMode())
  290. SetRenderMode();
  291. }
  292. TCHAR sg_tszFlexWndPointerProp[] = _T("CFlexWnd *");
  293. LRESULT CALLBACK __BaseFlexWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  294. {
  295. CFlexWnd *pThis = (CFlexWnd *)GetProp(hWnd, sg_tszFlexWndPointerProp);
  296. if ((msg == WM_MOUSEMOVE || msg == WM_MOUSEWHEEL) && hWnd != CFlexWnd::s_ToolTip.m_hWnd)
  297. {
  298. // Filter out the message with same window handle and point.
  299. // Windows sometimes seems to send us WM_MOUSEMOVE message even though the mouse is not moved.
  300. if (CFlexWnd::s_hWndLastMouseMove != hWnd || CFlexWnd::s_PointLastMouseMove != lParam)
  301. {
  302. CFlexWnd::s_hWndLastMouseMove = hWnd;
  303. CFlexWnd::s_PointLastMouseMove = lParam;
  304. CFlexWnd::s_dwLastMouseMove = GetTickCount(); // Get timestamp
  305. CFlexWnd::s_ToolTip.SetEnable(FALSE);
  306. CFlexWnd::s_ToolTip.SetToolTipParent(NULL);
  307. }
  308. }
  309. switch (msg)
  310. {
  311. case WM_CREATE:
  312. {
  313. LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
  314. if (lpcs == NULL)
  315. break;
  316. pThis = (CFlexWnd *)(void *)(lpcs->lpCreateParams);
  317. assert(sizeof(HANDLE) == sizeof(CFlexWnd *));
  318. SetProp(hWnd, sg_tszFlexWndPointerProp, (HANDLE)pThis);
  319. if (pThis != NULL)
  320. {
  321. pThis->m_bIsDialog = FALSE;
  322. pThis->SetHWND(hWnd);
  323. }
  324. break;
  325. }
  326. }
  327. if (pThis != NULL)
  328. return pThis->WndProc(hWnd, msg, wParam, lParam);
  329. else
  330. return DefWindowProc(hWnd, msg, wParam, lParam);
  331. }
  332. LRESULT CALLBACK __BaseFlexWndDialogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  333. {
  334. CFlexWnd *pThis = (CFlexWnd *)GetProp(hWnd, sg_tszFlexWndPointerProp);
  335. switch (msg)
  336. {
  337. case WM_INITDIALOG:
  338. pThis = (CFlexWnd *)(void *)lParam;
  339. assert(sizeof(HANDLE) == sizeof(CFlexWnd *));
  340. SetProp(hWnd, sg_tszFlexWndPointerProp, (HANDLE)pThis);
  341. if (pThis != NULL)
  342. {
  343. pThis->m_bIsDialog = TRUE;
  344. pThis->SetHWND(hWnd);
  345. }
  346. break;
  347. }
  348. if (pThis != NULL)
  349. return (BOOL)pThis->WndProc(hWnd, msg, wParam, lParam);
  350. else
  351. return FALSE;
  352. }
  353. void CFlexWnd::Invalidate()
  354. {
  355. if (m_hWnd != NULL)
  356. InvalidateRect(m_hWnd, NULL, TRUE);
  357. }
  358. SIZE CFlexWnd::GetClientSize() const
  359. {
  360. RECT rect = {0, 0, 0, 0};
  361. if (m_hWnd != NULL)
  362. ::GetClientRect(m_hWnd, &rect);
  363. SIZE size = {
  364. rect.right - rect.left,
  365. rect.bottom - rect.top};
  366. return size;
  367. }
  368. void CFlexWnd::FillWndClass(HINSTANCE hInst)
  369. {
  370. sm_WndClass.cbSize = sizeof(WNDCLASSEX);
  371. sm_WndClass.style = CS_DBLCLKS;
  372. sm_WndClass.lpfnWndProc = __BaseFlexWndProc;
  373. sm_WndClass.cbClsExtra = 0;
  374. sm_WndClass.cbWndExtra = sizeof(CFlexWnd *);
  375. sm_WndClass.hInstance = sm_hInstance = hInst;
  376. sm_WndClass.hIcon = NULL;
  377. sm_WndClass.hCursor = NULL;
  378. sm_WndClass.hbrBackground = NULL;
  379. sm_WndClass.lpszMenuName = NULL;
  380. sm_WndClass.lpszClassName = sm_tszWndClassName;
  381. sm_WndClass.hIconSm = NULL;
  382. }
  383. void CFlexWnd::RegisterWndClass(HINSTANCE hInst)
  384. {
  385. if (hInst == NULL)
  386. {
  387. assert(0);
  388. return;
  389. }
  390. FillWndClass(hInst);
  391. RegisterClassEx(&sm_WndClass);
  392. sm_bWndClassRegistered = TRUE;
  393. }
  394. void CFlexWnd::UnregisterWndClass(HINSTANCE hInst)
  395. {
  396. if (hInst == NULL)
  397. return;
  398. UnregisterClass(sm_tszWndClassName, hInst);
  399. sm_bWndClassRegistered = FALSE;
  400. }
  401. void CFlexWnd::GetClientRect(LPRECT lprect) const
  402. {
  403. if (lprect == NULL || m_hWnd == NULL)
  404. return;
  405. ::GetClientRect(m_hWnd, lprect);
  406. }
  407. LPCTSTR CFlexWnd::GetDefaultClassName()
  408. {
  409. return CFlexWnd::sm_tszWndClassName;
  410. }
  411. void CFlexWnd::SetRenderMode(BOOL bRender)
  412. {
  413. if (bRender == m_bRender)
  414. return;
  415. m_bRender = bRender;
  416. Invalidate();
  417. }
  418. BOOL CFlexWnd::InRenderMode()
  419. {
  420. return m_bRender;
  421. }
  422. void EnumChildWindowsZDown(HWND hParent, WNDENUMPROC proc, LPARAM lParam)
  423. {
  424. if (hParent == NULL || proc == NULL)
  425. return;
  426. HWND hWnd = GetWindow(hParent, GW_CHILD);
  427. while (hWnd != NULL)
  428. {
  429. if (!proc(hWnd, lParam))
  430. break;
  431. hWnd = GetWindow(hWnd, GW_HWNDNEXT);
  432. }
  433. }
  434. void EnumSiblingsAbove(HWND hParent, WNDENUMPROC proc, LPARAM lParam)
  435. {
  436. if (hParent == NULL || proc == NULL)
  437. return;
  438. HWND hWnd = hParent;
  439. while (1)
  440. {
  441. hWnd = GetWindow(hWnd, GW_HWNDPREV);
  442. if (hWnd == NULL)
  443. break;
  444. if (!proc(hWnd, lParam))
  445. break;
  446. }
  447. }
  448. static BOOL CALLBACK RenderIntoClipChild(HWND hWnd, LPARAM lParam)
  449. {
  450. CFlexWnd *pThis = (CFlexWnd *)(LPVOID)lParam;
  451. return pThis->RenderIntoClipChild(hWnd);
  452. }
  453. static BOOL CALLBACK RenderIntoRenderChild(HWND hWnd, LPARAM lParam)
  454. {
  455. CFlexWnd *pThis = (CFlexWnd *)(LPVOID)lParam;
  456. // Check if this is the immediate child. Do nothing if it's not immediate.
  457. HWND hParent = GetParent(hWnd);
  458. if (hParent != pThis->m_hWnd)
  459. return TRUE;
  460. return pThis->RenderIntoRenderChild(hWnd);
  461. }
  462. BOOL CFlexWnd::RenderIntoClipChild(HWND hChild)
  463. {
  464. if (m_hRenderInto != NULL && HasWnd() && hChild && IsWindowVisible(hChild))
  465. {
  466. RECT rect;
  467. GetWindowRect(hChild, &rect);
  468. POINT ul = {rect.left, rect.top}, lr = {rect.right, rect.bottom};
  469. ScreenToClient(m_hWnd, &ul);
  470. ScreenToClient(m_hWnd, &lr);
  471. ExcludeClipRect(m_hRenderInto, ul.x, ul.y, lr.x, lr.y);
  472. }
  473. return TRUE;
  474. }
  475. BOOL CFlexWnd::RenderIntoRenderChild(HWND hChild)
  476. {
  477. CFlexWnd *pChild = GetFlexWnd(hChild);
  478. if (m_hRenderInto != NULL && HasWnd() && pChild != NULL && IsWindowVisible(hChild))
  479. {
  480. RECT rect;
  481. GetWindowRect(hChild, &rect);
  482. POINT ul = {rect.left, rect.top};
  483. ScreenToClient(m_hWnd, &ul);
  484. pChild->RenderInto(m_hRenderInto, ul.x, ul.y);
  485. }
  486. return TRUE;
  487. }
  488. void CFlexWnd::RenderInto(HDC hDC, int x, int y)
  489. {
  490. if (hDC == NULL)
  491. return;
  492. int sdc = SaveDC(hDC);
  493. {
  494. OffsetViewportOrgEx(hDC, x, y, NULL);
  495. SIZE size = GetClientSize();
  496. IntersectClipRect(hDC, 0, 0, size.cx, size.cy);
  497. m_hRenderInto = hDC;
  498. int sdc2 = SaveDC(hDC);
  499. {
  500. EnumChildWindows/*ZDown*/(m_hWnd, ::RenderIntoClipChild, (LPARAM)(PVOID)this);
  501. EnumSiblingsAbove(m_hWnd, ::RenderIntoClipChild, (LPARAM)(PVOID)this);
  502. DoOnPaint(hDC);
  503. }
  504. if (sdc2)
  505. RestoreDC(hDC, sdc2);
  506. EnumChildWindows/*ZDown*/(m_hWnd, ::RenderIntoRenderChild, (LPARAM)(PVOID)this);
  507. m_hRenderInto = NULL;
  508. }
  509. if (sdc)
  510. RestoreDC(hDC, sdc);
  511. }
  512. void CFlexWnd::SetCapture()
  513. {
  514. ::SetCapture(m_hWnd);
  515. }
  516. void CFlexWnd::ReleaseCapture()
  517. {
  518. ::ReleaseCapture();
  519. }
  520. void CFlexWnd::DoOnPaint(HDC hDC)
  521. {
  522. OnPaint(hDC);
  523. }