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.

1329 lines
26 KiB

  1. // File: GenWindow.cpp
  2. #include "precomp.h"
  3. #include "GenWindow.h"
  4. #include "GenContainers.h"
  5. #include <windowsx.h>
  6. // We need a different tooltip window for each top level window, or the tooltip
  7. // will get hidden behind the window
  8. struct TT_TopWindow
  9. {
  10. HWND hwndTop;
  11. HWND hwndTooltip;
  12. } ;
  13. class CTopWindowArray
  14. {
  15. private:
  16. enum { InitSize = 4 } ;
  17. TT_TopWindow *m_pArray;
  18. UINT m_nArrayLen;
  19. int FindIndex(HWND hwndTop)
  20. {
  21. if (NULL == m_pArray)
  22. {
  23. return(-1);
  24. }
  25. // Just a linear search
  26. int i;
  27. for (i=m_nArrayLen-1; i>=0; --i)
  28. {
  29. if (m_pArray[i].hwndTop == hwndTop)
  30. {
  31. break;
  32. }
  33. }
  34. return(i);
  35. }
  36. public:
  37. CTopWindowArray() :
  38. m_pArray(NULL)
  39. {
  40. }
  41. ~CTopWindowArray()
  42. {
  43. delete[] m_pArray;
  44. }
  45. static HWND GetTopFrame(HWND hwnd)
  46. {
  47. HWND hwndParent;
  48. while (NULL != (hwndParent = GetParent(hwnd)))
  49. {
  50. hwnd = hwndParent;
  51. }
  52. return(hwnd);
  53. }
  54. void GrowArray()
  55. {
  56. if (NULL == m_pArray)
  57. {
  58. m_nArrayLen = InitSize;
  59. m_pArray = new TT_TopWindow[m_nArrayLen];
  60. ZeroMemory(m_pArray, m_nArrayLen*sizeof(TT_TopWindow));
  61. return;
  62. }
  63. // Grow exponentially
  64. TT_TopWindow *pArray = new TT_TopWindow[m_nArrayLen*2];
  65. if (NULL == pArray)
  66. {
  67. // very bad
  68. return;
  69. }
  70. CopyMemory(pArray, m_pArray, m_nArrayLen*sizeof(TT_TopWindow));
  71. ZeroMemory(pArray+m_nArrayLen, m_nArrayLen*sizeof(TT_TopWindow));
  72. delete[] m_pArray;
  73. m_pArray = pArray;
  74. m_nArrayLen *= 2;
  75. }
  76. void Add(HWND hwndTop, HWND hwndTooltip)
  77. {
  78. hwndTop = GetTopFrame(hwndTop);
  79. // I'm going to allow multiple adds of the same thing, but then you
  80. // must have the corresponding number of removes
  81. int i = FindIndex(NULL);
  82. if (i < 0)
  83. {
  84. GrowArray();
  85. i = FindIndex(NULL);
  86. if (i < 0)
  87. {
  88. // Very bad
  89. return;
  90. }
  91. }
  92. m_pArray[i].hwndTop = hwndTop;
  93. m_pArray[i].hwndTooltip = hwndTooltip;
  94. }
  95. void Remove(HWND hwndTop)
  96. {
  97. hwndTop = GetTopFrame(hwndTop);
  98. int i = FindIndex(hwndTop);
  99. if (i >= 0)
  100. {
  101. // LAZYLAZY georgep: I'm never going to shrink the array
  102. m_pArray[i].hwndTop = NULL;
  103. m_pArray[i].hwndTooltip = NULL;
  104. }
  105. }
  106. HWND Find(HWND hwndTop)
  107. {
  108. hwndTop = GetTopFrame(hwndTop);
  109. int i = FindIndex(hwndTop);
  110. if (i >= 0)
  111. {
  112. return(m_pArray[i].hwndTooltip);
  113. }
  114. return(NULL);
  115. }
  116. int GetCount()
  117. {
  118. if (NULL == m_pArray)
  119. {
  120. return(0);
  121. }
  122. int c = 0;
  123. for (int i=m_nArrayLen-1; i>=0; --i)
  124. {
  125. if (NULL != m_pArray[i].hwndTop)
  126. {
  127. ++c;
  128. }
  129. }
  130. return(c);
  131. }
  132. } ;
  133. static inline BOOL TT_AddToolInfo(HWND hwnd, TOOLINFO *pti)
  134. {
  135. return (BOOL)(SendMessage(hwnd, TTM_ADDTOOL, 0, reinterpret_cast<LPARAM>(pti)) != 0);
  136. }
  137. static inline void TT_DelToolInfo(HWND hwnd, TOOLINFO *pti)
  138. {
  139. SendMessage(hwnd, TTM_DELTOOL, 0, reinterpret_cast<LPARAM>(pti));
  140. }
  141. static inline BOOL TT_GetToolInfo(HWND hwnd, TOOLINFO *pti)
  142. {
  143. return (BOOL)(SendMessage(hwnd, TTM_GETTOOLINFO, 0, reinterpret_cast<LPARAM>(pti)) != 0);
  144. }
  145. static inline void TT_SetToolInfo(HWND hwnd, TOOLINFO *pti)
  146. {
  147. SendMessage(hwnd, TTM_SETTOOLINFO, 0, reinterpret_cast<LPARAM>(pti));
  148. }
  149. static inline int TT_GetToolCount(HWND hwnd)
  150. {
  151. return (int)(SendMessage(hwnd, TTM_GETTOOLCOUNT, 0, 0));
  152. }
  153. CGenWindow *CGenWindow::g_pCurHot = NULL;
  154. const DWORD IGenWindow::c_msgFromHandle = RegisterWindowMessage(_TEXT("NetMeeting::FromHandle"));
  155. IGenWindow *IGenWindow::FromHandle(HWND hwnd)
  156. {
  157. return(reinterpret_cast<IGenWindow*>(SendMessage(hwnd, c_msgFromHandle, 0, 0)));
  158. }
  159. // HACKHACK georgep: Need to make this larger than the largest DM_ message
  160. enum
  161. {
  162. GWM_LAYOUT = WM_USER + 111,
  163. GWM_CUSTOM,
  164. } ;
  165. CGenWindow::CGenWindow()
  166. : m_hwnd(NULL), m_lUserData(0)
  167. {
  168. // Init the ref count to 1
  169. REFCOUNT::AddRef();
  170. // This marks this object for deletion when the ref count goes to 0.
  171. REFCOUNT::Delete();
  172. }
  173. CGenWindow::~CGenWindow()
  174. {
  175. // I don't think the HWND can still exist, since the window proc does an AddRef
  176. ASSERT(!m_hwnd);
  177. }
  178. HRESULT STDMETHODCALLTYPE CGenWindow::QueryInterface(REFGUID riid, LPVOID *ppv)
  179. {
  180. HRESULT hr = S_OK;
  181. if ((__uuidof(IGenWindow) == riid) || (IID_IUnknown == riid))
  182. {
  183. *ppv = dynamic_cast<IGenWindow *>(this);
  184. }
  185. else if (__uuidof(CGenWindow) == riid)
  186. {
  187. *ppv = this;
  188. }
  189. else
  190. {
  191. hr = E_NOINTERFACE;
  192. *ppv = NULL;
  193. }
  194. if (S_OK == hr)
  195. {
  196. AddRef();
  197. }
  198. return hr;
  199. }
  200. BOOL CGenWindow::Create(
  201. HWND hWndParent, // Window parent
  202. LPCTSTR szWindowName, // Window name
  203. DWORD dwStyle, // Window style
  204. DWORD dwEXStyle, // Extended window style
  205. int x, // Window pos: x
  206. int y, // Window pos: y
  207. int nWidth, // Window size: width
  208. int nHeight, // Window size: height
  209. HINSTANCE hInst, // The hInstance to create the window on
  210. HMENU hmMain, // Window menu
  211. LPCTSTR szClassName // The class name to use
  212. )
  213. {
  214. if (NULL != m_hwnd)
  215. {
  216. // Alread created
  217. return(FALSE);
  218. }
  219. if (NULL == szClassName)
  220. {
  221. szClassName = TEXT("NMGenWindowClass");
  222. }
  223. if (!InitWindowClass(szClassName, hInst))
  224. {
  225. // Couldn't init the window class
  226. return(FALSE);
  227. }
  228. BOOL ret = (NULL != CreateWindowEx(dwEXStyle, szClassName, szWindowName, dwStyle,
  229. x, y, nWidth, nHeight, hWndParent, hmMain,
  230. hInst, (LPVOID)this));
  231. #ifdef DEBUG
  232. if (!ret)
  233. {
  234. GetLastError();
  235. }
  236. #endif // DEBUG
  237. return(ret);
  238. }
  239. BOOL CGenWindow::Create(
  240. HWND hWndParent, // Window parent
  241. INT_PTR nId, // ID of the child window
  242. LPCTSTR szWindowName, // Window name
  243. DWORD dwStyle, // Window style; WS_CHILD|WS_VISIBLE will be added to this
  244. DWORD dwEXStyle // Extended window style
  245. )
  246. {
  247. ASSERT(NULL != hWndParent);
  248. // Child windows should default to visible
  249. return(Create(
  250. hWndParent, // Window parent
  251. szWindowName, // Window name
  252. dwStyle|WS_CHILD|WS_VISIBLE, // Window style
  253. dwEXStyle, // Extended window style
  254. 0, // Window pos: x
  255. 0, // Window pos: y
  256. 10, // Window size: width
  257. 10, // Window size: height
  258. reinterpret_cast<HINSTANCE>(GetWindowLongPtr(hWndParent, GWLP_HINSTANCE)),
  259. reinterpret_cast<HMENU>(nId) // Window menu
  260. ));
  261. }
  262. BOOL CGenWindow::InitWindowClass(LPCTSTR szClassName, HINSTANCE hThis)
  263. {
  264. WNDCLASS wc;
  265. // See if the class is already registered
  266. if (GetClassInfo(hThis, szClassName, &wc))
  267. {
  268. ASSERT(RealWindowProc == wc.lpfnWndProc);
  269. // Already registered
  270. return(TRUE);
  271. }
  272. // If not, attempt to register it
  273. wc.cbClsExtra = 0;
  274. wc.cbWndExtra = 0;
  275. // BUGBUG georgep: Hard-coding the background color for now
  276. // wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
  277. // wc.hbrBackground = CreateSolidBrush(RGB(0xA9, 0xA9, 0xA9));
  278. wc.hbrBackground = NULL;
  279. wc.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
  280. wc.hIcon = NULL;
  281. wc.hInstance = hThis;
  282. wc.lpfnWndProc = RealWindowProc;
  283. wc.lpszClassName = szClassName;
  284. wc.lpszMenuName = NULL;
  285. wc.style = CS_DBLCLKS;
  286. return(RegisterClass(&wc));
  287. }
  288. LRESULT CALLBACK CGenWindow::RealWindowProc(
  289. HWND hWnd,
  290. UINT message,
  291. WPARAM wParam,
  292. LPARAM lParam
  293. )
  294. {
  295. // Handle the WM_CREATE message
  296. if (WM_NCCREATE == message)
  297. {
  298. HANDLE_WM_NCCREATE(hWnd, wParam, lParam, OnNCCreate);
  299. }
  300. // Get the "this" pointer and call the ProcessMessage virtual method
  301. LRESULT ret = 0;
  302. CGenWindow* pWnd = reinterpret_cast<CGenWindow*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
  303. // 'pWnd' won't be valid for any messages that come before WM_NCCREATE or after WM_NCDESTROY
  304. if(NULL != pWnd)
  305. {
  306. // Messages after WM_NCCREATE:
  307. ret = pWnd->ProcessMessage(hWnd, message, wParam, lParam);
  308. }
  309. else
  310. {
  311. // Messages before WM_CREATE:
  312. ret = DefWindowProc(hWnd, message, wParam, lParam);
  313. }
  314. // Clean up on WM_NCDESTROY
  315. if (WM_NCDESTROY == message && NULL != pWnd)
  316. {
  317. SetWindowLongPtr(hWnd, GWLP_USERDATA, 0);
  318. pWnd->m_hwnd = NULL;
  319. pWnd->OnMouseLeave();
  320. pWnd->Release();
  321. }
  322. return(ret);
  323. }
  324. void CGenWindow::OnShowWindow(HWND hwnd, BOOL fShow, int fnStatus)
  325. {
  326. OnDesiredSizeChanged();
  327. }
  328. LRESULT CGenWindow::ProcessMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  329. {
  330. switch (message)
  331. {
  332. HANDLE_MSG(hwnd, WM_SIZE , OnSize);
  333. HANDLE_MSG(hwnd, WM_ERASEBKGND, OnEraseBkgnd);
  334. HANDLE_MSG(hwnd, WM_MOUSEMOVE , OnMouseMove);
  335. HANDLE_MSG(hwnd, WM_SHOWWINDOW, OnShowWindow);
  336. case WM_MOUSELEAVE:
  337. OnMouseLeave();
  338. break;
  339. case GWM_LAYOUT:
  340. Layout();
  341. break;
  342. case GWM_CUSTOM:
  343. reinterpret_cast<InvokeProc>(lParam)(this, wParam);
  344. break;
  345. case WM_DESTROY:
  346. RemoveTooltip();
  347. break;
  348. default:
  349. if (c_msgFromHandle == message)
  350. {
  351. // Return the IGenWindow* for this object, as specified by the
  352. // IGenWindow interface
  353. return(reinterpret_cast<LRESULT>(dynamic_cast<IGenWindow*>(this)));
  354. }
  355. }
  356. return(DefWindowProc(hwnd, message, wParam, lParam));
  357. }
  358. void CGenWindow::ScheduleLayout()
  359. {
  360. HWND hwnd = GetWindow();
  361. MSG msg;
  362. // I don't know why we are getting messages for windows other than our own,
  363. // but it seems to happen for top level windows
  364. if (PeekMessage(&msg, hwnd, GWM_LAYOUT, GWM_LAYOUT, PM_NOREMOVE|PM_NOYIELD)
  365. && (msg.hwnd == hwnd))
  366. {
  367. // Message already posted
  368. return;
  369. }
  370. if (!PostMessage(hwnd, GWM_LAYOUT, 0, 0))
  371. {
  372. Layout();
  373. }
  374. }
  375. BOOL CGenWindow::AsyncInvoke(InvokeProc proc, WPARAM wParam)
  376. {
  377. return(!PostMessage(GetWindow(), GWM_CUSTOM, wParam, reinterpret_cast<LPARAM>(proc)));
  378. }
  379. void CGenWindow::OnSize(HWND hwnd, UINT state, int cx, int cy)
  380. {
  381. // Call the virtual Layout, and then forward to DefWindowProc
  382. ScheduleLayout();
  383. // Update the Tooltip info
  384. TOOLINFO ti;
  385. TCHAR szTip[MAX_PATH];
  386. BOOL bExist = InitToolInfo(&ti, szTip);
  387. if (bExist)
  388. {
  389. GetClientRect(hwnd, &ti.rect);
  390. HWND hwndTooltip = g_pTopArray->Find(hwnd);
  391. TT_SetToolInfo(hwndTooltip, &ti);
  392. }
  393. FORWARD_WM_SIZE(hwnd, state, cx, cy, DefWindowProc);
  394. }
  395. BOOL CGenWindow::OnEraseBkgnd(HWND hwnd, HDC hdc)
  396. {
  397. HBRUSH hErase = GetBackgroundBrush();
  398. if (NULL == hErase)
  399. {
  400. return(FORWARD_WM_ERASEBKGND(hwnd, hdc, DefWindowProc));
  401. }
  402. HPALETTE hOldPal = NULL;
  403. HPALETTE hPal = GetPalette();
  404. if (NULL != hPal)
  405. {
  406. hOldPal = SelectPalette(hdc, hPal, TRUE);
  407. RealizePalette(hdc);
  408. }
  409. RECT rc;
  410. GetClientRect(hwnd, &rc);
  411. HBRUSH hOld = (HBRUSH)SelectObject(hdc, hErase);
  412. PatBlt(hdc, 0, 0, rc.right, rc.bottom, PATCOPY);
  413. SelectObject(hdc, hOld);
  414. if (NULL != hOldPal)
  415. {
  416. SelectPalette(hdc, hOldPal, TRUE);
  417. }
  418. return(TRUE);
  419. }
  420. void CGenWindow::OnMouseLeave()
  421. {
  422. if (dynamic_cast<IGenWindow*>(this) == g_pCurHot)
  423. {
  424. SetHotControl(NULL);
  425. }
  426. }
  427. void CGenWindow::OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
  428. {
  429. SetHotControl(this);
  430. FORWARD_WM_MOUSEMOVE(hwnd, x, y, keyFlags, DefWindowProc);
  431. }
  432. // REVIEW georgep: Should this loop until it gets an IGenWindow?
  433. HBRUSH CGenWindow::GetBackgroundBrush()
  434. {
  435. HWND parent = GetParent(GetWindow());
  436. if (NULL == parent)
  437. {
  438. return(GetStandardBrush());
  439. }
  440. IGenWindow *pParent = FromHandle(parent);
  441. if (pParent == NULL)
  442. {
  443. return(GetStandardBrush());
  444. }
  445. return(pParent->GetBackgroundBrush());
  446. }
  447. // REVIEW georgep: Should this loop until it gets an IGenWindow?
  448. HPALETTE CGenWindow::GetPalette()
  449. {
  450. HWND parent = GetParent(GetWindow());
  451. if (NULL == parent)
  452. {
  453. return(GetStandardPalette());
  454. }
  455. IGenWindow *pParent = FromHandle(parent);
  456. if (pParent == NULL)
  457. {
  458. return(GetStandardPalette());
  459. }
  460. return(pParent->GetPalette());
  461. }
  462. BOOL CGenWindow::OnNCCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
  463. {
  464. // Store away the "this" pointer ahnd save off the window handle
  465. CGenWindow* pWnd = NULL;
  466. pWnd = (CGenWindow*) lpCreateStruct->lpCreateParams;
  467. ASSERT(pWnd);
  468. SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) pWnd);
  469. pWnd->AddRef();
  470. TRACE_OUT(("CGenWindow::OnNCCreate"));
  471. ASSERT(NULL == pWnd->m_hwnd);
  472. pWnd->m_hwnd = hwnd;
  473. return(TRUE);
  474. }
  475. void CGenWindow::GetDesiredSize(SIZE *ppt)
  476. {
  477. HWND hwnd = GetWindow();
  478. RECT rcTemp = { 0, 0, 0, 0 };
  479. AdjustWindowRectEx(&rcTemp, GetWindowLong(hwnd, GWL_STYLE), FALSE,
  480. GetWindowLong(hwnd, GWL_EXSTYLE));
  481. ppt->cx = rcTemp.right - rcTemp.left;
  482. ppt->cy = rcTemp.bottom - rcTemp.top;
  483. }
  484. void CGenWindow::OnDesiredSizeChanged()
  485. {
  486. HWND parent = GetParent(GetWindow());
  487. if (NULL != parent)
  488. {
  489. IGenWindow *pParent = FromHandle(parent);
  490. if (NULL != pParent)
  491. {
  492. pParent->OnDesiredSizeChanged();
  493. }
  494. }
  495. // Do this after telling the parents about the change, so their layouts
  496. // will happen before this one
  497. ScheduleLayout();
  498. }
  499. class GWTrackMouseLeave
  500. {
  501. private:
  502. enum { DefIdTimer = 100 };
  503. enum { DefTimeout = 500 };
  504. static HWND m_hwnd;
  505. static UINT_PTR m_idTimer;
  506. static DWORD m_dwWhere;
  507. static void CALLBACK OnTimer(HWND hwnd, UINT uMsg, UINT_PTR idTimer, DWORD dwTime)
  508. {
  509. RECT rc;
  510. GetWindowRect(m_hwnd, &rc);
  511. DWORD dwPos = GetMessagePos();
  512. // If the mouse has not moved since this timer started, then leave it hot
  513. // This allows a reasonable keyboard-only interface
  514. if (m_dwWhere == dwPos)
  515. {
  516. return;
  517. }
  518. POINT ptPos = { GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos) };
  519. if (!PtInRect(&rc, ptPos))
  520. {
  521. PostMessage(m_hwnd, WM_MOUSELEAVE, 0, 0);
  522. }
  523. }
  524. public:
  525. GWTrackMouseLeave() {}
  526. static void Track(HWND hwnd, BOOL bTrack)
  527. {
  528. if (!bTrack)
  529. {
  530. if (NULL != m_hwnd && hwnd == m_hwnd)
  531. {
  532. KillTimer(NULL, m_idTimer);
  533. m_hwnd = NULL;
  534. }
  535. return;
  536. }
  537. // Stop any previous tracking
  538. Track(m_hwnd, FALSE);
  539. m_hwnd = hwnd;
  540. m_dwWhere = GetMessagePos();
  541. m_idTimer = SetTimer(NULL, DefIdTimer, DefTimeout, OnTimer);
  542. }
  543. } ;
  544. HWND GWTrackMouseLeave::m_hwnd = NULL;
  545. DWORD GWTrackMouseLeave::m_dwWhere = 0;
  546. UINT_PTR GWTrackMouseLeave::m_idTimer;
  547. static void GWTrackMouseEvent(HWND hwnd, BOOL bTrack)
  548. {
  549. // I need to set up a timer to handle this
  550. GWTrackMouseLeave::Track(hwnd, bTrack);
  551. }
  552. // Set the global Hot control
  553. void CGenWindow::SetHotControl(CGenWindow *pHot)
  554. {
  555. CGenWindow *pGenWindow = NULL;
  556. if (NULL != pHot)
  557. {
  558. for (HWND hwndHot=pHot->GetWindow(); ; hwndHot=GetParent(hwndHot))
  559. {
  560. if (NULL == hwndHot)
  561. {
  562. break;
  563. }
  564. IGenWindow *pWindow = FromHandle(hwndHot);
  565. if (NULL == pWindow)
  566. {
  567. continue;
  568. }
  569. if (SUCCEEDED(pWindow->QueryInterface(__uuidof(CGenWindow),
  570. reinterpret_cast<LPVOID*>(&pGenWindow)))
  571. && NULL != pGenWindow)
  572. {
  573. pGenWindow->SetHot(TRUE);
  574. // Not all windows may care about the hot state
  575. BOOL bIsHot = pGenWindow->IsHot();
  576. pGenWindow->Release();
  577. if (bIsHot)
  578. {
  579. break;
  580. }
  581. }
  582. pGenWindow = NULL;
  583. }
  584. }
  585. if (g_pCurHot != pGenWindow)
  586. {
  587. if (NULL != g_pCurHot)
  588. {
  589. g_pCurHot->SetHot(FALSE);
  590. GWTrackMouseEvent(g_pCurHot->GetWindow(), FALSE);
  591. ULONG uRef = g_pCurHot->Release();
  592. }
  593. g_pCurHot = pGenWindow;
  594. if (NULL!= g_pCurHot)
  595. {
  596. ULONG uRef = g_pCurHot->AddRef();
  597. // Now we need to track the mouse leaving
  598. GWTrackMouseEvent(g_pCurHot->GetWindow(), TRUE);
  599. }
  600. }
  601. }
  602. // Set this control to be hot
  603. void CGenWindow::SetHot(BOOL bHot)
  604. {
  605. }
  606. // Is this control currently hot
  607. BOOL CGenWindow::IsHot()
  608. {
  609. return(FALSE);
  610. }
  611. LPARAM CGenWindow::GetUserData()
  612. {
  613. return(m_lUserData);
  614. }
  615. HPALETTE CGenWindow::g_hPal = NULL;
  616. BOOL CGenWindow::g_bNeedPalette = TRUE;
  617. HBRUSH CGenWindow::g_hBrush = NULL;
  618. CTopWindowArray *CGenWindow::g_pTopArray = NULL;
  619. // Not particularly robust: we give out our internal palette and trust everybody
  620. // not to delete it
  621. HPALETTE CGenWindow::GetStandardPalette()
  622. {
  623. #include "indeopal.h"
  624. if (!g_bNeedPalette || NULL != g_hPal)
  625. {
  626. return(g_hPal);
  627. }
  628. HDC hDC = ::GetDC(NULL);
  629. if (NULL != hDC)
  630. {
  631. // Use the Indeo palette
  632. // Check out the video mode. We only care about 8 bit mode.
  633. if (8 == ::GetDeviceCaps(hDC, BITSPIXEL) * ::GetDeviceCaps(hDC, PLANES))
  634. {
  635. #ifndef HALFTONE_PALETTE
  636. LOGPALETTE_NM gIndeoPalette = gcLogPaletteIndeo;
  637. if (SYSPAL_NOSTATIC != ::GetSystemPaletteUse(hDC))
  638. {
  639. // Preserve the static colors
  640. int nStaticColors = ::GetDeviceCaps(hDC, NUMCOLORS) >> 1;
  641. if (nStaticColors <= 128)
  642. {
  643. // Get the 10 first entries
  644. ::GetSystemPaletteEntries( hDC,
  645. 0,
  646. nStaticColors,
  647. &gIndeoPalette.aEntries[0]);
  648. // Get the 10 last entries
  649. ::GetSystemPaletteEntries( hDC,
  650. 256 - nStaticColors,
  651. nStaticColors,
  652. &gIndeoPalette.aEntries[256 - nStaticColors]);
  653. // Hammer the peFlags
  654. for (; --nStaticColors + 1;)
  655. {
  656. gIndeoPalette.aEntries[nStaticColors].peFlags = 0;
  657. gIndeoPalette.aEntries[255 - nStaticColors].peFlags = 0;
  658. }
  659. }
  660. }
  661. // Build a palette
  662. g_hPal = ::CreatePalette((LOGPALETTE *)&gIndeoPalette);
  663. #else // HALFTONE_PALETTE
  664. g_hPal = ::CreateHalftonePalette(hDC);
  665. #endif // HALFTONE_PALETTE
  666. }
  667. ::ReleaseDC(NULL, hDC);
  668. }
  669. g_bNeedPalette = (NULL != g_hPal);
  670. return(g_hPal);
  671. }
  672. void CGenWindow::DeleteStandardPalette()
  673. {
  674. if (NULL != g_hPal)
  675. {
  676. DeleteObject(g_hPal);
  677. g_hPal = NULL;
  678. }
  679. }
  680. // Get the standard palette for drawing
  681. HBRUSH CGenWindow::GetStandardBrush()
  682. {
  683. return(GetSysColorBrush(COLOR_3DFACE));
  684. }
  685. // Delete the standard palette for drawing
  686. void CGenWindow::DeleteStandardBrush()
  687. {
  688. }
  689. // Returns TRUE if the TT exists
  690. BOOL CGenWindow::InitToolInfo(TOOLINFO *pti, LPTSTR pszText)
  691. {
  692. TCHAR szText[MAX_PATH];
  693. if (NULL == pszText)
  694. {
  695. pszText = szText;
  696. }
  697. HWND hwnd = GetWindow();
  698. HWND hwndTooltip = NULL == g_pTopArray ? NULL : g_pTopArray->Find(hwnd);
  699. TOOLINFO &ti = *pti;
  700. ti.cbSize = sizeof(TOOLINFO);
  701. ti.hwnd = hwnd;
  702. ti.hinst = GetWindowInstance(hwnd);
  703. ti.lpszText = pszText;
  704. GetClientRect(hwnd, &ti.rect);
  705. ti.uId = reinterpret_cast<UINT_PTR>(hwnd);
  706. ti.uFlags = TTF_SUBCLASS;
  707. GetSharedTooltipInfo(&ti);
  708. // HACKHACK georgep: The flags keep getting messed up by the tooltip window
  709. UINT uFlags = ti.uFlags;
  710. BOOL bExist = NULL == hwndTooltip ? FALSE : TT_GetToolInfo(hwndTooltip, &ti);
  711. ti.uFlags = uFlags;
  712. if (ti.lpszText == szText)
  713. {
  714. ti.lpszText = NULL;
  715. }
  716. return(bExist);
  717. }
  718. // Set the tooltip for this window
  719. void CGenWindow::SetTooltip(LPCTSTR pszTip)
  720. {
  721. HWND hwnd = GetWindow();
  722. if (NULL == g_pTopArray)
  723. {
  724. g_pTopArray = new CTopWindowArray;
  725. if (NULL == g_pTopArray)
  726. {
  727. return;
  728. }
  729. }
  730. HWND hwndTop = CTopWindowArray::GetTopFrame(hwnd);
  731. HWND hwndTooltip = g_pTopArray->Find(hwndTop);
  732. if (NULL == hwndTooltip)
  733. {
  734. hwndTooltip = CreateWindowEx(0,
  735. TOOLTIPS_CLASS,
  736. NULL,
  737. 0, // styles
  738. CW_USEDEFAULT,
  739. CW_USEDEFAULT,
  740. CW_USEDEFAULT,
  741. CW_USEDEFAULT,
  742. hwndTop,
  743. (HMENU) NULL,
  744. GetWindowInstance(hwnd),
  745. NULL);
  746. if (NULL == hwndTooltip)
  747. {
  748. // Couldn't create the tooltip window
  749. return;
  750. }
  751. g_pTopArray->Add(hwndTop, hwndTooltip);
  752. }
  753. TOOLINFO ti;
  754. BOOL bExist = InitToolInfo(&ti);
  755. ti.lpszText = const_cast<LPTSTR>(pszTip);
  756. if (bExist)
  757. {
  758. TT_SetToolInfo(hwndTooltip, &ti);
  759. }
  760. else
  761. {
  762. TT_AddToolInfo(hwndTooltip, &ti);
  763. }
  764. }
  765. // Remove the tooltip for this window
  766. void CGenWindow::RemoveTooltip()
  767. {
  768. if (NULL == g_pTopArray)
  769. {
  770. // Nothing to do
  771. return;
  772. }
  773. HWND hwndTop = CTopWindowArray::GetTopFrame(GetWindow());
  774. HWND hwndTooltip = g_pTopArray->Find(hwndTop);
  775. BOOL bIsWindow = NULL != hwndTooltip && IsWindow(hwndTooltip);
  776. TOOLINFO ti;
  777. BOOL bExist = bIsWindow && InitToolInfo(&ti);
  778. if (bExist)
  779. {
  780. TT_DelToolInfo(hwndTooltip, &ti);
  781. }
  782. if (NULL != hwndTooltip && (!bIsWindow || 0 == TT_GetToolCount(hwndTooltip)))
  783. {
  784. if (bIsWindow)
  785. {
  786. DestroyWindow(hwndTooltip);
  787. }
  788. g_pTopArray->Remove(hwndTop);
  789. if (0 == g_pTopArray->GetCount())
  790. {
  791. delete g_pTopArray;
  792. g_pTopArray = NULL;
  793. }
  794. }
  795. }
  796. // Get the info necessary for displaying a tooltip
  797. void CGenWindow::GetSharedTooltipInfo(TOOLINFO *pti)
  798. {
  799. }
  800. // Just makes the first child fill the client area
  801. void CFillWindow::Layout()
  802. {
  803. HWND child = GetChild();
  804. if (NULL != child)
  805. {
  806. RECT rc;
  807. GetClientRect(GetWindow(), &rc);
  808. SetWindowPos(child, NULL, 0, 0, rc.right, rc.bottom, SWP_NOZORDER);
  809. }
  810. }
  811. void CFillWindow::GetDesiredSize(SIZE *psize)
  812. {
  813. CGenWindow::GetDesiredSize(psize);
  814. HWND child = GetChild();
  815. if (NULL != child)
  816. {
  817. IGenWindow *pChild = FromHandle(child);
  818. if (NULL != pChild)
  819. {
  820. SIZE sizeTemp;
  821. pChild->GetDesiredSize(&sizeTemp);
  822. psize->cx += sizeTemp.cx;
  823. psize->cy += sizeTemp.cy;
  824. }
  825. }
  826. }
  827. // Get the info necessary for displaying a tooltip
  828. void CFillWindow::GetSharedTooltipInfo(TOOLINFO *pti)
  829. {
  830. CGenWindow::GetSharedTooltipInfo(pti);
  831. // Since the child covers this whole area, we need to change the HWND to
  832. // hook
  833. pti->hwnd = GetChild();
  834. }
  835. CEdgedWindow::CEdgedWindow() :
  836. m_hMargin(0),
  837. m_vMargin(0),
  838. m_pHeader(NULL)
  839. {
  840. }
  841. CEdgedWindow::~CEdgedWindow()
  842. {
  843. SetHeader(NULL);
  844. }
  845. BOOL CEdgedWindow::Create(HWND hwndParent)
  846. {
  847. return(CGenWindow::Create(
  848. hwndParent, // Window parent
  849. 0, // ID of the child window
  850. TEXT("NMEdgedWindow"), // Window name
  851. WS_CLIPCHILDREN, // Window style; WS_CHILD|WS_VISIBLE will be added to this
  852. WS_EX_CONTROLPARENT // Extended window style
  853. ));
  854. }
  855. HWND CEdgedWindow::GetContentWindow()
  856. {
  857. // If we are hosting an IGenWindow, add on its desired size
  858. HWND child = GetFirstChild(GetWindow());
  859. if (NULL == child)
  860. {
  861. return(NULL);
  862. }
  863. if (NULL != m_pHeader && child == m_pHeader->GetWindow())
  864. {
  865. child = ::GetWindow(child, GW_HWNDNEXT);
  866. }
  867. return(child);
  868. }
  869. static const int LeftIndent = 20;
  870. // Just makes the first child fill the client area - the border
  871. void CEdgedWindow::Layout()
  872. {
  873. int nBorder = GetBorderWidth();
  874. int hBorder = m_hMargin + nBorder;
  875. int vBorder = m_vMargin + nBorder;
  876. HWND hwnd = GetWindow();
  877. RECT rc;
  878. GetClientRect(hwnd, &rc);
  879. CGenWindow *pHeader = GetHeader();
  880. if (NULL != pHeader)
  881. {
  882. SIZE sizeTemp;
  883. pHeader->GetDesiredSize(&sizeTemp);
  884. SetWindowPos(pHeader->GetWindow(), NULL, rc.left+LeftIndent, rc.top,
  885. sizeTemp.cx, sizeTemp.cy, SWP_NOZORDER|SWP_NOACTIVATE);
  886. rc.top += sizeTemp.cy;
  887. }
  888. HWND child = GetContentWindow();
  889. if (NULL != child)
  890. {
  891. SetWindowPos(child, NULL, rc.left+hBorder, rc.top+vBorder,
  892. rc.right-rc.left-2*hBorder, rc.bottom-rc.top-2*vBorder, SWP_NOZORDER|SWP_NOACTIVATE);
  893. }
  894. }
  895. void CEdgedWindow::GetDesiredSize(SIZE *psize)
  896. {
  897. int nBorder = GetBorderWidth();
  898. int hBorder = m_hMargin + nBorder;
  899. int vBorder = m_vMargin + nBorder;
  900. CGenWindow::GetDesiredSize(psize);
  901. psize->cx += 2*hBorder;
  902. psize->cy += 2*vBorder;
  903. // If we are hosting an IGenWindow, add on its desired size
  904. HWND child = GetContentWindow();
  905. if (NULL == child)
  906. {
  907. return;
  908. }
  909. IGenWindow *pChild = FromHandle(child);
  910. if (NULL == pChild)
  911. {
  912. return;
  913. }
  914. SIZE size;
  915. pChild->GetDesiredSize(&size);
  916. psize->cx += size.cx;
  917. psize->cy += size.cy;
  918. CGenWindow *pHeader = GetHeader();
  919. if (NULL != pHeader)
  920. {
  921. SIZE sizeTemp;
  922. pHeader->GetDesiredSize(&sizeTemp);
  923. psize->cy += sizeTemp.cy;
  924. psize->cx = max(psize->cx, sizeTemp.cx+LeftIndent+hBorder);
  925. }
  926. }
  927. void CEdgedWindow::OnPaint(HWND hwnd)
  928. {
  929. PAINTSTRUCT ps;
  930. HDC hdc = BeginPaint(hwnd, &ps);
  931. RECT rc;
  932. GetClientRect(hwnd, &rc);
  933. CGenWindow *pHeader = GetHeader();
  934. if (NULL != pHeader)
  935. {
  936. SIZE sizeTemp;
  937. pHeader->GetDesiredSize(&sizeTemp);
  938. // Make the etch go through the middle of the header
  939. rc.top += (sizeTemp.cy-GetBorderWidth()) / 2;
  940. }
  941. DrawEdge(hdc, &rc, EDGE_ETCHED, BF_RECT);
  942. EndPaint(hwnd, &ps);
  943. }
  944. LRESULT CEdgedWindow::ProcessMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  945. {
  946. switch (message)
  947. {
  948. HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
  949. case WM_DESTROY:
  950. SetHeader(NULL);
  951. break;
  952. case WM_SIZE:
  953. // Need to invalidate if we bacame larger to redraw the border in the
  954. // right place
  955. InvalidateRect(hwnd, NULL, TRUE);
  956. break;
  957. }
  958. return(CGenWindow::ProcessMessage(hwnd, message, wParam, lParam));
  959. }
  960. void CEdgedWindow::SetHeader(CGenWindow *pHeader)
  961. {
  962. if (NULL != m_pHeader)
  963. {
  964. m_pHeader->Release();
  965. }
  966. m_pHeader = pHeader;
  967. if (NULL != m_pHeader)
  968. {
  969. m_pHeader->AddRef();
  970. }
  971. }
  972. BOOL CFrame::Create(
  973. HWND hWndOwner, // Window owner
  974. LPCTSTR szWindowName, // Window name
  975. DWORD dwStyle, // Window style
  976. DWORD dwEXStyle, // Extended window style
  977. int x, // Window pos: x
  978. int y, // Window pos: y
  979. int nWidth, // Window size: width
  980. int nHeight, // Window size: height
  981. HINSTANCE hInst, // The hInstance to create the window on
  982. HICON hIcon, // The icon for the window
  983. HMENU hmMain, // Window menu
  984. LPCTSTR szClassName // The class name to use
  985. )
  986. {
  987. if (!CFillWindow::Create(hWndOwner, szWindowName, dwStyle, dwEXStyle,
  988. x, y, nWidth, nHeight, hInst, hmMain, szClassName))
  989. {
  990. return(FALSE);
  991. }
  992. if (NULL != hIcon)
  993. {
  994. SendMessage(GetWindow(), WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIcon));
  995. }
  996. return(TRUE);
  997. }
  998. void CFrame::Resize()
  999. {
  1000. Resize(this, 0);
  1001. }
  1002. void CFrame::Resize(CGenWindow *pThis, WPARAM wParam)
  1003. {
  1004. SIZE size;
  1005. pThis->GetDesiredSize(&size);
  1006. SetWindowPos(pThis->GetWindow(), NULL, 0, 0, size.cx, size.cy,
  1007. SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
  1008. }
  1009. void CFrame::OnDesiredSizeChanged()
  1010. {
  1011. // I should probably look at the window style and only do this if it is
  1012. // not resizable. But then that would be wrong sometimes too, so just
  1013. // override this if you want different behavior.
  1014. AsyncInvoke(Resize, 0);
  1015. }
  1016. LRESULT CFrame::ProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1017. {
  1018. switch (uMsg)
  1019. {
  1020. HANDLE_MSG(hwnd, WM_PALETTECHANGED , OnPaletteChanged);
  1021. HANDLE_MSG(hwnd, WM_QUERYNEWPALETTE, OnQueryNewPalette);
  1022. }
  1023. return(CFillWindow::ProcessMessage(hwnd, uMsg, wParam, lParam));
  1024. }
  1025. void CFrame::OnPaletteChanged(HWND hwnd, HWND hwndPaletteChange)
  1026. {
  1027. SelAndRealizePalette(TRUE);
  1028. ::RedrawWindow(GetWindow(), NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
  1029. }
  1030. BOOL CFrame::SelAndRealizePalette(BOOL bBackground)
  1031. {
  1032. BOOL bRet = FALSE;
  1033. HPALETTE hPal = GetPalette();
  1034. if (NULL == hPal)
  1035. {
  1036. return(bRet);
  1037. }
  1038. HWND hwnd = GetWindow();
  1039. HDC hdc = ::GetDC(hwnd);
  1040. if (NULL != hdc)
  1041. {
  1042. ::SelectPalette(hdc, hPal, bBackground);
  1043. bRet = (GDI_ERROR != ::RealizePalette(hdc));
  1044. ::ReleaseDC(hwnd, hdc);
  1045. }
  1046. return bRet;
  1047. }
  1048. BOOL CFrame::OnQueryNewPalette(HWND hwnd)
  1049. {
  1050. return(SelAndRealizePalette(FALSE));
  1051. }
  1052. BOOL CFrame::SetForeground()
  1053. {
  1054. BOOL bRet = FALSE;
  1055. HWND hwnd = GetWindow();
  1056. if (NULL != hwnd)
  1057. {
  1058. WINDOWPLACEMENT wp;
  1059. wp.length = sizeof(wp);
  1060. if (::GetWindowPlacement(hwnd, &wp) &&
  1061. ((SW_MINIMIZE == wp.showCmd) || (SW_SHOWMINIMIZED == wp.showCmd)))
  1062. {
  1063. // The window is minimized - restore it:
  1064. ::ShowWindow(hwnd, SW_RESTORE);
  1065. }
  1066. else
  1067. {
  1068. ::ShowWindow(hwnd, SW_SHOW);
  1069. }
  1070. // Bring it to the foreground
  1071. SetForegroundWindow(hwnd);
  1072. bRet = TRUE;
  1073. }
  1074. return bRet;
  1075. }
  1076. void CFrame::MoveEnsureVisible(int x, int y)
  1077. {
  1078. static const int MinVis = 16;
  1079. RECT rcThis;
  1080. GetWindowRect(GetWindow(), &rcThis);
  1081. // Change to width and height
  1082. rcThis.right -= rcThis.left;
  1083. rcThis.bottom -= rcThis.top;
  1084. RECT rcDesktop;
  1085. SystemParametersInfo(SPI_GETWORKAREA, 0, &rcDesktop, 0);
  1086. if ((x+rcThis.right < rcDesktop.left+MinVis) || (x > rcDesktop.right-MinVis))
  1087. {
  1088. x = (rcDesktop.left + rcDesktop.right - rcThis.right) / 2;
  1089. }
  1090. if ((y+rcThis.bottom < rcDesktop.top+MinVis) || (y > rcDesktop.bottom-MinVis))
  1091. {
  1092. y = (rcDesktop.top + rcDesktop.bottom - rcThis.bottom) / 2;
  1093. }
  1094. SetWindowPos(GetWindow(), NULL, x, y, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
  1095. }
  1096.