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.

1510 lines
31 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. void CGenWindow::SetWindowtext(LPCTSTR pszTip)
  719. {
  720. HWND hwnd = GetWindow();
  721. if(NULL != hwnd)
  722. {
  723. HWND child = (GetTopWindow(hwnd));
  724. if (NULL != child)
  725. {
  726. ::SetWindowText(child,pszTip);
  727. }
  728. }
  729. }
  730. // Set the tooltip for this window
  731. void CGenWindow::SetTooltip(LPCTSTR pszTip)
  732. {
  733. HWND hwnd = GetWindow();
  734. if (NULL == g_pTopArray)
  735. {
  736. g_pTopArray = new CTopWindowArray;
  737. if (NULL == g_pTopArray)
  738. {
  739. return;
  740. }
  741. }
  742. HWND hwndTop = CTopWindowArray::GetTopFrame(hwnd);
  743. HWND hwndTooltip = g_pTopArray->Find(hwndTop);
  744. if (NULL == hwndTooltip)
  745. {
  746. hwndTooltip = CreateWindowEx(0,
  747. TOOLTIPS_CLASS,
  748. NULL,
  749. 0, // styles
  750. CW_USEDEFAULT,
  751. CW_USEDEFAULT,
  752. CW_USEDEFAULT,
  753. CW_USEDEFAULT,
  754. hwndTop,
  755. (HMENU) NULL,
  756. GetWindowInstance(hwnd),
  757. NULL);
  758. if (NULL == hwndTooltip)
  759. {
  760. // Couldn't create the tooltip window
  761. return;
  762. }
  763. g_pTopArray->Add(hwndTop, hwndTooltip);
  764. }
  765. TOOLINFO ti;
  766. BOOL bExist = InitToolInfo(&ti);
  767. ti.lpszText = const_cast<LPTSTR>(pszTip);
  768. if (bExist)
  769. {
  770. TT_SetToolInfo(hwndTooltip, &ti);
  771. }
  772. else
  773. {
  774. TT_AddToolInfo(hwndTooltip, &ti);
  775. }
  776. }
  777. // Remove the tooltip for this window
  778. void CGenWindow::RemoveTooltip()
  779. {
  780. if (NULL == g_pTopArray)
  781. {
  782. // Nothing to do
  783. return;
  784. }
  785. HWND hwndTop = CTopWindowArray::GetTopFrame(GetWindow());
  786. HWND hwndTooltip = g_pTopArray->Find(hwndTop);
  787. BOOL bIsWindow = NULL != hwndTooltip && IsWindow(hwndTooltip);
  788. TOOLINFO ti;
  789. BOOL bExist = bIsWindow && InitToolInfo(&ti);
  790. if (bExist)
  791. {
  792. TT_DelToolInfo(hwndTooltip, &ti);
  793. }
  794. if (NULL != hwndTooltip && (!bIsWindow || 0 == TT_GetToolCount(hwndTooltip)))
  795. {
  796. if (bIsWindow)
  797. {
  798. DestroyWindow(hwndTooltip);
  799. }
  800. g_pTopArray->Remove(hwndTop);
  801. if (0 == g_pTopArray->GetCount())
  802. {
  803. delete g_pTopArray;
  804. g_pTopArray = NULL;
  805. }
  806. }
  807. }
  808. // Get the info necessary for displaying a tooltip
  809. void CGenWindow::GetSharedTooltipInfo(TOOLINFO *pti)
  810. {
  811. }
  812. // Just makes the first child fill the client area
  813. void CFillWindow::Layout()
  814. {
  815. HWND child = GetChild();
  816. if (NULL != child)
  817. {
  818. RECT rc;
  819. GetClientRect(GetWindow(), &rc);
  820. SetWindowPos(child, NULL, 0, 0, rc.right, rc.bottom, SWP_NOZORDER);
  821. }
  822. }
  823. void CFillWindow::GetDesiredSize(SIZE *psize)
  824. {
  825. CGenWindow::GetDesiredSize(psize);
  826. HWND child = GetChild();
  827. if (NULL != child)
  828. {
  829. IGenWindow *pChild = FromHandle(child);
  830. if (NULL != pChild)
  831. {
  832. SIZE sizeTemp;
  833. pChild->GetDesiredSize(&sizeTemp);
  834. psize->cx += sizeTemp.cx;
  835. psize->cy += sizeTemp.cy;
  836. }
  837. }
  838. }
  839. // Get the info necessary for displaying a tooltip
  840. void CFillWindow::GetSharedTooltipInfo(TOOLINFO *pti)
  841. {
  842. CGenWindow::GetSharedTooltipInfo(pti);
  843. // Since the child covers this whole area, we need to change the HWND to
  844. // hook
  845. pti->hwnd = GetChild();
  846. }
  847. CEdgedWindow::CEdgedWindow() :
  848. m_hMargin(0),
  849. m_vMargin(0),
  850. m_pHeader(NULL)
  851. {
  852. }
  853. CEdgedWindow::~CEdgedWindow()
  854. {
  855. SetHeader(NULL);
  856. }
  857. BOOL CEdgedWindow::Create(HWND hwndParent)
  858. {
  859. return(CGenWindow::Create(
  860. hwndParent, // Window parent
  861. 0, // ID of the child window
  862. TEXT("NMEdgedWindow"), // Window name
  863. WS_CLIPCHILDREN, // Window style; WS_CHILD|WS_VISIBLE will be added to this
  864. WS_EX_CONTROLPARENT // Extended window style
  865. ));
  866. }
  867. HWND CEdgedWindow::GetContentWindow()
  868. {
  869. // If we are hosting an IGenWindow, add on its desired size
  870. HWND child = GetFirstChild(GetWindow());
  871. if (NULL == child)
  872. {
  873. return(NULL);
  874. }
  875. if (NULL != m_pHeader && child == m_pHeader->GetWindow())
  876. {
  877. child = ::GetWindow(child, GW_HWNDNEXT);
  878. }
  879. return(child);
  880. }
  881. static const int LeftIndent = 20;
  882. // Just makes the first child fill the client area - the border
  883. void CEdgedWindow::Layout()
  884. {
  885. int nBorder = GetBorderWidth();
  886. int hBorder = m_hMargin + nBorder;
  887. int vBorder = m_vMargin + nBorder;
  888. HWND hwnd = GetWindow();
  889. RECT rc;
  890. GetClientRect(hwnd, &rc);
  891. CGenWindow *pHeader = GetHeader();
  892. if (NULL != pHeader)
  893. {
  894. SIZE sizeTemp;
  895. pHeader->GetDesiredSize(&sizeTemp);
  896. SetWindowPos(pHeader->GetWindow(), NULL, rc.left+LeftIndent, rc.top,
  897. sizeTemp.cx, sizeTemp.cy, SWP_NOZORDER|SWP_NOACTIVATE);
  898. rc.top += sizeTemp.cy;
  899. }
  900. HWND child = GetContentWindow();
  901. if (NULL != child)
  902. {
  903. SetWindowPos(child, NULL, rc.left+hBorder, rc.top+vBorder,
  904. rc.right-rc.left-2*hBorder, rc.bottom-rc.top-2*vBorder, SWP_NOZORDER|SWP_NOACTIVATE);
  905. }
  906. }
  907. void CEdgedWindow::GetDesiredSize(SIZE *psize)
  908. {
  909. int nBorder = GetBorderWidth();
  910. int hBorder = m_hMargin + nBorder;
  911. int vBorder = m_vMargin + nBorder;
  912. CGenWindow::GetDesiredSize(psize);
  913. psize->cx += 2*hBorder;
  914. psize->cy += 2*vBorder;
  915. // If we are hosting an IGenWindow, add on its desired size
  916. HWND child = GetContentWindow();
  917. if (NULL == child)
  918. {
  919. return;
  920. }
  921. IGenWindow *pChild = FromHandle(child);
  922. if (NULL == pChild)
  923. {
  924. return;
  925. }
  926. SIZE size;
  927. pChild->GetDesiredSize(&size);
  928. psize->cx += size.cx;
  929. psize->cy += size.cy;
  930. CGenWindow *pHeader = GetHeader();
  931. if (NULL != pHeader)
  932. {
  933. SIZE sizeTemp;
  934. pHeader->GetDesiredSize(&sizeTemp);
  935. psize->cy += sizeTemp.cy;
  936. psize->cx = max(psize->cx, sizeTemp.cx+LeftIndent+hBorder);
  937. }
  938. }
  939. void CEdgedWindow::OnPaint(HWND hwnd)
  940. {
  941. PAINTSTRUCT ps;
  942. HDC hdc = BeginPaint(hwnd, &ps);
  943. RECT rc;
  944. GetClientRect(hwnd, &rc);
  945. CGenWindow *pHeader = GetHeader();
  946. if (NULL != pHeader)
  947. {
  948. SIZE sizeTemp;
  949. pHeader->GetDesiredSize(&sizeTemp);
  950. // Make the etch go through the middle of the header
  951. rc.top += (sizeTemp.cy-GetBorderWidth()) / 2;
  952. }
  953. DrawEdge(hdc, &rc, EDGE_ETCHED, BF_RECT);
  954. EndPaint(hwnd, &ps);
  955. }
  956. LRESULT CEdgedWindow::ProcessMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  957. {
  958. switch (message)
  959. {
  960. HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
  961. case WM_DESTROY:
  962. SetHeader(NULL);
  963. break;
  964. case WM_SIZE:
  965. // Need to invalidate if we bacame larger to redraw the border in the
  966. // right place
  967. InvalidateRect(hwnd, NULL, TRUE);
  968. break;
  969. }
  970. return(CGenWindow::ProcessMessage(hwnd, message, wParam, lParam));
  971. }
  972. void CEdgedWindow::SetHeader(CGenWindow *pHeader)
  973. {
  974. if (NULL != m_pHeader)
  975. {
  976. m_pHeader->Release();
  977. }
  978. m_pHeader = pHeader;
  979. if (NULL != m_pHeader)
  980. {
  981. m_pHeader->AddRef();
  982. }
  983. }
  984. BOOL CFrame::Create(
  985. HWND hWndOwner, // Window owner
  986. LPCTSTR szWindowName, // Window name
  987. DWORD dwStyle, // Window style
  988. DWORD dwEXStyle, // Extended window style
  989. int x, // Window pos: x
  990. int y, // Window pos: y
  991. int nWidth, // Window size: width
  992. int nHeight, // Window size: height
  993. HINSTANCE hInst, // The hInstance to create the window on
  994. HICON hIcon, // The icon for the window
  995. HMENU hmMain, // Window menu
  996. LPCTSTR szClassName // The class name to use
  997. )
  998. {
  999. if (!CFillWindow::Create(hWndOwner, szWindowName, dwStyle, dwEXStyle,
  1000. x, y, nWidth, nHeight, hInst, hmMain, szClassName))
  1001. {
  1002. return(FALSE);
  1003. }
  1004. if (NULL != hIcon)
  1005. {
  1006. SendMessage(GetWindow(), WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIcon));
  1007. }
  1008. return(TRUE);
  1009. }
  1010. void CFrame::Resize()
  1011. {
  1012. Resize(this, 0);
  1013. }
  1014. void CFrame::Resize(CGenWindow *pThis, WPARAM wParam)
  1015. {
  1016. SIZE size;
  1017. pThis->GetDesiredSize(&size);
  1018. SetWindowPos(pThis->GetWindow(), NULL, 0, 0, size.cx, size.cy,
  1019. SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
  1020. }
  1021. void CFrame::OnDesiredSizeChanged()
  1022. {
  1023. // I should probably look at the window style and only do this if it is
  1024. // not resizable. But then that would be wrong sometimes too, so just
  1025. // override this if you want different behavior.
  1026. AsyncInvoke(Resize, 0);
  1027. }
  1028. LRESULT CFrame::ProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1029. {
  1030. switch (uMsg)
  1031. {
  1032. HANDLE_MSG(hwnd, WM_PALETTECHANGED , OnPaletteChanged);
  1033. HANDLE_MSG(hwnd, WM_QUERYNEWPALETTE, OnQueryNewPalette);
  1034. }
  1035. return(CFillWindow::ProcessMessage(hwnd, uMsg, wParam, lParam));
  1036. }
  1037. void CFrame::OnPaletteChanged(HWND hwnd, HWND hwndPaletteChange)
  1038. {
  1039. SelAndRealizePalette(TRUE);
  1040. ::RedrawWindow(GetWindow(), NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
  1041. }
  1042. BOOL CFrame::SelAndRealizePalette(BOOL bBackground)
  1043. {
  1044. BOOL bRet = FALSE;
  1045. HPALETTE hPal = GetPalette();
  1046. if (NULL == hPal)
  1047. {
  1048. return(bRet);
  1049. }
  1050. HWND hwnd = GetWindow();
  1051. HDC hdc = ::GetDC(hwnd);
  1052. if (NULL != hdc)
  1053. {
  1054. ::SelectPalette(hdc, hPal, bBackground);
  1055. bRet = (GDI_ERROR != ::RealizePalette(hdc));
  1056. ::ReleaseDC(hwnd, hdc);
  1057. }
  1058. return bRet;
  1059. }
  1060. BOOL CFrame::OnQueryNewPalette(HWND hwnd)
  1061. {
  1062. return(SelAndRealizePalette(FALSE));
  1063. }
  1064. BOOL CFrame::SetForeground()
  1065. {
  1066. BOOL bRet = FALSE;
  1067. HWND hwnd = GetWindow();
  1068. if (NULL != hwnd)
  1069. {
  1070. WINDOWPLACEMENT wp;
  1071. wp.length = sizeof(wp);
  1072. if (::GetWindowPlacement(hwnd, &wp) &&
  1073. ((SW_MINIMIZE == wp.showCmd) || (SW_SHOWMINIMIZED == wp.showCmd)))
  1074. {
  1075. // The window is minimized - restore it:
  1076. ::ShowWindow(hwnd, SW_RESTORE);
  1077. }
  1078. else
  1079. {
  1080. ::ShowWindow(hwnd, SW_SHOW);
  1081. }
  1082. // Bring it to the foreground
  1083. SetForegroundWindow(hwnd);
  1084. bRet = TRUE;
  1085. }
  1086. return bRet;
  1087. }
  1088. void CFrame::MoveEnsureVisible(int x, int y)
  1089. {
  1090. static const int MinVis = 16;
  1091. RECT rcThis;
  1092. GetWindowRect(GetWindow(), &rcThis);
  1093. // Change to width and height
  1094. rcThis.right -= rcThis.left;
  1095. rcThis.bottom -= rcThis.top;
  1096. RECT rcDesktop;
  1097. SystemParametersInfo(SPI_GETWORKAREA, 0, &rcDesktop, 0);
  1098. if ((x+rcThis.right < rcDesktop.left+MinVis) || (x > rcDesktop.right-MinVis))
  1099. {
  1100. x = (rcDesktop.left + rcDesktop.right - rcThis.right) / 2;
  1101. }
  1102. if ((y+rcThis.bottom < rcDesktop.top+MinVis) || (y > rcDesktop.bottom-MinVis))
  1103. {
  1104. y = (rcDesktop.top + rcDesktop.bottom - rcThis.bottom) / 2;
  1105. }
  1106. SetWindowPos(GetWindow(), NULL, x, y, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
  1107. }
  1108. BOOL IsWindowActive(HWND hwnd)
  1109. {
  1110. HWND hwndFocus = GetFocus();
  1111. while (NULL != hwndFocus)
  1112. {
  1113. if (hwndFocus == hwnd)
  1114. {
  1115. return(TRUE);
  1116. }
  1117. HWND hwndParent = GetParent(hwndFocus);
  1118. if (NULL == hwndParent)
  1119. {
  1120. hwndFocus = GetWindow(hwndFocus, GW_OWNER);
  1121. }
  1122. else
  1123. {
  1124. hwndFocus = hwndParent;
  1125. }
  1126. }
  1127. return(FALSE);
  1128. }
  1129. static BOOL ShouldTry(HWND child)
  1130. {
  1131. return((WS_VISIBLE) == (GetWindowStyle(child) & (WS_DISABLED|WS_VISIBLE)));
  1132. }
  1133. static BOOL IsTabbable(HWND child)
  1134. {
  1135. return((WS_TABSTOP|WS_VISIBLE) == (GetWindowStyle(child) & (WS_TABSTOP|WS_DISABLED|WS_VISIBLE)));
  1136. }
  1137. HWND NextControl(HWND hwndTop, HWND hwndFocus)
  1138. {
  1139. // Loop detection stuff
  1140. BOOL bGotToTop = FALSE;
  1141. // We'll loop to avoid really deep recursion
  1142. while (TRUE)
  1143. {
  1144. // First try the children of hwndFocus
  1145. if (hwndFocus == hwndTop || ShouldTry(hwndFocus))
  1146. {
  1147. HWND next = GetFirstChild(hwndFocus);
  1148. if (NULL != next)
  1149. {
  1150. if (IsTabbable(next))
  1151. {
  1152. return(next);
  1153. }
  1154. hwndFocus = next;
  1155. continue;
  1156. }
  1157. }
  1158. if (hwndFocus == hwndTop)
  1159. {
  1160. // Apparently hwndTop has no children
  1161. return(NULL);
  1162. }
  1163. HWND next;
  1164. while (NULL == (next = GetNextSibling(hwndFocus)))
  1165. {
  1166. hwndFocus = GetParent(hwndFocus);
  1167. if (NULL == hwndFocus)
  1168. {
  1169. // Invalid params
  1170. return(NULL);
  1171. }
  1172. if (hwndTop == hwndFocus)
  1173. {
  1174. break;
  1175. }
  1176. }
  1177. if (hwndTop == hwndFocus)
  1178. {
  1179. // Detect if we have looped back to the top again
  1180. if (bGotToTop)
  1181. {
  1182. return(NULL);
  1183. }
  1184. bGotToTop = TRUE;
  1185. continue;
  1186. }
  1187. if (IsTabbable(next))
  1188. {
  1189. return(next);
  1190. }
  1191. hwndFocus = next;
  1192. }
  1193. // We looped back to the beginning, so I guess nobody can take the focus
  1194. return(NULL);
  1195. }
  1196. // Determine the previous control in the tab order
  1197. HWND PrevControl(HWND hwndTop, HWND hwndFocus)
  1198. {
  1199. // In case hwndFocus is not focusable for some reason, we still need to
  1200. // detect the loop
  1201. HWND hwndStart = NextControl(hwndTop, hwndFocus);
  1202. // HACK for combo boxes: go from the edit control to the combo box control
  1203. while (NULL != hwndFocus
  1204. && hwndTop != hwndFocus
  1205. && !IsTabbable(hwndFocus)
  1206. )
  1207. {
  1208. hwndFocus = GetParent(hwndFocus);
  1209. }
  1210. HWND ret = hwndStart;
  1211. while (TRUE)
  1212. {
  1213. HWND next = NextControl(hwndTop, ret);
  1214. if (NULL == next)
  1215. {
  1216. // Oops!
  1217. return(NULL);
  1218. }
  1219. if (hwndFocus == next
  1220. || hwndStart == next
  1221. )
  1222. {
  1223. break;
  1224. }
  1225. ret = next;
  1226. }
  1227. return(ret);
  1228. }
  1229. void ShiftFocus(HWND hwndTop, BOOL bForward)
  1230. {
  1231. HWND hwndFocus = GetFocus();
  1232. if (!IsWindowActive(hwndTop))
  1233. {
  1234. hwndFocus = hwndTop;
  1235. }
  1236. HWND next = bForward ? NextControl(hwndTop, hwndFocus) : PrevControl(hwndTop, hwndFocus);
  1237. if (NULL != next)
  1238. {
  1239. SetFocus(next);
  1240. }
  1241. else
  1242. {
  1243. MessageBeep(MB_ICONHAND);
  1244. }
  1245. }