comparison foosdk/wtl/Include/atlframe.h @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
comparison
equal deleted inserted replaced
0:e9bb126753e7 1:20d02a178406
1 // Windows Template Library - WTL version 10.0
2 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.
3 //
4 // This file is a part of the Windows Template Library.
5 // The use and distribution terms for this software are covered by the
6 // Microsoft Public License (http://opensource.org/licenses/MS-PL)
7 // which can be found in the file MS-PL.txt at the root folder.
8
9 #ifndef __ATLFRAME_H__
10 #define __ATLFRAME_H__
11
12 #pragma once
13
14 #ifndef __ATLAPP_H__
15 #error atlframe.h requires atlapp.h to be included first
16 #endif
17
18 #ifndef __ATLWIN_H__
19 #error atlframe.h requires atlwin.h to be included first
20 #endif
21
22
23 ///////////////////////////////////////////////////////////////////////////////
24 // Classes in this file:
25 //
26 // CFrameWindowImpl<T, TBase, TWinTraits>
27 // CMDIWindow
28 // CMDIFrameWindowImpl<T, TBase, TWinTraits>
29 // CMDIChildWindowImpl<T, TBase, TWinTraits>
30 // COwnerDraw<T>
31 // CUpdateUIBase
32 // CUpdateUI<T>
33 // CDynamicUpdateUI<T>
34 // CAutoUpdateUI<T>
35 // CDialogResize<T>
36 // CDynamicDialogLayout<T>
37 // CDoubleBufferImpl<T>
38 // CDoubleBufferWindowImpl<T, TBase, TWinTraits>
39 //
40 // Global functions:
41 // AtlCreateSimpleToolBar()
42
43
44 namespace WTL
45 {
46
47 ///////////////////////////////////////////////////////////////////////////////
48 // CFrameWndClassInfo - Manages frame window Windows class information
49
50 class CFrameWndClassInfo
51 {
52 public:
53 enum { cchAutoName = 5 + sizeof(void*) * 2 }; // sizeof(void*) * 2 is the number of digits %p outputs
54 WNDCLASSEX m_wc;
55 LPCTSTR m_lpszOrigName;
56 WNDPROC pWndProc;
57 LPCTSTR m_lpszCursorID;
58 BOOL m_bSystemCursor;
59 ATOM m_atom;
60 TCHAR m_szAutoName[cchAutoName];
61 UINT m_uCommonResourceID;
62
63 ATOM Register(WNDPROC* pProc)
64 {
65 if (m_atom == 0)
66 {
67 CWindowCreateCriticalSectionLock lock;
68 if(FAILED(lock.Lock()))
69 {
70 ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CFrameWndClassInfo::Register.\n"));
71 ATLASSERT(FALSE);
72 return 0;
73 }
74
75 if(m_atom == 0)
76 {
77 HINSTANCE hInst = ModuleHelper::GetModuleInstance();
78
79 if (m_lpszOrigName != NULL)
80 {
81 ATLASSERT(pProc != NULL);
82 LPCTSTR lpsz = m_wc.lpszClassName;
83 WNDPROC proc = m_wc.lpfnWndProc;
84
85 WNDCLASSEX wc = { sizeof(WNDCLASSEX) };
86 // try process local class first
87 if(!::GetClassInfoEx(ModuleHelper::GetModuleInstance(), m_lpszOrigName, &wc))
88 {
89 // try global class
90 if(!::GetClassInfoEx(NULL, m_lpszOrigName, &wc))
91 {
92 lock.Unlock();
93 return 0;
94 }
95 }
96 m_wc = wc;
97 pWndProc = m_wc.lpfnWndProc;
98 m_wc.lpszClassName = lpsz;
99 m_wc.lpfnWndProc = proc;
100 }
101 else
102 {
103 m_wc.hCursor = ::LoadCursor(m_bSystemCursor ? NULL : hInst, m_lpszCursorID);
104 }
105
106 m_wc.hInstance = hInst;
107 m_wc.style &= ~CS_GLOBALCLASS; // we don't register global classes
108 if (m_wc.lpszClassName == NULL)
109 {
110 _stprintf_s(m_szAutoName, cchAutoName, _T("ATL:%p"), &m_wc);
111 m_wc.lpszClassName = m_szAutoName;
112 }
113
114 WNDCLASSEX wcTemp = m_wc;
115 m_atom = (ATOM)::GetClassInfoEx(m_wc.hInstance, m_wc.lpszClassName, &wcTemp);
116 if (m_atom == 0)
117 {
118 if(m_uCommonResourceID != 0) // use it if not zero
119 {
120 m_wc.hIcon = (HICON)::LoadImage(ModuleHelper::GetResourceInstance(),
121 MAKEINTRESOURCE(m_uCommonResourceID), IMAGE_ICON,
122 ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR);
123 m_wc.hIconSm = (HICON)::LoadImage(ModuleHelper::GetResourceInstance(),
124 MAKEINTRESOURCE(m_uCommonResourceID), IMAGE_ICON,
125 ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
126 }
127 m_atom = ::RegisterClassEx(&m_wc);
128 }
129 }
130
131 lock.Unlock();
132 }
133
134 if (m_lpszOrigName != NULL)
135 {
136 ATLASSERT(pProc != NULL);
137 ATLASSERT(pWndProc != NULL);
138 *pProc = pWndProc;
139 }
140
141 return m_atom;
142 }
143 };
144
145
146 ///////////////////////////////////////////////////////////////////////////////
147 // Macros for declaring frame window WNDCLASS
148
149 #define DECLARE_FRAME_WND_CLASS(WndClassName, uCommonResourceID) \
150 static WTL::CFrameWndClassInfo& GetWndClassInfo() \
151 { \
152 static WTL::CFrameWndClassInfo wc = \
153 { \
154 { sizeof(WNDCLASSEX), 0, StartWindowProc, \
155 0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName, NULL }, \
156 NULL, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID \
157 }; \
158 return wc; \
159 }
160
161 #define DECLARE_FRAME_WND_CLASS_EX(WndClassName, uCommonResourceID, style, bkgnd) \
162 static WTL::CFrameWndClassInfo& GetWndClassInfo() \
163 { \
164 static WTL::CFrameWndClassInfo wc = \
165 { \
166 { sizeof(WNDCLASSEX), style, StartWindowProc, \
167 0, 0, NULL, NULL, NULL, (HBRUSH)(bkgnd + 1), NULL, WndClassName, NULL }, \
168 NULL, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID \
169 }; \
170 return wc; \
171 }
172
173 #define DECLARE_FRAME_WND_SUPERCLASS(WndClassName, OrigWndClassName, uCommonResourceID) \
174 static WTL::CFrameWndClassInfo& GetWndClassInfo() \
175 { \
176 static WTL::CFrameWndClassInfo wc = \
177 { \
178 { sizeof(WNDCLASSEX), 0, StartWindowProc, \
179 0, 0, NULL, NULL, NULL, NULL, NULL, WndClassName, NULL }, \
180 OrigWndClassName, NULL, NULL, TRUE, 0, _T(""), uCommonResourceID \
181 }; \
182 return wc; \
183 }
184
185 // These are for templated classes
186 #define DECLARE_FRAME_WND_CLASS2(WndClassName, EnclosingClass, uCommonResourceID) \
187 static WTL::CFrameWndClassInfo& GetWndClassInfo() \
188 { \
189 static WTL::CFrameWndClassInfo wc = \
190 { \
191 { sizeof(WNDCLASSEX), 0, EnclosingClass::StartWindowProc, \
192 0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName, NULL }, \
193 NULL, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID \
194 }; \
195 return wc; \
196 }
197
198 #define DECLARE_FRAME_WND_CLASS_EX2(WndClassName, EnclosingClass, uCommonResourceID, style, bkgnd) \
199 static WTL::CFrameWndClassInfo& GetWndClassInfo() \
200 { \
201 static WTL::CFrameWndClassInfo wc = \
202 { \
203 { sizeof(WNDCLASSEX), style, EnclosingClass::StartWindowProc, \
204 0, 0, NULL, NULL, NULL, (HBRUSH)(bkgnd + 1), NULL, WndClassName, NULL }, \
205 NULL, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID \
206 }; \
207 return wc; \
208 }
209
210 #define DECLARE_FRAME_WND_SUPERCLASS2(WndClassName, EnclosingClass, OrigWndClassName, uCommonResourceID) \
211 static WTL::CFrameWndClassInfo& GetWndClassInfo() \
212 { \
213 static WTL::CFrameWndClassInfo wc = \
214 { \
215 { sizeof(WNDCLASSEX), 0, EnclosingClass::StartWindowProc, \
216 0, 0, NULL, NULL, NULL, NULL, NULL, WndClassName, NULL }, \
217 OrigWndClassName, NULL, NULL, TRUE, 0, _T(""), uCommonResourceID \
218 }; \
219 return wc; \
220 }
221
222
223 ///////////////////////////////////////////////////////////////////////////////
224 // CFrameWindowImpl
225
226 // Client window command chaining macro (only for frame windows)
227 #define CHAIN_CLIENT_COMMANDS() \
228 if((uMsg == WM_COMMAND) && (this->m_hWndClient != NULL)) \
229 ::SendMessage(this->m_hWndClient, uMsg, wParam, lParam);
230
231 // standard toolbar styles
232 #define ATL_SIMPLE_TOOLBAR_STYLE \
233 (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS)
234 // toolbar in a rebar pane
235 #define ATL_SIMPLE_TOOLBAR_PANE_STYLE \
236 (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT)
237 // standard rebar styles
238 #define ATL_SIMPLE_REBAR_STYLE \
239 (WS_CHILD | WS_VISIBLE | WS_BORDER | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | RBS_VARHEIGHT | RBS_BANDBORDERS | RBS_AUTOSIZE)
240 // rebar without borders
241 #define ATL_SIMPLE_REBAR_NOBORDER_STYLE \
242 (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | RBS_VARHEIGHT | RBS_BANDBORDERS | RBS_AUTOSIZE | CCS_NODIVIDER)
243
244 // command bar support
245 #if !defined(__ATLCTRLW_H__)
246
247 #define CBRM_GETCMDBAR (WM_USER + 301) // returns command bar HWND
248 #define CBRM_GETMENU (WM_USER + 302) // returns loaded or attached menu
249 #define CBRM_TRACKPOPUPMENU (WM_USER + 303) // displays a popup menu
250
251 struct _AtlFrameWnd_CmdBarPopupMenu
252 {
253 int cbSize;
254 HMENU hMenu;
255 UINT uFlags;
256 int x;
257 int y;
258 LPTPMPARAMS lptpm;
259 };
260
261 #define CBRPOPUPMENU _AtlFrameWnd_CmdBarPopupMenu
262
263 #endif // !defined(__ATLCTRLW_H__)
264
265
266 template <class TBase = ATL::CWindow, class TWinTraits = ATL::CFrameWinTraits>
267 class ATL_NO_VTABLE CFrameWindowImplBase : public ATL::CWindowImplBaseT< TBase, TWinTraits >
268 {
269 public:
270 typedef CFrameWindowImplBase<TBase, TWinTraits > _thisClass;
271 DECLARE_FRAME_WND_CLASS2(NULL, _thisClass, 0)
272
273 struct _ChevronMenuInfo
274 {
275 HMENU hMenu;
276 LPNMREBARCHEVRON lpnm;
277 bool bCmdBar;
278 };
279
280 // Data members
281 HWND m_hWndToolBar;
282 HWND m_hWndStatusBar;
283 HWND m_hWndClient;
284
285 HACCEL m_hAccel;
286
287 // Constructor
288 CFrameWindowImplBase() :
289 m_hWndToolBar(NULL),
290 m_hWndStatusBar(NULL),
291 m_hWndClient(NULL),
292 m_hAccel(NULL)
293 { }
294
295 // Methods
296 HWND Create(HWND hWndParent, ATL::_U_RECT rect, LPCTSTR szWindowName, DWORD dwStyle, DWORD dwExStyle, ATL::_U_MENUorID MenuOrID, ATOM atom, LPVOID lpCreateParam)
297 {
298 ATLASSERT(this->m_hWnd == NULL);
299
300 // Allocate the thunk structure here, where we can fail gracefully.
301 BOOL bRet = this->m_thunk.Init(NULL, NULL);
302 if(bRet == FALSE)
303 {
304 ::SetLastError(ERROR_OUTOFMEMORY);
305 return NULL;
306 }
307
308 if(atom == 0)
309 return NULL;
310
311 ModuleHelper::AddCreateWndData(&this->m_thunk.cd, this);
312
313 if((MenuOrID.m_hMenu == NULL) && (dwStyle & WS_CHILD))
314 MenuOrID.m_hMenu = (HMENU)(UINT_PTR)this;
315 if(rect.m_lpRect == NULL)
316 rect.m_lpRect = &TBase::rcDefault;
317
318 HWND hWnd = ::CreateWindowEx(dwExStyle, MAKEINTATOM(atom), szWindowName,
319 dwStyle, rect.m_lpRect->left, rect.m_lpRect->top, rect.m_lpRect->right - rect.m_lpRect->left,
320 rect.m_lpRect->bottom - rect.m_lpRect->top, hWndParent, MenuOrID.m_hMenu,
321 ModuleHelper::GetModuleInstance(), lpCreateParam);
322
323 ATLASSERT((hWnd == NULL) || (this->m_hWnd == hWnd));
324
325 return hWnd;
326 }
327
328 static HWND CreateSimpleToolBarCtrl(HWND hWndParent, UINT nResourceID, BOOL bInitialSeparator = FALSE,
329 DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
330 {
331 HINSTANCE hInst = ModuleHelper::GetResourceInstance();
332 HRSRC hRsrc = ::FindResource(hInst, MAKEINTRESOURCE(nResourceID), RT_TOOLBAR);
333 if (hRsrc == NULL)
334 return NULL;
335
336 HGLOBAL hGlobal = ::LoadResource(hInst, hRsrc);
337 if (hGlobal == NULL)
338 return NULL;
339
340 _AtlToolBarData* pData = (_AtlToolBarData*)::LockResource(hGlobal);
341 if (pData == NULL)
342 return NULL;
343 ATLASSERT(pData->wVersion == 1);
344
345 WORD* pItems = pData->items();
346 int nItems = pData->wItemCount + (bInitialSeparator ? 1 : 0);
347 ATL::CTempBuffer<TBBUTTON, _WTL_STACK_ALLOC_THRESHOLD> buff;
348 TBBUTTON* pTBBtn = buff.Allocate(nItems);
349 ATLASSERT(pTBBtn != NULL);
350 if(pTBBtn == NULL)
351 return NULL;
352
353 const int cxSeparator = 8;
354
355 // set initial separator (half width)
356 if(bInitialSeparator)
357 {
358 pTBBtn[0].iBitmap = cxSeparator / 2;
359 pTBBtn[0].idCommand = 0;
360 pTBBtn[0].fsState = 0;
361 pTBBtn[0].fsStyle = BTNS_SEP;
362 pTBBtn[0].dwData = 0;
363 pTBBtn[0].iString = 0;
364 }
365
366 int nBmp = 0;
367 for(int i = 0, j = bInitialSeparator ? 1 : 0; i < pData->wItemCount; i++, j++)
368 {
369 if(pItems[i] != 0)
370 {
371 pTBBtn[j].iBitmap = nBmp++;
372 pTBBtn[j].idCommand = pItems[i];
373 pTBBtn[j].fsState = TBSTATE_ENABLED;
374 pTBBtn[j].fsStyle = BTNS_BUTTON;
375 pTBBtn[j].dwData = 0;
376 pTBBtn[j].iString = 0;
377 }
378 else
379 {
380 pTBBtn[j].iBitmap = cxSeparator;
381 pTBBtn[j].idCommand = 0;
382 pTBBtn[j].fsState = 0;
383 pTBBtn[j].fsStyle = BTNS_SEP;
384 pTBBtn[j].dwData = 0;
385 pTBBtn[j].iString = 0;
386 }
387 }
388
389 HWND hWnd = ::CreateWindowEx(0, TOOLBARCLASSNAME, NULL, dwStyle, 0, 0, 100, 100, hWndParent, (HMENU)LongToHandle(nID), ModuleHelper::GetModuleInstance(), NULL);
390 if(hWnd == NULL)
391 {
392 ATLASSERT(FALSE);
393 return NULL;
394 }
395
396 ::SendMessage(hWnd, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0L);
397
398 // check if font is taller than our bitmaps
399 CFontHandle font = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0L);
400 if(font.IsNull())
401 font = (HFONT)::GetStockObject(SYSTEM_FONT);
402 LOGFONT lf = {};
403 font.GetLogFont(lf);
404 WORD cyFontHeight = (WORD)abs(lf.lfHeight);
405
406 WORD bitsPerPixel = AtlGetBitmapResourceBitsPerPixel(nResourceID);
407 if(bitsPerPixel > 4)
408 {
409 COLORREF crMask = CLR_DEFAULT;
410 if(bitsPerPixel == 32)
411 {
412 // 32-bit color bitmap with alpha channel (valid for Windows XP and later)
413 crMask = CLR_NONE;
414 }
415 HIMAGELIST hImageList = ImageList_LoadImage(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(nResourceID), pData->wWidth, 1, crMask, IMAGE_BITMAP, LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
416 ATLASSERT(hImageList != NULL);
417 ::SendMessage(hWnd, TB_SETIMAGELIST, 0, (LPARAM)hImageList);
418 }
419 else
420 {
421 TBADDBITMAP tbab = {};
422 tbab.hInst = hInst;
423 tbab.nID = nResourceID;
424 ::SendMessage(hWnd, TB_ADDBITMAP, nBmp, (LPARAM)&tbab);
425 }
426
427 ::SendMessage(hWnd, TB_ADDBUTTONS, nItems, (LPARAM)pTBBtn);
428 ::SendMessage(hWnd, TB_SETBITMAPSIZE, 0, MAKELONG(pData->wWidth, __max(pData->wHeight, cyFontHeight)));
429 const int cxyButtonMargin = 7;
430 ::SendMessage(hWnd, TB_SETBUTTONSIZE, 0, MAKELONG(pData->wWidth + cxyButtonMargin, __max(pData->wHeight, cyFontHeight) + cxyButtonMargin));
431
432 return hWnd;
433 }
434
435 static HWND CreateSimpleReBarCtrl(HWND hWndParent, DWORD dwStyle = ATL_SIMPLE_REBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
436 {
437 // Ensure style combinations for proper rebar painting
438 if(dwStyle & CCS_NODIVIDER && (dwStyle & WS_BORDER))
439 dwStyle &= ~WS_BORDER;
440 else if(!(dwStyle & WS_BORDER) && !(dwStyle & CCS_NODIVIDER))
441 dwStyle |= CCS_NODIVIDER;
442
443 // Create rebar window
444 HWND hWndReBar = ::CreateWindowEx(0, REBARCLASSNAME, NULL, dwStyle, 0, 0, 100, 100, hWndParent, (HMENU)LongToHandle(nID), ModuleHelper::GetModuleInstance(), NULL);
445 if(hWndReBar == NULL)
446 {
447 ATLTRACE2(atlTraceUI, 0, _T("Failed to create rebar.\n"));
448 return NULL;
449 }
450
451 // Initialize and send the REBARINFO structure
452 REBARINFO rbi = { sizeof(REBARINFO), 0 };
453 if(::SendMessage(hWndReBar, RB_SETBARINFO, 0, (LPARAM)&rbi) == 0)
454 {
455 ATLTRACE2(atlTraceUI, 0, _T("Failed to initialize rebar.\n"));
456 ::DestroyWindow(hWndReBar);
457 return NULL;
458 }
459
460 return hWndReBar;
461 }
462
463 BOOL CreateSimpleReBar(DWORD dwStyle = ATL_SIMPLE_REBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
464 {
465 ATLASSERT(!::IsWindow(m_hWndToolBar));
466 m_hWndToolBar = CreateSimpleReBarCtrl(this->m_hWnd, dwStyle, nID);
467 return (m_hWndToolBar != NULL);
468 }
469
470 static BOOL AddSimpleReBarBandCtrl(HWND hWndReBar, HWND hWndBand, int nID = 0, LPCTSTR lpstrTitle = NULL, BOOL bNewRow = FALSE, int cxWidth = 0, BOOL bFullWidthAlways = FALSE)
471 {
472 ATLASSERT(::IsWindow(hWndReBar)); // must be already created
473 #ifdef _DEBUG
474 // block - check if this is really a rebar
475 {
476 TCHAR lpszClassName[sizeof(REBARCLASSNAME)] = {};
477 ::GetClassName(hWndReBar, lpszClassName, sizeof(REBARCLASSNAME));
478 ATLASSERT(lstrcmp(lpszClassName, REBARCLASSNAME) == 0);
479 }
480 #endif // _DEBUG
481 ATLASSERT(::IsWindow(hWndBand)); // must be already created
482
483 // Get number of buttons on the toolbar
484 int nBtnCount = (int)::SendMessage(hWndBand, TB_BUTTONCOUNT, 0, 0L);
485
486 // Set band info structure
487 REBARBANDINFO rbBand = { RunTimeHelper::SizeOf_REBARBANDINFO() };
488 rbBand.fMask = RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_ID | RBBIM_SIZE | RBBIM_IDEALSIZE;
489 if(lpstrTitle != NULL)
490 rbBand.fMask |= RBBIM_TEXT;
491 rbBand.fStyle = RBBS_CHILDEDGE;
492 if(nBtnCount > 0) // add chevron style for toolbar with buttons
493 rbBand.fStyle |= RBBS_USECHEVRON;
494 if(bNewRow)
495 rbBand.fStyle |= RBBS_BREAK;
496
497 rbBand.lpText = (LPTSTR)lpstrTitle;
498 rbBand.hwndChild = hWndBand;
499 if(nID == 0) // calc band ID
500 nID = ATL_IDW_BAND_FIRST + (int)::SendMessage(hWndReBar, RB_GETBANDCOUNT, 0, 0L);
501 rbBand.wID = nID;
502
503 // Calculate the size of the band
504 BOOL bRet = FALSE;
505 RECT rcTmp = {};
506 if(nBtnCount > 0)
507 {
508 bRet = (BOOL)::SendMessage(hWndBand, TB_GETITEMRECT, nBtnCount - 1, (LPARAM)&rcTmp);
509 ATLASSERT(bRet);
510 rbBand.cx = (cxWidth != 0) ? cxWidth : rcTmp.right;
511 rbBand.cyMinChild = rcTmp.bottom - rcTmp.top;
512 if(bFullWidthAlways)
513 {
514 rbBand.cxMinChild = rbBand.cx;
515 }
516 else if(lpstrTitle == NULL)
517 {
518 bRet = (BOOL)::SendMessage(hWndBand, TB_GETITEMRECT, 0, (LPARAM)&rcTmp);
519 ATLASSERT(bRet);
520 rbBand.cxMinChild = rcTmp.right;
521 }
522 else
523 {
524 rbBand.cxMinChild = 0;
525 }
526 }
527 else // no buttons, either not a toolbar or really has no buttons
528 {
529 bRet = ::GetWindowRect(hWndBand, &rcTmp);
530 ATLASSERT(bRet);
531 rbBand.cx = (cxWidth != 0) ? cxWidth : (rcTmp.right - rcTmp.left);
532 rbBand.cxMinChild = bFullWidthAlways ? rbBand.cx : 0;
533 rbBand.cyMinChild = rcTmp.bottom - rcTmp.top;
534 }
535
536 rbBand.cxIdeal = rbBand.cx;
537
538 // Add the band
539 LRESULT lRes = ::SendMessage(hWndReBar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand);
540 if(lRes == 0)
541 {
542 ATLTRACE2(atlTraceUI, 0, _T("Failed to add a band to the rebar.\n"));
543 return FALSE;
544 }
545
546 DWORD dwExStyle = (DWORD)::SendMessage(hWndBand, TB_GETEXTENDEDSTYLE, 0, 0L);
547 ::SendMessage(hWndBand, TB_SETEXTENDEDSTYLE, 0, dwExStyle | TBSTYLE_EX_HIDECLIPPEDBUTTONS);
548
549 return TRUE;
550 }
551
552 BOOL AddSimpleReBarBand(HWND hWndBand, LPCTSTR lpstrTitle = NULL, BOOL bNewRow = FALSE, int cxWidth = 0, BOOL bFullWidthAlways = FALSE)
553 {
554 ATLASSERT(::IsWindow(m_hWndToolBar)); // must be an existing rebar
555 ATLASSERT(::IsWindow(hWndBand)); // must be created
556 return AddSimpleReBarBandCtrl(m_hWndToolBar, hWndBand, 0, lpstrTitle, bNewRow, cxWidth, bFullWidthAlways);
557 }
558
559 void SizeSimpleReBarBands()
560 {
561 ATLASSERT(::IsWindow(m_hWndToolBar)); // must be an existing rebar
562
563 int nCount = (int)::SendMessage(m_hWndToolBar, RB_GETBANDCOUNT, 0, 0L);
564
565 for(int i = 0; i < nCount; i++)
566 {
567 REBARBANDINFO rbBand = { RunTimeHelper::SizeOf_REBARBANDINFO() };
568 rbBand.fMask = RBBIM_SIZE;
569 BOOL bRet = (BOOL)::SendMessage(m_hWndToolBar, RB_GETBANDINFO, i, (LPARAM)&rbBand);
570 ATLASSERT(bRet);
571 RECT rect = {};
572 ::SendMessage(m_hWndToolBar, RB_GETBANDBORDERS, i, (LPARAM)&rect);
573 rbBand.cx += rect.left + rect.right;
574 bRet = (BOOL)::SendMessage(m_hWndToolBar, RB_SETBANDINFO, i, (LPARAM)&rbBand);
575 ATLASSERT(bRet);
576 }
577 }
578
579 BOOL CreateSimpleStatusBar(LPCTSTR lpstrText, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR)
580 {
581 ATLASSERT(!::IsWindow(m_hWndStatusBar));
582 m_hWndStatusBar = ::CreateStatusWindow(dwStyle, lpstrText, this->m_hWnd, nID);
583 return (m_hWndStatusBar != NULL);
584 }
585
586 BOOL CreateSimpleStatusBar(UINT nTextID = ATL_IDS_IDLEMESSAGE, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR)
587 {
588 const int cchMax = 128; // max text length is 127 for status bars (+1 for null)
589 TCHAR szText[cchMax] = {};
590 ::LoadString(ModuleHelper::GetResourceInstance(), nTextID, szText, cchMax);
591 return CreateSimpleStatusBar(szText, dwStyle, nID);
592 }
593
594 void UpdateLayout(BOOL bResizeBars = TRUE)
595 {
596 RECT rect = {};
597 this->GetClientRect(&rect);
598
599 // position bars and offset their dimensions
600 UpdateBarsPosition(rect, bResizeBars);
601
602 // resize client window
603 if(m_hWndClient != NULL)
604 ::SetWindowPos(m_hWndClient, NULL, rect.left, rect.top,
605 rect.right - rect.left, rect.bottom - rect.top,
606 SWP_NOZORDER | SWP_NOACTIVATE);
607 }
608
609 void UpdateBarsPosition(RECT& rect, BOOL bResizeBars = TRUE)
610 {
611 // resize toolbar
612 if((m_hWndToolBar != NULL) && ((DWORD)::GetWindowLong(m_hWndToolBar, GWL_STYLE) & WS_VISIBLE))
613 {
614 if(bResizeBars != FALSE)
615 {
616 ::SendMessage(m_hWndToolBar, WM_SIZE, 0, 0);
617 ::InvalidateRect(m_hWndToolBar, NULL, TRUE);
618 }
619 RECT rectTB = {};
620 ::GetWindowRect(m_hWndToolBar, &rectTB);
621 rect.top += rectTB.bottom - rectTB.top;
622 }
623
624 // resize status bar
625 if((m_hWndStatusBar != NULL) && ((DWORD)::GetWindowLong(m_hWndStatusBar, GWL_STYLE) & WS_VISIBLE))
626 {
627 if(bResizeBars != FALSE)
628 ::SendMessage(m_hWndStatusBar, WM_SIZE, 0, 0);
629 RECT rectSB = {};
630 ::GetWindowRect(m_hWndStatusBar, &rectSB);
631 rect.bottom -= rectSB.bottom - rectSB.top;
632 }
633 }
634
635 BOOL PreTranslateMessage(MSG* pMsg)
636 {
637 if((m_hAccel != NULL) && ::TranslateAccelerator(this->m_hWnd, m_hAccel, pMsg))
638 return TRUE;
639 return FALSE;
640 }
641
642 BEGIN_MSG_MAP(CFrameWindowImplBase)
643 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
644 MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
645 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
646 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
647 NOTIFY_CODE_HANDLER(TTN_GETDISPINFOA, OnToolTipTextA)
648 NOTIFY_CODE_HANDLER(TTN_GETDISPINFOW, OnToolTipTextW)
649 END_MSG_MAP()
650
651 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
652 {
653 if(m_hWndClient != NULL) // view will paint itself instead
654 return 1;
655
656 bHandled = FALSE;
657 return 0;
658 }
659
660 LRESULT OnMenuSelect(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
661 {
662 bHandled = FALSE;
663
664 if(m_hWndStatusBar == NULL)
665 return 1;
666
667 WORD wFlags = HIWORD(wParam);
668 if((wFlags == 0xFFFF) && (lParam == NULL)) // menu closing
669 {
670 ::SendMessage(m_hWndStatusBar, SB_SIMPLE, FALSE, 0L);
671 }
672 else
673 {
674 const int cchBuff = 256;
675 TCHAR szBuff[cchBuff] = {};
676 if(!(wFlags & MF_POPUP))
677 {
678 WORD wID = LOWORD(wParam);
679 // check for special cases
680 if((wID >= 0xF000) && (wID < 0xF1F0)) // system menu IDs
681 wID = (WORD)(((wID - 0xF000) >> 4) + ATL_IDS_SCFIRST);
682 else if((wID >= ID_FILE_MRU_FIRST) && (wID <= ID_FILE_MRU_LAST)) // MRU items
683 wID = ATL_IDS_MRU_FILE;
684 else if((wID >= ATL_IDM_FIRST_MDICHILD) && (wID <= ATL_IDM_LAST_MDICHILD)) // MDI child windows
685 wID = ATL_IDS_MDICHILD;
686
687 int nRet = ::LoadString(ModuleHelper::GetResourceInstance(), wID, szBuff, cchBuff);
688 for(int i = 0; i < nRet; i++)
689 {
690 if(szBuff[i] == _T('\n'))
691 {
692 szBuff[i] = 0;
693 break;
694 }
695 }
696 }
697 ::SendMessage(m_hWndStatusBar, SB_SIMPLE, TRUE, 0L);
698 ::SendMessage(m_hWndStatusBar, SB_SETTEXT, (255 | SBT_NOBORDERS), (LPARAM)szBuff);
699 }
700
701 return 1;
702 }
703
704 LRESULT OnSetFocus(UINT, WPARAM, LPARAM, BOOL& bHandled)
705 {
706 if(m_hWndClient != NULL)
707 ::SetFocus(m_hWndClient);
708
709 bHandled = FALSE;
710 return 1;
711 }
712
713 LRESULT OnDestroy(UINT, WPARAM, LPARAM, BOOL& bHandled)
714 {
715 if((this->GetStyle() & (WS_CHILD | WS_POPUP)) == 0)
716 ::PostQuitMessage(1);
717
718 bHandled = FALSE;
719 return 1;
720 }
721
722 LRESULT OnToolTipTextA(int idCtrl, LPNMHDR pnmh, BOOL& /*bHandled*/)
723 {
724 LPNMTTDISPINFOA pDispInfo = (LPNMTTDISPINFOA)pnmh;
725 if((idCtrl != 0) && !(pDispInfo->uFlags & TTF_IDISHWND))
726 {
727 const int cchBuff = 256;
728 char szBuff[cchBuff] = {};
729 int nRet = ::LoadStringA(ModuleHelper::GetResourceInstance(), idCtrl, szBuff, cchBuff);
730 for(int i = 0; i < nRet; i++)
731 {
732 if(szBuff[i] == '\n')
733 {
734 ATL::Checked::strncpy_s(pDispInfo->szText, _countof(pDispInfo->szText), &szBuff[i + 1], _TRUNCATE);
735 break;
736 }
737 }
738 if(nRet > 0) // string was loaded, save it
739 pDispInfo->uFlags |= TTF_DI_SETITEM;
740 }
741
742 return 0;
743 }
744
745 LRESULT OnToolTipTextW(int idCtrl, LPNMHDR pnmh, BOOL& /*bHandled*/)
746 {
747 LPNMTTDISPINFOW pDispInfo = (LPNMTTDISPINFOW)pnmh;
748 if((idCtrl != 0) && !(pDispInfo->uFlags & TTF_IDISHWND))
749 {
750 const int cchBuff = 256;
751 wchar_t szBuff[cchBuff] = {};
752 int nRet = ::LoadStringW(ModuleHelper::GetResourceInstance(), idCtrl, szBuff, cchBuff);
753 for(int i = 0; i < nRet; i++)
754 {
755 if(szBuff[i] == L'\n')
756 {
757 ATL::Checked::wcsncpy_s(pDispInfo->szText, _countof(pDispInfo->szText), &szBuff[i + 1], _TRUNCATE);
758 break;
759 }
760 }
761 if(nRet > 0) // string was loaded, save it
762 pDispInfo->uFlags |= TTF_DI_SETITEM;
763 }
764
765 return 0;
766 }
767
768 // Implementation - chevron menu support
769 bool PrepareChevronMenu(_ChevronMenuInfo& cmi)
770 {
771 // get rebar and toolbar
772 REBARBANDINFO rbbi = { RunTimeHelper::SizeOf_REBARBANDINFO() };
773 rbbi.fMask = RBBIM_CHILD;
774 BOOL bRet = (BOOL)::SendMessage(cmi.lpnm->hdr.hwndFrom, RB_GETBANDINFO, cmi.lpnm->uBand, (LPARAM)&rbbi);
775 ATLASSERT(bRet);
776
777 // assume the band is a toolbar
778 ATL::CWindow wnd = rbbi.hwndChild;
779 int nCount = (int)wnd.SendMessage(TB_BUTTONCOUNT);
780 if(nCount <= 0) // probably not a toolbar
781 return false;
782
783 // check if it's a command bar
784 CMenuHandle menuCmdBar = (HMENU)wnd.SendMessage(CBRM_GETMENU);
785 cmi.bCmdBar = (menuCmdBar.m_hMenu != NULL);
786
787 // build a menu from hidden items
788 CMenuHandle menu;
789 bRet = menu.CreatePopupMenu();
790 ATLASSERT(bRet);
791 RECT rcClient = {};
792 bRet = wnd.GetClientRect(&rcClient);
793 ATLASSERT(bRet);
794 for(int i = 0; i < nCount; i++)
795 {
796 TBBUTTON tbb = {};
797 bRet = (BOOL)wnd.SendMessage(TB_GETBUTTON, i, (LPARAM)&tbb);
798 ATLASSERT(bRet);
799 // skip hidden buttons
800 if((tbb.fsState & TBSTATE_HIDDEN) != 0)
801 continue;
802 RECT rcButton = {};
803 bRet = (BOOL)wnd.SendMessage(TB_GETITEMRECT, i, (LPARAM)&rcButton);
804 ATLASSERT(bRet);
805 bool bEnabled = ((tbb.fsState & TBSTATE_ENABLED) != 0);
806 if((rcButton.right > rcClient.right) || (rcButton.bottom > rcClient.bottom))
807 {
808 if(tbb.fsStyle & BTNS_SEP)
809 {
810 if(menu.GetMenuItemCount() > 0)
811 menu.AppendMenu(MF_SEPARATOR);
812 }
813 else if(cmi.bCmdBar)
814 {
815 const int cchBuff = 200;
816 TCHAR szBuff[cchBuff] = {};
817 CMenuItemInfo mii;
818 mii.fMask = MIIM_TYPE | MIIM_SUBMENU;
819 mii.dwTypeData = szBuff;
820 mii.cch = cchBuff;
821 bRet = menuCmdBar.GetMenuItemInfo(i, TRUE, &mii);
822 ATLASSERT(bRet);
823 // Note: CmdBar currently supports only drop-down items
824 ATLASSERT(::IsMenu(mii.hSubMenu));
825 bRet = menu.AppendMenu(MF_STRING | MF_POPUP | (bEnabled ? MF_ENABLED : MF_GRAYED), (UINT_PTR)mii.hSubMenu, mii.dwTypeData);
826 ATLASSERT(bRet);
827 }
828 else
829 {
830 // get button's text
831 const int cchBuff = 200;
832 TCHAR szBuff[cchBuff] = {};
833 LPCTSTR lpstrText = szBuff;
834 TBBUTTONINFO tbbi = {};
835 tbbi.cbSize = sizeof(TBBUTTONINFO);
836 tbbi.dwMask = TBIF_TEXT;
837 tbbi.pszText = szBuff;
838 tbbi.cchText = cchBuff;
839 if((wnd.SendMessage(TB_GETBUTTONINFO, tbb.idCommand, (LPARAM)&tbbi) == -1) || (szBuff[0] == 0))
840 {
841 // no text for this button, try a resource string
842 lpstrText = _T("");
843 int nRet = ::LoadString(ModuleHelper::GetResourceInstance(), tbb.idCommand, szBuff, cchBuff);
844 for(int n = 0; n < nRet; n++)
845 {
846 if(szBuff[n] == _T('\n'))
847 {
848 lpstrText = &szBuff[n + 1];
849 break;
850 }
851 }
852 }
853 bRet = menu.AppendMenu(MF_STRING | (bEnabled ? MF_ENABLED : MF_GRAYED), tbb.idCommand, lpstrText);
854 ATLASSERT(bRet);
855 }
856 }
857 }
858
859 if(menu.GetMenuItemCount() == 0) // no hidden buttons after all
860 {
861 menu.DestroyMenu();
862 ::MessageBeep((UINT)-1);
863 return false;
864 }
865
866 cmi.hMenu = menu;
867 return true;
868 }
869
870 void DisplayChevronMenu(_ChevronMenuInfo& cmi)
871 {
872 // convert chevron rect to screen coordinates
873 ATL::CWindow wndFrom = cmi.lpnm->hdr.hwndFrom;
874 POINT pt = { cmi.lpnm->rc.left, cmi.lpnm->rc.bottom };
875 wndFrom.MapWindowPoints(NULL, &pt, 1);
876 RECT rc = cmi.lpnm->rc;
877 wndFrom.MapWindowPoints(NULL, &rc);
878 // set up flags and rect
879 UINT uMenuFlags = TPM_LEFTBUTTON | TPM_VERTICAL | TPM_LEFTALIGN | TPM_TOPALIGN | TPM_VERPOSANIMATION;
880 TPMPARAMS TPMParams = {};
881 TPMParams.cbSize = sizeof(TPMPARAMS);
882 TPMParams.rcExclude = rc;
883 // check if this window has a command bar
884 HWND hWndCmdBar = (HWND)::SendMessage(this->m_hWnd, CBRM_GETCMDBAR, 0, 0L);
885 if(::IsWindow(hWndCmdBar))
886 {
887 CBRPOPUPMENU CBRPopupMenu = { sizeof(CBRPOPUPMENU), cmi.hMenu, uMenuFlags, pt.x, pt.y, &TPMParams };
888 ::SendMessage(hWndCmdBar, CBRM_TRACKPOPUPMENU, 0, (LPARAM)&CBRPopupMenu);
889 }
890 else
891 {
892 CMenuHandle menu = cmi.hMenu;
893 menu.TrackPopupMenuEx(uMenuFlags, pt.x, pt.y, this->m_hWnd, &TPMParams);
894 }
895 }
896
897 void CleanupChevronMenu(_ChevronMenuInfo& cmi)
898 {
899 CMenuHandle menu = cmi.hMenu;
900 // if menu is from a command bar, detach submenus so they are not destroyed
901 if(cmi.bCmdBar)
902 {
903 for(int i = menu.GetMenuItemCount() - 1; i >=0; i--)
904 menu.RemoveMenu(i, MF_BYPOSITION);
905 }
906 // destroy menu
907 menu.DestroyMenu();
908 // convert chevron rect to screen coordinates
909 ATL::CWindow wndFrom = cmi.lpnm->hdr.hwndFrom;
910 RECT rc = cmi.lpnm->rc;
911 wndFrom.MapWindowPoints(NULL, &rc);
912 // eat next message if click is on the same button
913 MSG msg = {};
914 if(::PeekMessage(&msg, this->m_hWnd, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_NOREMOVE) && ::PtInRect(&rc, msg.pt))
915 ::PeekMessage(&msg, this->m_hWnd, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE);
916 }
917 };
918
919
920 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CFrameWinTraits>
921 class ATL_NO_VTABLE CFrameWindowImpl : public CFrameWindowImplBase< TBase, TWinTraits >
922 {
923 public:
924 HWND Create(HWND hWndParent = NULL, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
925 DWORD dwStyle = 0, DWORD dwExStyle = 0,
926 HMENU hMenu = NULL, LPVOID lpCreateParam = NULL)
927 {
928 ATOM atom = T::GetWndClassInfo().Register(&this->m_pfnSuperWindowProc);
929
930 dwStyle = T::GetWndStyle(dwStyle);
931 dwExStyle = T::GetWndExStyle(dwExStyle);
932
933 if(rect.m_lpRect == NULL)
934 rect.m_lpRect = &TBase::rcDefault;
935
936 return CFrameWindowImplBase< TBase, TWinTraits >::Create(hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, hMenu, atom, lpCreateParam);
937 }
938
939 HWND CreateEx(HWND hWndParent = NULL, ATL::_U_RECT rect = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, LPVOID lpCreateParam = NULL)
940 {
941 const int cchName = 256;
942 TCHAR szWindowName[cchName] = {};
943 ::LoadString(ModuleHelper::GetResourceInstance(), T::GetWndClassInfo().m_uCommonResourceID, szWindowName, cchName);
944 HMENU hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID));
945
946 T* pT = static_cast<T*>(this);
947 HWND hWnd = pT->Create(hWndParent, rect, szWindowName, dwStyle, dwExStyle, hMenu, lpCreateParam);
948
949 if(hWnd != NULL)
950 this->m_hAccel = ::LoadAccelerators(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID));
951
952 return hWnd;
953 }
954
955 BOOL CreateSimpleToolBar(UINT nResourceID = 0, DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
956 {
957 if(nResourceID == 0)
958 nResourceID = T::GetWndClassInfo().m_uCommonResourceID;
959 ATLASSERT(!::IsWindow(this->m_hWndToolBar));
960 this->m_hWndToolBar = T::CreateSimpleToolBarCtrl(this->m_hWnd, nResourceID, TRUE, dwStyle, nID);
961 return (this->m_hWndToolBar != NULL);
962 }
963
964 // message map and handlers
965 typedef CFrameWindowImplBase< TBase, TWinTraits > _baseClass;
966
967 BEGIN_MSG_MAP(CFrameWindowImpl)
968 MESSAGE_HANDLER(WM_SIZE, OnSize)
969 #ifndef _ATL_NO_REBAR_SUPPORT
970 NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnReBarAutoSize)
971 NOTIFY_CODE_HANDLER(RBN_CHEVRONPUSHED, OnChevronPushed)
972 #endif // !_ATL_NO_REBAR_SUPPORT
973 CHAIN_MSG_MAP(_baseClass)
974 END_MSG_MAP()
975
976 LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
977 {
978 if(wParam != SIZE_MINIMIZED)
979 {
980 T* pT = static_cast<T*>(this);
981 pT->UpdateLayout();
982 }
983 bHandled = FALSE;
984 return 1;
985 }
986
987 #ifndef _ATL_NO_REBAR_SUPPORT
988 LRESULT OnReBarAutoSize(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
989 {
990 T* pT = static_cast<T*>(this);
991 pT->UpdateLayout(FALSE);
992 return 0;
993 }
994
995 LRESULT OnChevronPushed(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
996 {
997 T* pT = static_cast<T*>(this);
998 typename CFrameWindowImplBase< TBase, TWinTraits >::_ChevronMenuInfo cmi = { NULL, (LPNMREBARCHEVRON)pnmh, false };
999 if(!pT->PrepareChevronMenu(cmi))
1000 {
1001 bHandled = FALSE;
1002 return 1;
1003 }
1004 // display a popup menu with hidden items
1005 pT->DisplayChevronMenu(cmi);
1006 // cleanup
1007 pT->CleanupChevronMenu(cmi);
1008 return 0;
1009 }
1010 #endif // !_ATL_NO_REBAR_SUPPORT
1011 };
1012
1013
1014 ///////////////////////////////////////////////////////////////////////////////
1015 // AtlCreateSimpleToolBar - helper for creating simple toolbars
1016
1017 inline HWND AtlCreateSimpleToolBar(HWND hWndParent, UINT nResourceID, BOOL bInitialSeparator = FALSE,
1018 DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
1019 {
1020 return CFrameWindowImplBase<>::CreateSimpleToolBarCtrl(hWndParent, nResourceID, bInitialSeparator, dwStyle, nID);
1021 }
1022
1023
1024 ///////////////////////////////////////////////////////////////////////////////
1025 // CMDIWindow
1026
1027 #ifndef _WTL_MDIWINDOWMENU_TEXT
1028 #define _WTL_MDIWINDOWMENU_TEXT _T("&Window")
1029 #endif
1030
1031 class CMDIWindow : public ATL::CWindow
1032 {
1033 public:
1034 // Data members
1035 HWND m_hWndMDIClient;
1036 HMENU m_hMenu;
1037
1038 // Constructors
1039 CMDIWindow(HWND hWnd = NULL) : ATL::CWindow(hWnd), m_hWndMDIClient(NULL), m_hMenu(NULL)
1040 { }
1041
1042 CMDIWindow& operator =(HWND hWnd)
1043 {
1044 m_hWnd = hWnd;
1045 return *this;
1046 }
1047
1048 // Operations
1049 HWND MDIGetActive(BOOL* lpbMaximized = NULL)
1050 {
1051 ATLASSERT(::IsWindow(m_hWndMDIClient));
1052 return (HWND)::SendMessage(m_hWndMDIClient, WM_MDIGETACTIVE, 0, (LPARAM)lpbMaximized);
1053 }
1054
1055 void MDIActivate(HWND hWndChildToActivate)
1056 {
1057 ATLASSERT(::IsWindow(m_hWndMDIClient));
1058 ATLASSERT(::IsWindow(hWndChildToActivate));
1059 ::SendMessage(m_hWndMDIClient, WM_MDIACTIVATE, (WPARAM)hWndChildToActivate, 0);
1060 }
1061
1062 void MDINext(HWND hWndChild, BOOL bPrevious = FALSE)
1063 {
1064 ATLASSERT(::IsWindow(m_hWndMDIClient));
1065 ATLASSERT((hWndChild == NULL) || ::IsWindow(hWndChild));
1066 ::SendMessage(m_hWndMDIClient, WM_MDINEXT, (WPARAM)hWndChild, (LPARAM)bPrevious);
1067 }
1068
1069 void MDIMaximize(HWND hWndChildToMaximize)
1070 {
1071 ATLASSERT(::IsWindow(m_hWndMDIClient));
1072 ATLASSERT(::IsWindow(hWndChildToMaximize));
1073 ::SendMessage(m_hWndMDIClient, WM_MDIMAXIMIZE, (WPARAM)hWndChildToMaximize, 0);
1074 }
1075
1076 void MDIRestore(HWND hWndChildToRestore)
1077 {
1078 ATLASSERT(::IsWindow(m_hWndMDIClient));
1079 ATLASSERT(::IsWindow(hWndChildToRestore));
1080 ::SendMessage(m_hWndMDIClient, WM_MDIRESTORE, (WPARAM)hWndChildToRestore, 0);
1081 }
1082
1083 void MDIDestroy(HWND hWndChildToDestroy)
1084 {
1085 ATLASSERT(::IsWindow(m_hWndMDIClient));
1086 ATLASSERT(::IsWindow(hWndChildToDestroy));
1087 ::SendMessage(m_hWndMDIClient, WM_MDIDESTROY, (WPARAM)hWndChildToDestroy, 0);
1088 }
1089
1090 BOOL MDICascade(UINT uFlags = 0)
1091 {
1092 ATLASSERT(::IsWindow(m_hWndMDIClient));
1093 return (BOOL)::SendMessage(m_hWndMDIClient, WM_MDICASCADE, (WPARAM)uFlags, 0);
1094 }
1095
1096 BOOL MDITile(UINT uFlags = MDITILE_HORIZONTAL)
1097 {
1098 ATLASSERT(::IsWindow(m_hWndMDIClient));
1099 return (BOOL)::SendMessage(m_hWndMDIClient, WM_MDITILE, (WPARAM)uFlags, 0);
1100 }
1101
1102 void MDIIconArrange()
1103 {
1104 ATLASSERT(::IsWindow(m_hWndMDIClient));
1105 ::SendMessage(m_hWndMDIClient, WM_MDIICONARRANGE, 0, 0);
1106 }
1107
1108 HMENU MDISetMenu(HMENU hMenuFrame, HMENU hMenuWindow)
1109 {
1110 ATLASSERT(::IsWindow(m_hWndMDIClient));
1111 return (HMENU)::SendMessage(m_hWndMDIClient, WM_MDISETMENU, (WPARAM)hMenuFrame, (LPARAM)hMenuWindow);
1112 }
1113
1114 HMENU MDIRefreshMenu()
1115 {
1116 ATLASSERT(::IsWindow(m_hWndMDIClient));
1117 return (HMENU)::SendMessage(m_hWndMDIClient, WM_MDIREFRESHMENU, 0, 0);
1118 }
1119
1120 // Additional operations
1121 static HMENU GetStandardWindowMenu(HMENU hMenu)
1122 {
1123 int nCount = ::GetMenuItemCount(hMenu);
1124 if(nCount == -1)
1125 return NULL;
1126 int nLen = ::GetMenuString(hMenu, nCount - 2, NULL, 0, MF_BYPOSITION);
1127 if(nLen == 0)
1128 return NULL;
1129 ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
1130 LPTSTR lpszText = buff.Allocate(nLen + 1);
1131 if(lpszText == NULL)
1132 return NULL;
1133 if(::GetMenuString(hMenu, nCount - 2, lpszText, nLen + 1, MF_BYPOSITION) != nLen)
1134 return NULL;
1135 if(lstrcmp(lpszText, _WTL_MDIWINDOWMENU_TEXT) != 0)
1136 return NULL;
1137 return ::GetSubMenu(hMenu, nCount - 2);
1138 }
1139
1140 void SetMDIFrameMenu()
1141 {
1142 HMENU hWindowMenu = GetStandardWindowMenu(m_hMenu);
1143 MDISetMenu(m_hMenu, hWindowMenu);
1144 MDIRefreshMenu();
1145 ::DrawMenuBar(GetMDIFrame());
1146 }
1147
1148 HWND GetMDIFrame() const
1149 {
1150 return ::GetParent(m_hWndMDIClient);
1151 }
1152 };
1153
1154
1155 ///////////////////////////////////////////////////////////////////////////////
1156 // CMDIFrameWindowImpl
1157
1158 // MDI child command chaining macro (only for MDI frame windows)
1159 #define CHAIN_MDI_CHILD_COMMANDS() \
1160 if(uMsg == WM_COMMAND) \
1161 { \
1162 HWND hWndChild = this->MDIGetActive(); \
1163 if(hWndChild != NULL) \
1164 ::SendMessage(hWndChild, uMsg, wParam, lParam); \
1165 }
1166
1167 template <class T, class TBase = CMDIWindow, class TWinTraits = ATL::CFrameWinTraits>
1168 class ATL_NO_VTABLE CMDIFrameWindowImpl : public CFrameWindowImplBase<TBase, TWinTraits >
1169 {
1170 public:
1171 HWND Create(HWND hWndParent = NULL, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
1172 DWORD dwStyle = 0, DWORD dwExStyle = 0,
1173 HMENU hMenu = NULL, LPVOID lpCreateParam = NULL)
1174 {
1175 this->m_hMenu = hMenu;
1176 ATOM atom = T::GetWndClassInfo().Register(&this->m_pfnSuperWindowProc);
1177
1178 dwStyle = T::GetWndStyle(dwStyle);
1179 dwExStyle = T::GetWndExStyle(dwExStyle);
1180
1181 if(rect.m_lpRect == NULL)
1182 rect.m_lpRect = &TBase::rcDefault;
1183
1184 return CFrameWindowImplBase<TBase, TWinTraits >::Create(hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, hMenu, atom, lpCreateParam);
1185 }
1186
1187 HWND CreateEx(HWND hWndParent = NULL, ATL::_U_RECT rect = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, LPVOID lpCreateParam = NULL)
1188 {
1189 const int cchName = 256;
1190 TCHAR szWindowName[cchName] = {};
1191 ::LoadString(ModuleHelper::GetResourceInstance(), T::GetWndClassInfo().m_uCommonResourceID, szWindowName, cchName);
1192 HMENU hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID));
1193
1194 T* pT = static_cast<T*>(this);
1195 HWND hWnd = pT->Create(hWndParent, rect, szWindowName, dwStyle, dwExStyle, hMenu, lpCreateParam);
1196
1197 if(hWnd != NULL)
1198 this->m_hAccel = ::LoadAccelerators(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID));
1199
1200 return hWnd;
1201 }
1202
1203 BOOL CreateSimpleToolBar(UINT nResourceID = 0, DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
1204 {
1205 ATLASSERT(!::IsWindow(this->m_hWndToolBar));
1206 if(nResourceID == 0)
1207 nResourceID = T::GetWndClassInfo().m_uCommonResourceID;
1208 this->m_hWndToolBar = T::CreateSimpleToolBarCtrl(this->m_hWnd, nResourceID, TRUE, dwStyle, nID);
1209 return (this->m_hWndToolBar != NULL);
1210 }
1211
1212 virtual WNDPROC GetWindowProc()
1213 {
1214 return MDIFrameWindowProc;
1215 }
1216
1217 static LRESULT CALLBACK MDIFrameWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1218 {
1219 CMDIFrameWindowImpl< T, TBase, TWinTraits >* pThis = (CMDIFrameWindowImpl< T, TBase, TWinTraits >*)hWnd;
1220 // set a ptr to this message and save the old value
1221 ATL::_ATL_MSG msg(pThis->m_hWnd, uMsg, wParam, lParam);
1222 const ATL::_ATL_MSG* pOldMsg = pThis->m_pCurrentMsg;
1223 pThis->m_pCurrentMsg = &msg;
1224 // pass to the message map to process
1225 LRESULT lRes = 0;
1226 BOOL bRet = pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam, lRes, 0);
1227 // restore saved value for the current message
1228 ATLASSERT(pThis->m_pCurrentMsg == &msg);
1229 pThis->m_pCurrentMsg = pOldMsg;
1230 // do the default processing if message was not handled
1231 if(!bRet)
1232 {
1233 if(uMsg != WM_NCDESTROY)
1234 {
1235 lRes = pThis->DefWindowProc(uMsg, wParam, lParam);
1236 }
1237 else
1238 {
1239 // unsubclass, if needed
1240 LONG_PTR pfnWndProc = ::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC);
1241 lRes = pThis->DefWindowProc(uMsg, wParam, lParam);
1242 if((pThis->m_pfnSuperWindowProc != ::DefWindowProc) && (::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC) == pfnWndProc))
1243 ::SetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC, (LONG_PTR)pThis->m_pfnSuperWindowProc);
1244 // mark window as destryed
1245 pThis->m_dwState |= ATL::CWindowImplRoot< TBase >::WINSTATE_DESTROYED;
1246 }
1247 }
1248 if((pThis->m_dwState & ATL::CWindowImplRoot< TBase >::WINSTATE_DESTROYED) && (pThis->m_pCurrentMsg == NULL))
1249 {
1250 // clear out window handle
1251 HWND hWndThis = pThis->m_hWnd;
1252 pThis->m_hWnd = NULL;
1253 pThis->m_dwState &= ~ATL::CWindowImplRoot< TBase >::WINSTATE_DESTROYED;
1254 // clean up after window is destroyed
1255 pThis->OnFinalMessage(hWndThis);
1256 }
1257 return lRes;
1258 }
1259
1260 // Overriden to call DefWindowProc which uses DefFrameProc
1261 LRESULT DefWindowProc()
1262 {
1263 const ATL::_ATL_MSG* pMsg = this->m_pCurrentMsg;
1264 LRESULT lRes = 0;
1265 if (pMsg != NULL)
1266 lRes = DefWindowProc(pMsg->message, pMsg->wParam, pMsg->lParam);
1267 return lRes;
1268 }
1269
1270 LRESULT DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
1271 {
1272 return ::DefFrameProc(this->m_hWnd, this->m_hWndMDIClient, uMsg, wParam, lParam);
1273 }
1274
1275 BOOL PreTranslateMessage(MSG* pMsg)
1276 {
1277 if(CFrameWindowImplBase<TBase, TWinTraits>::PreTranslateMessage(pMsg))
1278 return TRUE;
1279 return ::TranslateMDISysAccel(this->m_hWndMDIClient, pMsg);
1280 }
1281
1282 HWND CreateMDIClient(HMENU hWindowMenu = NULL, UINT nID = ATL_IDW_CLIENT, UINT nFirstChildID = ATL_IDM_FIRST_MDICHILD)
1283 {
1284 DWORD dwStyle = WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | MDIS_ALLCHILDSTYLES;
1285 DWORD dwExStyle = WS_EX_CLIENTEDGE;
1286
1287 CLIENTCREATESTRUCT ccs = {};
1288 ccs.hWindowMenu = hWindowMenu;
1289 ccs.idFirstChild = nFirstChildID;
1290
1291 if((this->GetStyle() & (WS_HSCROLL | WS_VSCROLL)) != 0)
1292 {
1293 // parent MDI frame's scroll styles move to the MDICLIENT
1294 dwStyle |= (this->GetStyle() & (WS_HSCROLL | WS_VSCROLL));
1295
1296 // fast way to turn off the scrollbar bits (without a resize)
1297 this->ModifyStyle(WS_HSCROLL | WS_VSCROLL, 0, SWP_NOREDRAW | SWP_FRAMECHANGED);
1298 }
1299
1300 // Create MDICLIENT window
1301 this->m_hWndClient = ::CreateWindowEx(dwExStyle, _T("MDIClient"), NULL,
1302 dwStyle, 0, 0, 1, 1, this->m_hWnd, (HMENU)LongToHandle(nID),
1303 ModuleHelper::GetModuleInstance(), (LPVOID)&ccs);
1304 if (this->m_hWndClient == NULL)
1305 {
1306 ATLTRACE2(atlTraceUI, 0, _T("MDI Frame failed to create MDICLIENT.\n"));
1307 return NULL;
1308 }
1309
1310 // Move it to the top of z-order
1311 ::BringWindowToTop(this->m_hWndClient);
1312
1313 // set as MDI client window
1314 this->m_hWndMDIClient = this->m_hWndClient;
1315
1316 // update to proper size
1317 T* pT = static_cast<T*>(this);
1318 pT->UpdateLayout();
1319
1320 return this->m_hWndClient;
1321 }
1322
1323 typedef CFrameWindowImplBase<TBase, TWinTraits > _baseClass;
1324
1325 BEGIN_MSG_MAP(CMDIFrameWindowImpl)
1326 MESSAGE_HANDLER(WM_SIZE, OnSize)
1327 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
1328 MESSAGE_HANDLER(WM_MDISETMENU, OnMDISetMenu)
1329 #ifndef _ATL_NO_REBAR_SUPPORT
1330 NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnReBarAutoSize)
1331 NOTIFY_CODE_HANDLER(RBN_CHEVRONPUSHED, OnChevronPushed)
1332 #endif // !_ATL_NO_REBAR_SUPPORT
1333 CHAIN_MSG_MAP(_baseClass)
1334 END_MSG_MAP()
1335
1336 LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1337 {
1338 if(wParam != SIZE_MINIMIZED)
1339 {
1340 T* pT = static_cast<T*>(this);
1341 pT->UpdateLayout();
1342 }
1343 // message must be handled, otherwise DefFrameProc would resize the client again
1344 return 0;
1345 }
1346
1347 LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
1348 {
1349 // don't allow CFrameWindowImplBase to handle this one
1350 return DefWindowProc(uMsg, wParam, lParam);
1351 }
1352
1353 LRESULT OnMDISetMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1354 {
1355 this->SetMDIFrameMenu();
1356 return 0;
1357 }
1358
1359 #ifndef _ATL_NO_REBAR_SUPPORT
1360 LRESULT OnReBarAutoSize(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
1361 {
1362 T* pT = static_cast<T*>(this);
1363 pT->UpdateLayout(FALSE);
1364 return 0;
1365 }
1366
1367 LRESULT OnChevronPushed(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
1368 {
1369 T* pT = static_cast<T*>(this);
1370 typename CFrameWindowImplBase<TBase, TWinTraits >::_ChevronMenuInfo cmi = { NULL, (LPNMREBARCHEVRON)pnmh, false };
1371 if(!pT->PrepareChevronMenu(cmi))
1372 {
1373 bHandled = FALSE;
1374 return 1;
1375 }
1376 // display a popup menu with hidden items
1377 pT->DisplayChevronMenu(cmi);
1378 // cleanup
1379 pT->CleanupChevronMenu(cmi);
1380 return 0;
1381 }
1382 #endif // !_ATL_NO_REBAR_SUPPORT
1383 };
1384
1385
1386 ///////////////////////////////////////////////////////////////////////////////
1387 // CMDIChildWindowImpl
1388
1389 template <class T, class TBase = CMDIWindow, class TWinTraits = ATL::CMDIChildWinTraits>
1390 class ATL_NO_VTABLE CMDIChildWindowImpl : public CFrameWindowImplBase<TBase, TWinTraits >
1391 {
1392 public:
1393 HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
1394 DWORD dwStyle = 0, DWORD dwExStyle = 0,
1395 UINT nMenuID = 0, LPVOID lpCreateParam = NULL)
1396 {
1397 ATOM atom = T::GetWndClassInfo().Register(&this->m_pfnSuperWindowProc);
1398
1399 if(nMenuID != 0)
1400 this->m_hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(nMenuID));
1401
1402 dwStyle = T::GetWndStyle(dwStyle);
1403 dwExStyle = T::GetWndExStyle(dwExStyle);
1404
1405 dwExStyle |= WS_EX_MDICHILD; // force this one
1406 this->m_pfnSuperWindowProc = ::DefMDIChildProc;
1407 this->m_hWndMDIClient = hWndParent;
1408 ATLASSERT(::IsWindow(this->m_hWndMDIClient));
1409
1410 if(rect.m_lpRect == NULL)
1411 rect.m_lpRect = &TBase::rcDefault;
1412
1413 // If the currently active MDI child is maximized, we want to create this one maximized too
1414 ATL::CWindow wndParent = hWndParent;
1415 BOOL bMaximized = FALSE;
1416 wndParent.SendMessage(WM_MDIGETACTIVE, 0, (LPARAM)&bMaximized);
1417 if(bMaximized)
1418 wndParent.SetRedraw(FALSE);
1419
1420 HWND hWnd = CFrameWindowImplBase<TBase, TWinTraits >::Create(hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, (UINT)0U, atom, lpCreateParam);
1421
1422 if(bMaximized)
1423 {
1424 // Maximize and redraw everything
1425 if(hWnd != NULL)
1426 this->MDIMaximize(hWnd);
1427 wndParent.SetRedraw(TRUE);
1428 wndParent.RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
1429 ::SetFocus(this->GetMDIFrame()); // focus will be set back to this window
1430 }
1431 else if((hWnd != NULL) && ::IsWindowVisible(this->m_hWnd) && !::IsChild(hWnd, ::GetFocus()))
1432 {
1433 ::SetFocus(hWnd);
1434 }
1435
1436 return hWnd;
1437 }
1438
1439 HWND CreateEx(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR lpcstrWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, LPVOID lpCreateParam = NULL)
1440 {
1441 const int cchName = 256;
1442 TCHAR szWindowName[cchName] = {};
1443 if(lpcstrWindowName == NULL)
1444 {
1445 ::LoadString(ModuleHelper::GetResourceInstance(), T::GetWndClassInfo().m_uCommonResourceID, szWindowName, cchName);
1446 lpcstrWindowName = szWindowName;
1447 }
1448
1449 T* pT = static_cast<T*>(this);
1450 HWND hWnd = pT->Create(hWndParent, rect, lpcstrWindowName, dwStyle, dwExStyle, T::GetWndClassInfo().m_uCommonResourceID, lpCreateParam);
1451
1452 if(hWnd != NULL)
1453 this->m_hAccel = ::LoadAccelerators(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID));
1454
1455 return hWnd;
1456 }
1457
1458 BOOL CreateSimpleToolBar(UINT nResourceID = 0, DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
1459 {
1460 ATLASSERT(!::IsWindow(this->m_hWndToolBar));
1461 if(nResourceID == 0)
1462 nResourceID = T::GetWndClassInfo().m_uCommonResourceID;
1463 this->m_hWndToolBar = T::CreateSimpleToolBarCtrl(this->m_hWnd, nResourceID, TRUE, dwStyle, nID);
1464 return (this->m_hWndToolBar != NULL);
1465 }
1466
1467 BOOL UpdateClientEdge(LPRECT lpRect = NULL)
1468 {
1469 // only adjust for active MDI child window
1470 HWND hWndChild = this->MDIGetActive();
1471 if((hWndChild != NULL) && (hWndChild != this->m_hWnd))
1472 return FALSE;
1473
1474 // need to adjust the client edge style as max/restore happens
1475 DWORD dwStyle = ::GetWindowLong(this->m_hWndMDIClient, GWL_EXSTYLE);
1476 DWORD dwNewStyle = dwStyle;
1477 if((hWndChild != NULL) && ((this->GetExStyle() & WS_EX_CLIENTEDGE) == 0) && ((this->GetStyle() & WS_MAXIMIZE) != 0))
1478 dwNewStyle &= ~(WS_EX_CLIENTEDGE);
1479 else
1480 dwNewStyle |= WS_EX_CLIENTEDGE;
1481
1482 if(dwStyle != dwNewStyle)
1483 {
1484 // SetWindowPos will not move invalid bits
1485 ::RedrawWindow(this->m_hWndMDIClient, NULL, NULL,
1486 RDW_INVALIDATE | RDW_ALLCHILDREN);
1487 // remove/add WS_EX_CLIENTEDGE to MDI client area
1488 ::SetWindowLong(this->m_hWndMDIClient, GWL_EXSTYLE, dwNewStyle);
1489 ::SetWindowPos(this->m_hWndMDIClient, NULL, 0, 0, 0, 0,
1490 SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE |
1491 SWP_NOZORDER | SWP_NOCOPYBITS);
1492
1493 // return new client area
1494 if (lpRect != NULL)
1495 ::GetClientRect(this->m_hWndMDIClient, lpRect);
1496
1497 return TRUE;
1498 }
1499
1500 return FALSE;
1501 }
1502
1503 typedef CFrameWindowImplBase<TBase, TWinTraits > _baseClass;
1504 BEGIN_MSG_MAP(CMDIChildWindowImpl)
1505 MESSAGE_HANDLER(WM_SIZE, OnSize)
1506 MESSAGE_HANDLER(WM_WINDOWPOSCHANGED, OnWindowPosChanged)
1507 MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
1508 MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
1509 MESSAGE_HANDLER(WM_MDIACTIVATE, OnMDIActivate)
1510 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
1511 #ifndef _ATL_NO_REBAR_SUPPORT
1512 NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnReBarAutoSize)
1513 NOTIFY_CODE_HANDLER(RBN_CHEVRONPUSHED, OnChevronPushed)
1514 #endif // !_ATL_NO_REBAR_SUPPORT
1515 CHAIN_MSG_MAP(_baseClass)
1516 END_MSG_MAP()
1517
1518 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
1519 {
1520 this->DefWindowProc(uMsg, wParam, lParam); // needed for MDI children
1521 if(wParam != SIZE_MINIMIZED)
1522 {
1523 T* pT = static_cast<T*>(this);
1524 pT->UpdateLayout();
1525 }
1526 return 0;
1527 }
1528
1529 LRESULT OnWindowPosChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
1530 {
1531 // update MDI client edge and adjust MDI child rect
1532 LPWINDOWPOS lpWndPos = (LPWINDOWPOS)lParam;
1533
1534 if(!(lpWndPos->flags & SWP_NOSIZE))
1535 {
1536 RECT rectClient = {};
1537 if(UpdateClientEdge(&rectClient) && ((this->GetStyle() & WS_MAXIMIZE) != 0))
1538 {
1539 ::AdjustWindowRectEx(&rectClient, this->GetStyle(), FALSE, this->GetExStyle());
1540 lpWndPos->x = rectClient.left;
1541 lpWndPos->y = rectClient.top;
1542 lpWndPos->cx = rectClient.right - rectClient.left;
1543 lpWndPos->cy = rectClient.bottom - rectClient.top;
1544 }
1545 }
1546
1547 bHandled = FALSE;
1548 return 1;
1549 }
1550
1551 LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
1552 {
1553 LRESULT lRes = this->DefWindowProc(uMsg, wParam, lParam);
1554
1555 // Activate this MDI window if needed
1556 if((lRes == MA_ACTIVATE) || (lRes == MA_ACTIVATEANDEAT))
1557 {
1558 if(this->MDIGetActive() != this->m_hWnd)
1559 this->MDIActivate(this->m_hWnd);
1560 }
1561
1562 return lRes;
1563 }
1564
1565 LRESULT OnMenuSelect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
1566 {
1567 return ::SendMessage(this->GetMDIFrame(), uMsg, wParam, lParam);
1568 }
1569
1570 LRESULT OnMDIActivate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
1571 {
1572 if(((HWND)lParam == this->m_hWnd) && (this->m_hMenu != NULL))
1573 this->SetMDIFrameMenu();
1574 else if((HWND)lParam == NULL)
1575 ::SendMessage(this->GetMDIFrame(), WM_MDISETMENU, 0, 0);
1576
1577 bHandled = FALSE;
1578 return 1;
1579 }
1580
1581 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
1582 {
1583 if(this->m_hMenu != NULL)
1584 {
1585 ::DestroyMenu(this->m_hMenu);
1586 this->m_hMenu = NULL;
1587 }
1588 UpdateClientEdge();
1589 bHandled = FALSE;
1590 return 1;
1591 }
1592
1593 #ifndef _ATL_NO_REBAR_SUPPORT
1594 LRESULT OnReBarAutoSize(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
1595 {
1596 T* pT = static_cast<T*>(this);
1597 pT->UpdateLayout(FALSE);
1598 return 0;
1599 }
1600
1601 LRESULT OnChevronPushed(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
1602 {
1603 T* pT = static_cast<T*>(this);
1604 typename CFrameWindowImplBase<TBase, TWinTraits >::_ChevronMenuInfo cmi = { NULL, (LPNMREBARCHEVRON)pnmh, false };
1605 if(!pT->PrepareChevronMenu(cmi))
1606 {
1607 bHandled = FALSE;
1608 return 1;
1609 }
1610 // display a popup menu with hidden items
1611 pT->DisplayChevronMenu(cmi);
1612 // cleanup
1613 pT->CleanupChevronMenu(cmi);
1614 return 0;
1615 }
1616 #endif // !_ATL_NO_REBAR_SUPPORT
1617 };
1618
1619
1620 ///////////////////////////////////////////////////////////////////////////////
1621 // COwnerDraw - MI class for owner-draw support
1622
1623 template <class T>
1624 class COwnerDraw
1625 {
1626 public:
1627 // Message map and handlers
1628 BEGIN_MSG_MAP(COwnerDraw< T >)
1629 MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem)
1630 MESSAGE_HANDLER(WM_MEASUREITEM, OnMeasureItem)
1631 MESSAGE_HANDLER(WM_COMPAREITEM, OnCompareItem)
1632 MESSAGE_HANDLER(WM_DELETEITEM, OnDeleteItem)
1633 ALT_MSG_MAP(1)
1634 MESSAGE_HANDLER(OCM_DRAWITEM, OnDrawItem)
1635 MESSAGE_HANDLER(OCM_MEASUREITEM, OnMeasureItem)
1636 MESSAGE_HANDLER(OCM_COMPAREITEM, OnCompareItem)
1637 MESSAGE_HANDLER(OCM_DELETEITEM, OnDeleteItem)
1638 END_MSG_MAP()
1639
1640 LRESULT OnDrawItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
1641 {
1642 T* pT = static_cast<T*>(this);
1643 pT->SetMsgHandled(TRUE);
1644 pT->DrawItem((LPDRAWITEMSTRUCT)lParam);
1645 bHandled = pT->IsMsgHandled();
1646 return (LRESULT)TRUE;
1647 }
1648
1649 LRESULT OnMeasureItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
1650 {
1651 T* pT = static_cast<T*>(this);
1652 pT->SetMsgHandled(TRUE);
1653 pT->MeasureItem((LPMEASUREITEMSTRUCT)lParam);
1654 bHandled = pT->IsMsgHandled();
1655 return (LRESULT)TRUE;
1656 }
1657
1658 LRESULT OnCompareItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
1659 {
1660 T* pT = static_cast<T*>(this);
1661 pT->SetMsgHandled(TRUE);
1662 bHandled = pT->IsMsgHandled();
1663 return (LRESULT)pT->CompareItem((LPCOMPAREITEMSTRUCT)lParam);
1664 }
1665
1666 LRESULT OnDeleteItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
1667 {
1668 T* pT = static_cast<T*>(this);
1669 pT->SetMsgHandled(TRUE);
1670 pT->DeleteItem((LPDELETEITEMSTRUCT)lParam);
1671 bHandled = pT->IsMsgHandled();
1672 return (LRESULT)TRUE;
1673 }
1674
1675 // Overrideables
1676 void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/)
1677 {
1678 // must be implemented
1679 ATLASSERT(FALSE);
1680 }
1681
1682 void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
1683 {
1684 if(lpMeasureItemStruct->CtlType != ODT_MENU)
1685 {
1686 // return default height for a system font
1687 T* pT = static_cast<T*>(this);
1688 HWND hWnd = pT->GetDlgItem(lpMeasureItemStruct->CtlID);
1689 CClientDC dc(hWnd);
1690 TEXTMETRIC tm = {};
1691 dc.GetTextMetrics(&tm);
1692
1693 lpMeasureItemStruct->itemHeight = tm.tmHeight;
1694 }
1695 else
1696 lpMeasureItemStruct->itemHeight = ::GetSystemMetrics(SM_CYMENU);
1697 }
1698
1699 int CompareItem(LPCOMPAREITEMSTRUCT /*lpCompareItemStruct*/)
1700 {
1701 // all items are equal
1702 return 0;
1703 }
1704
1705 void DeleteItem(LPDELETEITEMSTRUCT /*lpDeleteItemStruct*/)
1706 {
1707 // default - nothing
1708 }
1709 };
1710
1711
1712 ///////////////////////////////////////////////////////////////////////////////
1713 // Update UI macros
1714
1715 // these build the Update UI map inside a class definition
1716 #define BEGIN_UPDATE_UI_MAP(thisClass) \
1717 static const CUpdateUIBase::_AtlUpdateUIMap* GetUpdateUIMap() \
1718 { \
1719 static const CUpdateUIBase::_AtlUpdateUIMap theMap[] = \
1720 {
1721
1722 #define UPDATE_ELEMENT(nID, wType) \
1723 { nID, wType },
1724
1725 #define END_UPDATE_UI_MAP() \
1726 { (WORD)-1, 0 } \
1727 }; \
1728 return theMap; \
1729 }
1730
1731 ///////////////////////////////////////////////////////////////////////////////
1732 // CUpdateUI - manages UI elements updating
1733
1734 class CUpdateUIBase
1735 {
1736 public:
1737 // constants
1738 enum
1739 {
1740 // UI element type
1741 UPDUI_MENUPOPUP = 0x0001,
1742 UPDUI_MENUBAR = 0x0002,
1743 UPDUI_CHILDWINDOW = 0x0004,
1744 UPDUI_TOOLBAR = 0x0008,
1745 UPDUI_STATUSBAR = 0x0010,
1746 // state
1747 UPDUI_ENABLED = 0x0000,
1748 UPDUI_DISABLED = 0x0100,
1749 UPDUI_CHECKED = 0x0200,
1750 UPDUI_CHECKED2 = 0x0400,
1751 UPDUI_RADIO = 0x0800,
1752 UPDUI_DEFAULT = 0x1000,
1753 UPDUI_TEXT = 0x2000,
1754 // internal state
1755 UPDUI_CLEARDEFAULT = 0x4000,
1756 };
1757
1758 // element data
1759 struct _AtlUpdateUIElement
1760 {
1761 HWND m_hWnd;
1762 WORD m_wType;
1763
1764 bool operator ==(const _AtlUpdateUIElement& e) const
1765 { return ((m_hWnd == e.m_hWnd) && (m_wType == e.m_wType)); }
1766 };
1767
1768 // map data
1769 struct _AtlUpdateUIMap
1770 {
1771 WORD m_nID;
1772 WORD m_wType;
1773
1774 bool operator ==(const _AtlUpdateUIMap& e) const
1775 { return ((m_nID == e.m_nID) && (m_wType == e.m_wType)); }
1776 };
1777
1778 // instance data
1779 #pragma warning(push)
1780 #pragma warning(disable: 4201) // nameless unions are part of C++
1781
1782 struct _AtlUpdateUIData
1783 {
1784 WORD m_wState;
1785 union
1786 {
1787 void* m_lpData;
1788 LPTSTR m_lpstrText;
1789 struct
1790 {
1791 WORD m_nIDFirst;
1792 WORD m_nIDLast;
1793 };
1794 };
1795
1796 bool operator ==(const _AtlUpdateUIData& e) const
1797 { return ((m_wState == e.m_wState) && (m_lpData == e.m_lpData)); }
1798 };
1799
1800 #pragma warning(pop)
1801
1802 ATL::CSimpleArray<_AtlUpdateUIElement> m_UIElements; // elements data
1803 const _AtlUpdateUIMap* m_pUIMap; // static UI data
1804 _AtlUpdateUIData* m_pUIData; // instance UI data
1805 WORD m_wDirtyType; // global dirty flag
1806
1807 bool m_bBlockAccelerators;
1808
1809
1810 // Constructor, destructor
1811 CUpdateUIBase() : m_pUIMap(NULL), m_pUIData(NULL), m_wDirtyType(0), m_bBlockAccelerators(false)
1812 { }
1813
1814 ~CUpdateUIBase()
1815 {
1816 if((m_pUIMap != NULL) && (m_pUIData != NULL))
1817 {
1818 const _AtlUpdateUIMap* pUIMap = m_pUIMap;
1819 _AtlUpdateUIData* pUIData = m_pUIData;
1820 while(pUIMap->m_nID != (WORD)-1)
1821 {
1822 if(pUIData->m_wState & UPDUI_TEXT)
1823 delete [] pUIData->m_lpstrText;
1824 pUIMap++;
1825 pUIData++;
1826 }
1827 delete [] m_pUIData;
1828 }
1829 }
1830
1831 // Check for disabled commands
1832 bool UIGetBlockAccelerators() const
1833 {
1834 return m_bBlockAccelerators;
1835 }
1836
1837 bool UISetBlockAccelerators(bool bBlock)
1838 {
1839 bool bOld = m_bBlockAccelerators;
1840 m_bBlockAccelerators = bBlock;
1841 return bOld;
1842 }
1843
1844 // Add elements
1845 BOOL UIAddMenuBar(HWND hWnd) // menu bar (main menu)
1846 {
1847 if(hWnd == NULL)
1848 return FALSE;
1849 _AtlUpdateUIElement e;
1850 e.m_hWnd = hWnd;
1851 e.m_wType = UPDUI_MENUBAR;
1852 return m_UIElements.Add(e);
1853 }
1854
1855 BOOL UIAddToolBar(HWND hWnd) // toolbar
1856 {
1857 if(hWnd == NULL)
1858 return FALSE;
1859 _AtlUpdateUIElement e;
1860 e.m_hWnd = hWnd;
1861 e.m_wType = UPDUI_TOOLBAR;
1862 return m_UIElements.Add(e);
1863 }
1864
1865 BOOL UIAddStatusBar(HWND hWnd) // status bar
1866 {
1867 if(hWnd == NULL)
1868 return FALSE;
1869 _AtlUpdateUIElement e;
1870 e.m_hWnd = hWnd;
1871 e.m_wType = UPDUI_STATUSBAR;
1872 return m_UIElements.Add(e);
1873 }
1874
1875 BOOL UIAddChildWindowContainer(HWND hWnd) // child window
1876 {
1877 if(hWnd == NULL)
1878 return FALSE;
1879 _AtlUpdateUIElement e;
1880 e.m_hWnd = hWnd;
1881 e.m_wType = UPDUI_CHILDWINDOW;
1882 return m_UIElements.Add(e);
1883 }
1884
1885 // Message map for popup menu updates and accelerator blocking
1886 BEGIN_MSG_MAP(CUpdateUIBase)
1887 MESSAGE_HANDLER(WM_INITMENUPOPUP, OnInitMenuPopup)
1888 MESSAGE_HANDLER(WM_COMMAND, OnCommand)
1889 END_MSG_MAP()
1890
1891 LRESULT OnInitMenuPopup(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
1892 {
1893 bHandled = FALSE;
1894 HMENU hMenu = (HMENU)wParam;
1895 if(hMenu == NULL)
1896 return 1;
1897 _AtlUpdateUIData* pUIData = m_pUIData;
1898 if(pUIData == NULL)
1899 return 1;
1900 const _AtlUpdateUIMap* pMap = m_pUIMap;
1901 while(pMap->m_nID != (WORD)-1)
1902 {
1903 if(pMap->m_wType & UPDUI_MENUPOPUP)
1904 {
1905 UIUpdateMenuBarElement(pMap->m_nID, pUIData, hMenu);
1906
1907 if((pUIData->m_wState & UPDUI_RADIO) != 0)
1908 ::CheckMenuRadioItem(hMenu, pUIData->m_nIDFirst, pUIData->m_nIDLast, pMap->m_nID, MF_BYCOMMAND);
1909 }
1910 pMap++;
1911 pUIData++;
1912 }
1913 return 0;
1914 }
1915
1916 LRESULT OnCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
1917 {
1918 bHandled = FALSE;
1919 if(m_bBlockAccelerators && (HIWORD(wParam) == 1)) // accelerators only
1920 {
1921 int nID = LOWORD(wParam);
1922 if((UIGetState(nID) & UPDUI_DISABLED) == UPDUI_DISABLED)
1923 {
1924 ATLTRACE2(atlTraceUI, 0, _T("CUpdateUIBase::OnCommand - blocked disabled command 0x%4.4X\n"), nID);
1925 bHandled = TRUE; // eat the command, UI item is disabled
1926 }
1927 }
1928 return 0;
1929 }
1930
1931 // methods for setting UI element state
1932 BOOL UIEnable(int nID, BOOL bEnable, BOOL bForceUpdate = FALSE)
1933 {
1934 const _AtlUpdateUIMap* pMap = m_pUIMap;
1935 _AtlUpdateUIData* pUIData = m_pUIData;
1936 if(pUIData == NULL)
1937 return FALSE;
1938
1939 for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
1940 {
1941 if(nID == (int)pMap->m_nID)
1942 {
1943 if(bEnable)
1944 {
1945 if(pUIData->m_wState & UPDUI_DISABLED)
1946 {
1947 pUIData->m_wState |= pMap->m_wType;
1948 pUIData->m_wState &= ~UPDUI_DISABLED;
1949 }
1950 }
1951 else
1952 {
1953 if(!(pUIData->m_wState & UPDUI_DISABLED))
1954 {
1955 pUIData->m_wState |= pMap->m_wType;
1956 pUIData->m_wState |= UPDUI_DISABLED;
1957 }
1958 }
1959
1960 if(bForceUpdate)
1961 pUIData->m_wState |= pMap->m_wType;
1962 if(pUIData->m_wState & pMap->m_wType)
1963 m_wDirtyType |= pMap->m_wType;
1964
1965 break; // found
1966 }
1967 }
1968
1969 return TRUE;
1970 }
1971
1972 BOOL UISetCheck(int nID, int nCheck, BOOL bForceUpdate = FALSE)
1973 {
1974 const _AtlUpdateUIMap* pMap = m_pUIMap;
1975 _AtlUpdateUIData* pUIData = m_pUIData;
1976 if(pUIData == NULL)
1977 return FALSE;
1978
1979 for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
1980 {
1981 if(nID == (int)pMap->m_nID)
1982 {
1983 switch(nCheck)
1984 {
1985 case 0:
1986 if((pUIData->m_wState & UPDUI_CHECKED) || (pUIData->m_wState & UPDUI_CHECKED2))
1987 {
1988 pUIData->m_wState |= pMap->m_wType;
1989 pUIData->m_wState &= ~(UPDUI_CHECKED | UPDUI_CHECKED2);
1990 }
1991 break;
1992 case 1:
1993 if(!(pUIData->m_wState & UPDUI_CHECKED))
1994 {
1995 pUIData->m_wState |= pMap->m_wType;
1996 pUIData->m_wState &= ~UPDUI_CHECKED2;
1997 pUIData->m_wState |= UPDUI_CHECKED;
1998 }
1999 break;
2000 case 2:
2001 if(!(pUIData->m_wState & UPDUI_CHECKED2))
2002 {
2003 pUIData->m_wState |= pMap->m_wType;
2004 pUIData->m_wState &= ~UPDUI_CHECKED;
2005 pUIData->m_wState |= UPDUI_CHECKED2;
2006 }
2007 break;
2008 }
2009
2010 if(bForceUpdate)
2011 pUIData->m_wState |= pMap->m_wType;
2012 if(pUIData->m_wState & pMap->m_wType)
2013 m_wDirtyType |= pMap->m_wType;
2014
2015 break; // found
2016 }
2017 }
2018
2019 return TRUE;
2020 }
2021
2022 // variant that supports bool (checked/not-checked, no intermediate state)
2023 BOOL UISetCheck(int nID, bool bCheck, BOOL bForceUpdate = FALSE)
2024 {
2025 return UISetCheck(nID, bCheck ? 1 : 0, bForceUpdate);
2026 }
2027
2028 BOOL UISetRadio(int nID, BOOL bRadio, BOOL bForceUpdate = FALSE)
2029 {
2030 const _AtlUpdateUIMap* pMap = m_pUIMap;
2031 _AtlUpdateUIData* pUIData = m_pUIData;
2032 if(pUIData == NULL)
2033 return FALSE;
2034
2035 for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
2036 {
2037 if(nID == (int)pMap->m_nID)
2038 {
2039 if(bRadio)
2040 {
2041 if(!(pUIData->m_wState & UPDUI_RADIO))
2042 {
2043 pUIData->m_wState |= pMap->m_wType;
2044 pUIData->m_wState |= UPDUI_RADIO;
2045 }
2046 }
2047 else
2048 {
2049 if(pUIData->m_wState & UPDUI_RADIO)
2050 {
2051 pUIData->m_wState |= pMap->m_wType;
2052 pUIData->m_wState &= ~UPDUI_RADIO;
2053 }
2054 }
2055
2056 if(bForceUpdate)
2057 pUIData->m_wState |= pMap->m_wType;
2058 if(pUIData->m_wState & pMap->m_wType)
2059 m_wDirtyType |= pMap->m_wType;
2060
2061 break; // found
2062 }
2063 }
2064
2065 return TRUE;
2066 }
2067
2068 // for menu items
2069 BOOL UISetRadioMenuItem(int nID, int nIDFirst, int nIDLast, BOOL bForceUpdate = FALSE)
2070 {
2071 const _AtlUpdateUIMap* pMap = m_pUIMap;
2072 _AtlUpdateUIData* pUIData = m_pUIData;
2073 if(pUIData == NULL)
2074 return FALSE;
2075
2076 for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
2077 {
2078 if(nID == (int)pMap->m_nID)
2079 {
2080 pUIData->m_wState |= pMap->m_wType;
2081 pUIData->m_wState |= UPDUI_RADIO;
2082 pUIData->m_nIDFirst = (WORD)nIDFirst;
2083 pUIData->m_nIDLast = (WORD)nIDLast;
2084
2085 if(bForceUpdate)
2086 pUIData->m_wState |= pMap->m_wType;
2087 if(pUIData->m_wState & pMap->m_wType)
2088 m_wDirtyType |= pMap->m_wType;
2089 }
2090 else if((pMap->m_nID >= nIDFirst) && (pMap->m_nID <= nIDLast))
2091 {
2092 if(pUIData->m_wState & UPDUI_RADIO)
2093 {
2094 pUIData->m_wState &= ~pMap->m_wType;
2095 pUIData->m_wState &= ~UPDUI_RADIO;
2096 pUIData->m_nIDFirst = 0;
2097 pUIData->m_nIDLast = 0;
2098 }
2099 }
2100
2101 if(pMap->m_nID == nIDLast)
2102 break;
2103 }
2104
2105 return TRUE;
2106 }
2107
2108 BOOL UISetText(int nID, LPCTSTR lpstrText, BOOL bForceUpdate = FALSE)
2109 {
2110 const _AtlUpdateUIMap* pMap = m_pUIMap;
2111 _AtlUpdateUIData* pUIData = m_pUIData;
2112 if(pUIData == NULL)
2113 return FALSE;
2114 if(lpstrText == NULL)
2115 lpstrText = _T("");
2116
2117 for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
2118 {
2119 if(nID == (int)pMap->m_nID)
2120 {
2121 if((pUIData->m_lpstrText == NULL) || lstrcmp(pUIData->m_lpstrText, lpstrText))
2122 {
2123 delete [] pUIData->m_lpstrText;
2124 pUIData->m_lpstrText = NULL;
2125 int nStrLen = lstrlen(lpstrText);
2126 ATLTRY(pUIData->m_lpstrText = new TCHAR[nStrLen + 1]);
2127 if(pUIData->m_lpstrText == NULL)
2128 {
2129 ATLTRACE2(atlTraceUI, 0, _T("UISetText - memory allocation failed\n"));
2130 break;
2131 }
2132 ATL::Checked::tcscpy_s(pUIData->m_lpstrText, nStrLen + 1, lpstrText);
2133 pUIData->m_wState |= (UPDUI_TEXT | pMap->m_wType);
2134 }
2135
2136 if(bForceUpdate)
2137 pUIData->m_wState |= (UPDUI_TEXT | pMap->m_wType);
2138 if(pUIData->m_wState & pMap->m_wType)
2139 m_wDirtyType |= pMap->m_wType;
2140
2141 break; // found
2142 }
2143 }
2144
2145 return TRUE;
2146 }
2147
2148 BOOL UISetDefault(int nID, BOOL bDefault, BOOL bForceUpdate = FALSE)
2149 {
2150 const _AtlUpdateUIMap* pMap = m_pUIMap;
2151 _AtlUpdateUIData* pUIData = m_pUIData;
2152 if(pUIData == NULL)
2153 return FALSE;
2154
2155 for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
2156 {
2157 if(nID == (int)pMap->m_nID)
2158 {
2159 if(bDefault)
2160 {
2161 if((pUIData->m_wState & UPDUI_DEFAULT) == 0)
2162 {
2163 pUIData->m_wState |= pMap->m_wType;
2164 pUIData->m_wState |= UPDUI_DEFAULT;
2165 }
2166 }
2167 else
2168 {
2169 if((pUIData->m_wState & UPDUI_DEFAULT) != 0)
2170 {
2171 pUIData->m_wState |= pMap->m_wType;
2172 pUIData->m_wState &= ~UPDUI_DEFAULT;
2173 pUIData->m_wState |= UPDUI_CLEARDEFAULT;
2174 }
2175 }
2176
2177 if(bForceUpdate)
2178 pUIData->m_wState |= pMap->m_wType;
2179 if(pUIData->m_wState & pMap->m_wType)
2180 m_wDirtyType |= pMap->m_wType;
2181
2182 break; // found
2183 }
2184 }
2185
2186 return TRUE;
2187 }
2188
2189 // methods for complete state set/get
2190 BOOL UISetState(int nID, DWORD dwState)
2191 {
2192 const _AtlUpdateUIMap* pMap = m_pUIMap;
2193 _AtlUpdateUIData* pUIData = m_pUIData;
2194 if(pUIData == NULL)
2195 return FALSE;
2196 for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
2197 {
2198 if(nID == (int)pMap->m_nID)
2199 {
2200 pUIData->m_wState = (WORD)(dwState | pMap->m_wType);
2201 m_wDirtyType |= pMap->m_wType;
2202 break; // found
2203 }
2204 }
2205 return TRUE;
2206 }
2207
2208 DWORD UIGetState(int nID)
2209 {
2210 const _AtlUpdateUIMap* pMap = m_pUIMap;
2211 _AtlUpdateUIData* pUIData = m_pUIData;
2212 if(pUIData == NULL)
2213 return 0;
2214 for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
2215 {
2216 if(nID == (int)pMap->m_nID)
2217 return pUIData->m_wState;
2218 }
2219 return 0;
2220 }
2221
2222 // methods for updating UI
2223 BOOL UIUpdateMenuBar(BOOL bForceUpdate = FALSE, BOOL bMainMenu = FALSE)
2224 {
2225 if(!(m_wDirtyType & UPDUI_MENUBAR) && !bForceUpdate)
2226 return TRUE;
2227
2228 const _AtlUpdateUIMap* pMap = m_pUIMap;
2229 _AtlUpdateUIData* pUIData = m_pUIData;
2230 if(pUIData == NULL)
2231 return FALSE;
2232
2233 while(pMap->m_nID != (WORD)-1)
2234 {
2235 for(int i = 0; i < m_UIElements.GetSize(); i++)
2236 {
2237 if(m_UIElements[i].m_wType == UPDUI_MENUBAR)
2238 {
2239 HMENU hMenu = ::GetMenu(m_UIElements[i].m_hWnd);
2240 if((hMenu != NULL) && (pUIData->m_wState & UPDUI_MENUBAR) && (pMap->m_wType & UPDUI_MENUBAR))
2241 UIUpdateMenuBarElement(pMap->m_nID, pUIData, hMenu);
2242 }
2243 if(bMainMenu)
2244 ::DrawMenuBar(m_UIElements[i].m_hWnd);
2245 }
2246 pMap++;
2247 pUIData->m_wState &= ~UPDUI_MENUBAR;
2248 if(pUIData->m_wState & UPDUI_TEXT)
2249 {
2250 delete [] pUIData->m_lpstrText;
2251 pUIData->m_lpstrText = NULL;
2252 pUIData->m_wState &= ~UPDUI_TEXT;
2253 }
2254 pUIData++;
2255 }
2256
2257 m_wDirtyType &= ~UPDUI_MENUBAR;
2258 return TRUE;
2259 }
2260
2261 BOOL UIUpdateToolBar(BOOL bForceUpdate = FALSE)
2262 {
2263 if(!(m_wDirtyType & UPDUI_TOOLBAR) && !bForceUpdate)
2264 return TRUE;
2265
2266 const _AtlUpdateUIMap* pMap = m_pUIMap;
2267 _AtlUpdateUIData* pUIData = m_pUIData;
2268 if(pUIData == NULL)
2269 return FALSE;
2270
2271 while(pMap->m_nID != (WORD)-1)
2272 {
2273 for(int i = 0; i < m_UIElements.GetSize(); i++)
2274 {
2275 if(m_UIElements[i].m_wType == UPDUI_TOOLBAR)
2276 {
2277 if((pUIData->m_wState & UPDUI_TOOLBAR) && (pMap->m_wType & UPDUI_TOOLBAR))
2278 UIUpdateToolBarElement(pMap->m_nID, pUIData, m_UIElements[i].m_hWnd);
2279 }
2280 }
2281 pMap++;
2282 pUIData->m_wState &= ~UPDUI_TOOLBAR;
2283 pUIData++;
2284 }
2285
2286 m_wDirtyType &= ~UPDUI_TOOLBAR;
2287 return TRUE;
2288 }
2289
2290 BOOL UIUpdateStatusBar(BOOL bForceUpdate = FALSE)
2291 {
2292 if(!(m_wDirtyType & UPDUI_STATUSBAR) && !bForceUpdate)
2293 return TRUE;
2294
2295 const _AtlUpdateUIMap* pMap = m_pUIMap;
2296 _AtlUpdateUIData* pUIData = m_pUIData;
2297 if(pUIData == NULL)
2298 return FALSE;
2299
2300 while(pMap->m_nID != (WORD)-1)
2301 {
2302 for(int i = 0; i < m_UIElements.GetSize(); i++)
2303 {
2304 if(m_UIElements[i].m_wType == UPDUI_STATUSBAR)
2305 {
2306 if((pUIData->m_wState & UPDUI_STATUSBAR) && (pMap->m_wType & UPDUI_STATUSBAR))
2307 UIUpdateStatusBarElement(pMap->m_nID, pUIData, m_UIElements[i].m_hWnd);
2308 }
2309 }
2310 pMap++;
2311 pUIData->m_wState &= ~UPDUI_STATUSBAR;
2312 if(pUIData->m_wState & UPDUI_TEXT)
2313 {
2314 delete [] pUIData->m_lpstrText;
2315 pUIData->m_lpstrText = NULL;
2316 pUIData->m_wState &= ~UPDUI_TEXT;
2317 }
2318 pUIData++;
2319 }
2320
2321 m_wDirtyType &= ~UPDUI_STATUSBAR;
2322 return TRUE;
2323 }
2324
2325 BOOL UIUpdateChildWindows(BOOL bForceUpdate = FALSE)
2326 {
2327 if(!(m_wDirtyType & UPDUI_CHILDWINDOW) && !bForceUpdate)
2328 return TRUE;
2329
2330 const _AtlUpdateUIMap* pMap = m_pUIMap;
2331 _AtlUpdateUIData* pUIData = m_pUIData;
2332 if(pUIData == NULL)
2333 return FALSE;
2334
2335 while(pMap->m_nID != (WORD)-1)
2336 {
2337 for(int i = 0; i < m_UIElements.GetSize(); i++)
2338 {
2339 if(m_UIElements[i].m_wType == UPDUI_CHILDWINDOW)
2340 {
2341 if((pUIData->m_wState & UPDUI_CHILDWINDOW) && (pMap->m_wType & UPDUI_CHILDWINDOW))
2342 UIUpdateChildWindow(pMap->m_nID, pUIData, m_UIElements[i].m_hWnd);
2343 }
2344 }
2345 pMap++;
2346 pUIData->m_wState &= ~UPDUI_CHILDWINDOW;
2347 if(pUIData->m_wState & UPDUI_TEXT)
2348 {
2349 delete [] pUIData->m_lpstrText;
2350 pUIData->m_lpstrText = NULL;
2351 pUIData->m_wState &= ~UPDUI_TEXT;
2352 }
2353 pUIData++;
2354 }
2355
2356 m_wDirtyType &= ~UPDUI_CHILDWINDOW;
2357 return TRUE;
2358 }
2359
2360 // internal element specific methods
2361 static void UIUpdateMenuBarElement(int nID, _AtlUpdateUIData* pUIData, HMENU hMenu)
2362 {
2363 if((pUIData->m_wState & UPDUI_CLEARDEFAULT) != 0)
2364 {
2365 ::SetMenuDefaultItem(hMenu, (UINT)-1, 0);
2366 pUIData->m_wState &= ~UPDUI_CLEARDEFAULT;
2367 }
2368
2369 CMenuItemInfo mii;
2370 mii.fMask = MIIM_STATE;
2371 mii.wID = nID;
2372
2373 if((pUIData->m_wState & UPDUI_DISABLED) != 0)
2374 mii.fState |= MFS_DISABLED | MFS_GRAYED;
2375 else
2376 mii.fState |= MFS_ENABLED;
2377
2378 if((pUIData->m_wState & UPDUI_CHECKED) != 0)
2379 mii.fState |= MFS_CHECKED;
2380 else
2381 mii.fState |= MFS_UNCHECKED;
2382
2383 if((pUIData->m_wState & UPDUI_DEFAULT) != 0)
2384 mii.fState |= MFS_DEFAULT;
2385
2386 if((pUIData->m_wState & UPDUI_TEXT) != 0)
2387 {
2388 CMenuItemInfo miiNow;
2389 miiNow.fMask = MIIM_TYPE;
2390 miiNow.wID = nID;
2391 if(::GetMenuItemInfo(hMenu, nID, FALSE, &miiNow))
2392 {
2393 mii.fMask |= MIIM_TYPE;
2394 // MFT_BITMAP and MFT_SEPARATOR don't go together with MFT_STRING
2395 mii.fType |= (miiNow.fType & ~(MFT_BITMAP | MFT_SEPARATOR)) | MFT_STRING;
2396 mii.dwTypeData = pUIData->m_lpstrText;
2397 }
2398 }
2399
2400 ::SetMenuItemInfo(hMenu, nID, FALSE, &mii);
2401 }
2402
2403 static void UIUpdateToolBarElement(int nID, _AtlUpdateUIData* pUIData, HWND hWndToolBar)
2404 {
2405 // Note: only handles enabled/disabled, checked state, and radio (press)
2406 ::SendMessage(hWndToolBar, TB_ENABLEBUTTON, nID, (LPARAM)(pUIData->m_wState & UPDUI_DISABLED) ? FALSE : TRUE);
2407 ::SendMessage(hWndToolBar, TB_CHECKBUTTON, nID, (LPARAM)(pUIData->m_wState & UPDUI_CHECKED) ? TRUE : FALSE);
2408 ::SendMessage(hWndToolBar, TB_INDETERMINATE, nID, (LPARAM)(pUIData->m_wState & UPDUI_CHECKED2) ? TRUE : FALSE);
2409 ::SendMessage(hWndToolBar, TB_PRESSBUTTON, nID, (LPARAM)(pUIData->m_wState & UPDUI_RADIO) ? TRUE : FALSE);
2410 }
2411
2412 static void UIUpdateStatusBarElement(int nID, _AtlUpdateUIData* pUIData, HWND hWndStatusBar)
2413 {
2414 // Note: only handles text
2415 if(pUIData->m_wState & UPDUI_TEXT)
2416 ::SendMessage(hWndStatusBar, SB_SETTEXT, nID, (LPARAM)pUIData->m_lpstrText);
2417 }
2418
2419 static void UIUpdateChildWindow(int nID, _AtlUpdateUIData* pUIData, HWND hWnd)
2420 {
2421 HWND hChild = ::GetDlgItem(hWnd, nID);
2422
2423 ::EnableWindow(hChild, (pUIData->m_wState & UPDUI_DISABLED) ? FALSE : TRUE);
2424 // for check and radio, assume that window is a button
2425 int nCheck = BST_UNCHECKED;
2426 if((pUIData->m_wState & UPDUI_CHECKED) || (pUIData->m_wState & UPDUI_RADIO))
2427 nCheck = BST_CHECKED;
2428 else if(pUIData->m_wState & UPDUI_CHECKED2)
2429 nCheck = BST_INDETERMINATE;
2430 ::SendMessage(hChild, BM_SETCHECK, nCheck, 0L);
2431 if(pUIData->m_wState & UPDUI_DEFAULT)
2432 {
2433 DWORD dwRet = (DWORD)::SendMessage(hWnd, DM_GETDEFID, 0, 0L);
2434 if(HIWORD(dwRet) == DC_HASDEFID)
2435 {
2436 HWND hOldDef = ::GetDlgItem(hWnd, (int)(short)LOWORD(dwRet));
2437 // remove BS_DEFPUSHBUTTON
2438 ::SendMessage(hOldDef, BM_SETSTYLE, BS_PUSHBUTTON, MAKELPARAM(TRUE, 0));
2439 }
2440 ::SendMessage(hWnd, DM_SETDEFID, nID, 0L);
2441 }
2442 if(pUIData->m_wState & UPDUI_TEXT)
2443 ::SetWindowText(hChild, pUIData->m_lpstrText);
2444 }
2445 };
2446
2447 template <class T>
2448 class CUpdateUI : public CUpdateUIBase
2449 {
2450 public:
2451 CUpdateUI()
2452 {
2453 T* pT = static_cast<T*>(this);
2454 (void)pT; // avoid level 4 warning
2455 const _AtlUpdateUIMap* pMap = pT->GetUpdateUIMap();
2456 m_pUIMap = pMap;
2457 ATLASSERT(m_pUIMap != NULL);
2458 int nCount = 1;
2459 for( ; pMap->m_nID != (WORD)-1; nCount++)
2460 pMap++;
2461
2462 // check for duplicates (debug only)
2463 #ifdef _DEBUG
2464 for(int i = 0; i < nCount; i++)
2465 {
2466 for(int j = 0; j < nCount; j++)
2467 {
2468 // shouldn't have duplicates in the update UI map
2469 if(i != j)
2470 ATLASSERT(m_pUIMap[j].m_nID != m_pUIMap[i].m_nID);
2471 }
2472 }
2473 #endif // _DEBUG
2474
2475 ATLTRY(m_pUIData = new _AtlUpdateUIData[nCount]);
2476 ATLASSERT(m_pUIData != NULL);
2477
2478 if(m_pUIData != NULL)
2479 memset(m_pUIData, 0, sizeof(_AtlUpdateUIData) * nCount);
2480 }
2481 };
2482
2483
2484 ///////////////////////////////////////////////////////////////////////////////
2485 // CDynamicUpdateUI - allows update elements to dynamically added and removed
2486 // in addition to a static update UI map
2487
2488 template <class T>
2489 class CDynamicUpdateUI : public CUpdateUIBase
2490 {
2491 public:
2492 // Data members
2493 ATL::CSimpleArray<_AtlUpdateUIMap> m_arrUIMap; // copy of the static UI data
2494 ATL::CSimpleArray<_AtlUpdateUIData> m_arrUIData; // instance UI data
2495
2496 // Constructor/destructor
2497 CDynamicUpdateUI()
2498 {
2499 T* pT = static_cast<T*>(this);
2500 (void)pT; // avoid level 4 warning
2501 const _AtlUpdateUIMap* pMap = pT->GetUpdateUIMap();
2502 ATLASSERT(pMap != NULL);
2503
2504 for(;;)
2505 {
2506 BOOL bRet = m_arrUIMap.Add(*(_AtlUpdateUIMap*)pMap);
2507 ATLASSERT(bRet);
2508
2509 if(bRet != FALSE)
2510 {
2511 _AtlUpdateUIData data = { 0, NULL };
2512 bRet = m_arrUIData.Add(data);
2513 ATLASSERT(bRet);
2514 }
2515
2516 if(pMap->m_nID == (WORD)-1)
2517 break;
2518
2519 pMap++;
2520 }
2521
2522 ATLASSERT(m_arrUIMap.GetSize() == m_arrUIData.GetSize());
2523
2524 #ifdef _DEBUG
2525 // check for duplicates (debug only)
2526 for(int i = 0; i < m_arrUIMap.GetSize(); i++)
2527 {
2528 for(int j = 0; j < m_arrUIMap.GetSize(); j++)
2529 {
2530 // shouldn't have duplicates in the update UI map
2531 if(i != j)
2532 ATLASSERT(m_arrUIMap[j].m_nID != m_arrUIMap[i].m_nID);
2533 }
2534 }
2535 #endif // _DEBUG
2536
2537 // Set internal data pointers to point to the new data arrays
2538 m_pUIMap = m_arrUIMap.m_aT;
2539 m_pUIData = m_arrUIData.m_aT;
2540 }
2541
2542 ~CDynamicUpdateUI()
2543 {
2544 for(int i = 0; i < m_arrUIData.GetSize(); i++)
2545 {
2546 if((m_arrUIData[i].m_wState & UPDUI_TEXT) != 0)
2547 delete [] m_arrUIData[i].m_lpstrText;
2548 }
2549
2550 // Reset internal data pointers (memory will be released by CSimpleArray d-tor)
2551 m_pUIMap = NULL;
2552 m_pUIData = NULL;
2553 }
2554
2555 // Methods for dynamically adding and removing update elements
2556 bool UIAddUpdateElement(WORD nID, WORD wType)
2557 {
2558 // check for duplicates
2559 for(int i = 0; i < m_arrUIMap.GetSize(); i++)
2560 {
2561 // shouldn't have duplicates in the update UI map
2562 ATLASSERT(m_arrUIMap[i].m_nID != nID);
2563 if(m_arrUIMap[i].m_nID == nID)
2564 return false;
2565 }
2566
2567 bool bRetVal = false;
2568
2569 // Add new end element
2570 _AtlUpdateUIMap uumEnd = { (WORD)-1, 0 };
2571 BOOL bRet = m_arrUIMap.Add(uumEnd);
2572 ATLASSERT(bRet);
2573
2574 if(bRet != FALSE)
2575 {
2576 _AtlUpdateUIData uud = { 0, NULL };
2577 bRet = m_arrUIData.Add(uud);
2578 ATLASSERT(bRet);
2579
2580 // Set new data to the previous end element
2581 if(bRet != FALSE)
2582 {
2583 int nSize = m_arrUIMap.GetSize();
2584 _AtlUpdateUIMap uum = { nID, wType };
2585 m_arrUIMap.SetAtIndex(nSize - 2, uum);
2586 m_arrUIData.SetAtIndex(nSize - 2, uud);
2587
2588 // Set internal data pointers again, just in case that memory moved
2589 m_pUIMap = m_arrUIMap.m_aT;
2590 m_pUIData = m_arrUIData.m_aT;
2591
2592 bRetVal = true;
2593 }
2594 }
2595
2596 return bRetVal;
2597 }
2598
2599 bool UIRemoveUpdateElement(WORD nID)
2600 {
2601 bool bRetVal = false;
2602
2603 for(int i = 0; i < m_arrUIMap.GetSize(); i++)
2604 {
2605 if(m_arrUIMap[i].m_nID == nID)
2606 {
2607 if((m_arrUIData[i].m_wState & UPDUI_TEXT) != 0)
2608 delete [] m_arrUIData[i].m_lpstrText;
2609
2610 BOOL bRet = m_arrUIMap.RemoveAt(i);
2611 ATLASSERT(bRet);
2612 bRet = m_arrUIData.RemoveAt(i);
2613 ATLASSERT(bRet);
2614
2615 bRetVal = true;
2616 break;
2617 }
2618 }
2619
2620 return bRetVal;
2621 }
2622 };
2623
2624
2625 ///////////////////////////////////////////////////////////////////////////////
2626 // CAutoUpdateUI : Automatic mapping of UI elements
2627
2628 template <class T>
2629 class CAutoUpdateUI : public CDynamicUpdateUI<T>
2630 {
2631 public:
2632 LPCTSTR UIGetText(int nID)
2633 {
2634 for(int i = 0; i < this->m_arrUIMap.GetSize(); i++)
2635 {
2636 if(this->m_arrUIMap[i].m_nID == nID)
2637 return this->m_arrUIData[i].m_lpstrText;
2638 }
2639
2640 return NULL;
2641 }
2642
2643 // Element
2644 template <WORD t_wType>
2645 bool UIAddElement(UINT nID)
2646 {
2647 // check for existing UI map element
2648 for(int i = 0; i < this->m_arrUIMap.GetSize(); i++)
2649 {
2650 if(this->m_arrUIMap[i].m_nID == nID)
2651 {
2652 // set requested type
2653 this->m_arrUIMap[i].m_wType |= t_wType;
2654 return true;
2655 }
2656 }
2657
2658 // Add element to UI map with requested type
2659 return this->UIAddUpdateElement((WORD)nID, t_wType);
2660 }
2661
2662 template <WORD t_wType>
2663 bool UIRemoveElement(UINT nID)
2664 {
2665 for(int i = 0; i < this->m_arrUIMap.GetSize(); i++)
2666 {
2667 if(this->m_arrUIMap[i].m_nID == nID) // matching UI map element
2668 {
2669 WORD wType = this->m_arrUIMap[i].m_wType & ~t_wType;
2670 if (wType != 0) // has other types
2671 {
2672 this->m_arrUIMap[i].m_wType = wType; // keep other types
2673 return true;
2674 }
2675 else
2676 {
2677 return this->UIRemoveUpdateElement((WORD)nID);
2678 }
2679 }
2680 }
2681
2682 return false;
2683 }
2684
2685 // Menu
2686 bool UIAddMenu(HMENU hMenu, bool bSetText = false)
2687 {
2688 ATLASSERT(::IsMenu(hMenu));
2689 MENUITEMINFO mii = {sizeof(MENUITEMINFO), MIIM_TYPE | MIIM_ID | MIIM_SUBMENU};
2690
2691 // Complete the UI map
2692 for (INT uItem = 0; CMenuHandle(hMenu).GetMenuItemInfo(uItem, TRUE, &mii); uItem++)
2693 {
2694 if(mii.hSubMenu)
2695 {
2696 // Add submenu to UI map
2697 UIAddMenu(mii.hSubMenu, bSetText);
2698 }
2699 else if (mii.wID != 0)
2700 {
2701 // Add element to UI map
2702 UIAddElement<CDynamicUpdateUI<T>::UPDUI_MENUPOPUP>(mii.wID);
2703 if (bSetText)
2704 {
2705 TCHAR sText[64] = {};
2706 if (GetMenuString(hMenu, uItem, sText, 64, MF_BYPOSITION))
2707 this->UISetText(mii.wID, sText);
2708 }
2709 }
2710 }
2711
2712 return true;
2713 }
2714
2715 bool UIAddMenu(UINT uID, bool bSetText = false)
2716 {
2717 CMenu menu;
2718 ATLVERIFY(menu.LoadMenu(uID));
2719 return UIAddMenu(menu, bSetText);
2720 }
2721
2722 // ToolBar
2723 bool UIAddToolBar(HWND hWndToolBar)
2724 {
2725 ATLASSERT(::IsWindow(hWndToolBar));
2726 TBBUTTONINFO tbbi = { sizeof(TBBUTTONINFO), TBIF_COMMAND | TBIF_STYLE | TBIF_BYINDEX };
2727
2728 // Add toolbar buttons
2729 for (int uItem = 0; ::SendMessage(hWndToolBar, TB_GETBUTTONINFO, uItem, (LPARAM)&tbbi) != -1; uItem++)
2730 {
2731 if (tbbi.fsStyle ^ BTNS_SEP)
2732 UIAddElement<CDynamicUpdateUI<T>::UPDUI_TOOLBAR>(tbbi.idCommand);
2733 }
2734
2735 // Add embedded controls if any
2736 if (::GetWindow(hWndToolBar, GW_CHILD))
2737 UIAddChildWindowContainer(hWndToolBar);
2738
2739 return (CUpdateUIBase::UIAddToolBar(hWndToolBar) != FALSE);
2740 }
2741
2742 // Container
2743 bool UIAddChildWindowContainer(HWND hWnd)
2744 {
2745 ATLASSERT(::IsWindow(hWnd));
2746
2747 // Add children controls if any
2748 for (ATL::CWindow wCtl = ::GetWindow(hWnd, GW_CHILD); wCtl.IsWindow(); wCtl = wCtl.GetWindow(GW_HWNDNEXT))
2749 {
2750 int id = wCtl.GetDlgCtrlID();
2751 if(id != 0)
2752 UIAddElement<CDynamicUpdateUI<T>::UPDUI_CHILDWINDOW>(id);
2753 }
2754
2755 return (CUpdateUIBase::UIAddChildWindowContainer(hWnd) != FALSE);
2756 }
2757
2758 // StatusBar
2759 BOOL UIUpdateStatusBar(BOOL bForceUpdate = FALSE)
2760 {
2761 if(!(this->m_wDirtyType & CDynamicUpdateUI<T>::UPDUI_STATUSBAR) && !bForceUpdate)
2762 return TRUE;
2763
2764 for(int i = 0; i < this->m_arrUIMap.GetSize(); i++)
2765 {
2766 for(int e = 0; e < this->m_UIElements.GetSize(); e++)
2767 {
2768 if((this->m_UIElements[e].m_wType == CDynamicUpdateUI<T>::UPDUI_STATUSBAR) &&
2769 (this->m_arrUIMap[i].m_wType & CDynamicUpdateUI<T>::UPDUI_STATUSBAR) &&
2770 (this->m_arrUIData[i].m_wState & CDynamicUpdateUI<T>::UPDUI_STATUSBAR))
2771 {
2772 this->UIUpdateStatusBarElement(this->m_arrUIMap[i].m_nID, &this->m_arrUIData[i], this->m_UIElements[e].m_hWnd);
2773 this->m_arrUIData[i].m_wState &= ~CDynamicUpdateUI<T>::UPDUI_STATUSBAR;
2774 if(this->m_arrUIData[i].m_wState & CDynamicUpdateUI<T>::UPDUI_TEXT)
2775 this->m_arrUIData[i].m_wState &= ~CDynamicUpdateUI<T>::UPDUI_TEXT;
2776 }
2777 }
2778 }
2779
2780 this->m_wDirtyType &= ~CDynamicUpdateUI<T>::UPDUI_STATUSBAR;
2781 return TRUE;
2782 }
2783
2784 bool UIAddStatusBar(HWND hWndStatusBar, INT nPanes = 1)
2785 {
2786 ATLASSERT(::IsWindow(hWndStatusBar));
2787
2788 // Add StatusBar panes
2789 for (int iPane = 0; iPane < nPanes; iPane++)
2790 UIAddElement<CDynamicUpdateUI<T>::UPDUI_STATUSBAR>(ID_DEFAULT_PANE + iPane);
2791
2792 return (CUpdateUIBase::UIAddStatusBar(hWndStatusBar) != FALSE);
2793 }
2794
2795 // UI Map used if derived class has none
2796 BEGIN_UPDATE_UI_MAP(CAutoUpdateUI)
2797 END_UPDATE_UI_MAP()
2798 };
2799
2800
2801 ///////////////////////////////////////////////////////////////////////////////
2802 // CDialogResize - provides support for resizing dialog controls
2803 // (works for any window that has child controls)
2804
2805 // Put CDialogResize in the list of base classes for a dialog (or even plain window),
2806 // then implement DLGRESIZE map by specifying controls and groups of control
2807 // and using DLSZ_* values to specify how are they supposed to be resized.
2808 //
2809 // Notes:
2810 // - Resizeable border (WS_THICKFRAME style) should be set in the dialog template
2811 // for top level dialogs (popup or overlapped), so that users can resize the dialog.
2812 // - Some flags cannot be combined; for instance DLSZ_CENTER_X overrides DLSZ_SIZE_X,
2813 // DLSZ_SIZE_X overrides DLSZ_MOVE_X. X and Y flags can be combined.
2814 // - Order of controls is important - group controls are resized and moved based
2815 // on the position of the previous control in a group.
2816
2817 // dialog resize map macros
2818 struct _AtlDlgResizeMap
2819 {
2820 int m_nCtlID;
2821 DWORD m_dwResizeFlags;
2822 };
2823
2824 #define BEGIN_DLGRESIZE_MAP(thisClass) \
2825 static const WTL::_AtlDlgResizeMap* GetDlgResizeMap() \
2826 { \
2827 static const WTL::_AtlDlgResizeMap theMap[] = \
2828 {
2829
2830 #define END_DLGRESIZE_MAP() \
2831 { -1, 0 }, \
2832 }; \
2833 return theMap; \
2834 }
2835
2836 #define DLGRESIZE_CONTROL(id, flags) \
2837 { id, flags },
2838
2839 #define BEGIN_DLGRESIZE_GROUP() \
2840 { -1, _DLSZ_BEGIN_GROUP },
2841
2842 #define END_DLGRESIZE_GROUP() \
2843 { -1, _DLSZ_END_GROUP },
2844
2845
2846 template <class T>
2847 class CDialogResize
2848 {
2849 public:
2850 // Data declarations and members
2851 enum
2852 {
2853 DLSZ_SIZE_X = 0x00000001,
2854 DLSZ_SIZE_Y = 0x00000002,
2855 DLSZ_MOVE_X = 0x00000004,
2856 DLSZ_MOVE_Y = 0x00000008,
2857 DLSZ_REPAINT = 0x00000010,
2858 DLSZ_CENTER_X = 0x00000020,
2859 DLSZ_CENTER_Y = 0x00000040,
2860
2861 // internal use only
2862 _DLSZ_BEGIN_GROUP = 0x00001000,
2863 _DLSZ_END_GROUP = 0x00002000,
2864 _DLSZ_GRIPPER = 0x00004000
2865 };
2866
2867 struct _AtlDlgResizeData
2868 {
2869 int m_nCtlID;
2870 DWORD m_dwResizeFlags;
2871 RECT m_rect;
2872
2873 int GetGroupCount() const
2874 {
2875 return (int)LOBYTE(HIWORD(m_dwResizeFlags));
2876 }
2877
2878 void SetGroupCount(int nCount)
2879 {
2880 ATLASSERT((nCount > 0) && (nCount < 256));
2881 DWORD dwCount = (DWORD)MAKELONG(0, MAKEWORD(nCount, 0));
2882 m_dwResizeFlags &= 0xFF00FFFF;
2883 m_dwResizeFlags |= dwCount;
2884 }
2885
2886 bool operator ==(const _AtlDlgResizeData& r) const
2887 { return ((m_nCtlID == r.m_nCtlID) && (m_dwResizeFlags == r.m_dwResizeFlags)); }
2888 };
2889
2890 ATL::CSimpleArray<_AtlDlgResizeData> m_arrData;
2891 SIZE m_sizeDialog;
2892 POINT m_ptMinTrackSize;
2893 bool m_bGripper;
2894
2895
2896 // Constructor
2897 CDialogResize() : m_bGripper(false)
2898 {
2899 m_sizeDialog.cx = 0;
2900 m_sizeDialog.cy = 0;
2901 m_ptMinTrackSize.x = -1;
2902 m_ptMinTrackSize.y = -1;
2903 }
2904
2905 // Operations
2906 void DlgResize_Init(bool bAddGripper = true, bool bUseMinTrackSize = true, DWORD dwForceStyle = WS_CLIPCHILDREN)
2907 {
2908 T* pT = static_cast<T*>(this);
2909 ATLASSERT(::IsWindow(pT->m_hWnd));
2910
2911 DWORD dwStyle = pT->GetStyle();
2912
2913 #ifdef _DEBUG
2914 // Debug only: Check if top level dialogs have a resizeable border.
2915 if(((dwStyle & WS_CHILD) == 0) && ((dwStyle & WS_THICKFRAME) == 0))
2916 ATLTRACE2(atlTraceUI, 0, _T("DlgResize_Init - warning: top level dialog without the WS_THICKFRAME style - user cannot resize it\n"));
2917 #endif // _DEBUG
2918
2919 // Force specified styles (default WS_CLIPCHILDREN reduces flicker)
2920 if((dwStyle & dwForceStyle) != dwForceStyle)
2921 pT->ModifyStyle(0, dwForceStyle);
2922
2923 // Adding this style removes an empty icon that dialogs with WS_THICKFRAME have.
2924 // Setting icon to NULL is required when XP themes are active.
2925 // Note: This will not prevent adding an icon for the dialog using SetIcon()
2926 if((dwStyle & WS_CHILD) == 0)
2927 {
2928 pT->ModifyStyleEx(0, WS_EX_DLGMODALFRAME);
2929 if(pT->GetIcon(FALSE) == NULL)
2930 pT->SetIcon(NULL, FALSE);
2931 }
2932
2933 // Cleanup in case of multiple initialization
2934 // block: first check for the gripper control, destroy it if needed
2935 {
2936 ATL::CWindow wndGripper = pT->GetDlgItem(ATL_IDW_STATUS_BAR);
2937 if(wndGripper.IsWindow() && (m_arrData.GetSize() > 0) && (m_arrData[0].m_dwResizeFlags & _DLSZ_GRIPPER) != 0)
2938 wndGripper.DestroyWindow();
2939 }
2940 // clear out everything else
2941 m_arrData.RemoveAll();
2942 m_sizeDialog.cx = 0;
2943 m_sizeDialog.cy = 0;
2944 m_ptMinTrackSize.x = -1;
2945 m_ptMinTrackSize.y = -1;
2946
2947 // Get initial dialog client size
2948 RECT rectDlg = {};
2949 pT->GetClientRect(&rectDlg);
2950 m_sizeDialog.cx = rectDlg.right;
2951 m_sizeDialog.cy = rectDlg.bottom;
2952
2953 // Create gripper if requested
2954 m_bGripper = false;
2955 if(bAddGripper)
2956 {
2957 // shouldn't exist already
2958 ATLASSERT(!pT->GetDlgItem(ATL_IDW_STATUS_BAR).IsWindow());
2959 if(!pT->GetDlgItem(ATL_IDW_STATUS_BAR).IsWindow())
2960 {
2961 ATL::CWindow wndGripper;
2962 wndGripper.Create(_T("SCROLLBAR"), pT->m_hWnd, rectDlg, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SBS_SIZEBOX | SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN, 0, ATL_IDW_STATUS_BAR);
2963 ATLASSERT(wndGripper.IsWindow());
2964 if(wndGripper.IsWindow())
2965 {
2966 m_bGripper = true;
2967 RECT rectCtl = {};
2968 wndGripper.GetWindowRect(&rectCtl);
2969 ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rectCtl, 2);
2970 _AtlDlgResizeData data = { ATL_IDW_STATUS_BAR, DLSZ_MOVE_X | DLSZ_MOVE_Y | DLSZ_REPAINT | _DLSZ_GRIPPER, { rectCtl.left, rectCtl.top, rectCtl.right, rectCtl.bottom } };
2971 m_arrData.Add(data);
2972 }
2973 }
2974 }
2975
2976 // Get min track position if requested
2977 if(bUseMinTrackSize)
2978 {
2979 if((dwStyle & WS_CHILD) != 0)
2980 {
2981 RECT rect = {};
2982 pT->GetClientRect(&rect);
2983 m_ptMinTrackSize.x = rect.right - rect.left;
2984 m_ptMinTrackSize.y = rect.bottom - rect.top;
2985 }
2986 else
2987 {
2988 RECT rect = {};
2989 pT->GetWindowRect(&rect);
2990 m_ptMinTrackSize.x = rect.right - rect.left;
2991 m_ptMinTrackSize.y = rect.bottom - rect.top;
2992 }
2993 }
2994
2995 // Walk the map and initialize data
2996 const _AtlDlgResizeMap* pMap = pT->GetDlgResizeMap();
2997 ATLASSERT(pMap != NULL);
2998 int nGroupStart = -1;
2999 for(int nCount = 1; !((pMap->m_nCtlID == -1) && (pMap->m_dwResizeFlags == 0)); nCount++, pMap++)
3000 {
3001 if(pMap->m_nCtlID == -1)
3002 {
3003 switch(pMap->m_dwResizeFlags)
3004 {
3005 case _DLSZ_BEGIN_GROUP:
3006 ATLASSERT(nGroupStart == -1);
3007 nGroupStart = m_arrData.GetSize();
3008 break;
3009 case _DLSZ_END_GROUP:
3010 {
3011 ATLASSERT(nGroupStart != -1);
3012 int nGroupCount = m_arrData.GetSize() - nGroupStart;
3013 m_arrData[nGroupStart].SetGroupCount(nGroupCount);
3014 nGroupStart = -1;
3015 }
3016 break;
3017 default:
3018 ATLASSERT(FALSE && _T("Invalid DLGRESIZE Map Entry"));
3019 break;
3020 }
3021 }
3022 else
3023 {
3024 // this ID conflicts with the default gripper one
3025 ATLASSERT(m_bGripper ? (pMap->m_nCtlID != ATL_IDW_STATUS_BAR) : TRUE);
3026
3027 ATL::CWindow ctl = pT->GetDlgItem(pMap->m_nCtlID);
3028 ATLASSERT(ctl.IsWindow());
3029 RECT rectCtl = {};
3030 ctl.GetWindowRect(&rectCtl);
3031 ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rectCtl, 2);
3032
3033 DWORD dwGroupFlag = ((nGroupStart != -1) && (m_arrData.GetSize() == nGroupStart)) ? _DLSZ_BEGIN_GROUP : 0;
3034 _AtlDlgResizeData data = { pMap->m_nCtlID, pMap->m_dwResizeFlags | dwGroupFlag, { rectCtl.left, rectCtl.top, rectCtl.right, rectCtl.bottom } };
3035 m_arrData.Add(data);
3036 }
3037 }
3038 ATLASSERT((nGroupStart == -1) && _T("No End Group Entry in the DLGRESIZE Map"));
3039 }
3040
3041 void DlgResize_UpdateLayout(int cxWidth, int cyHeight)
3042 {
3043 T* pT = static_cast<T*>(this);
3044 ATLASSERT(::IsWindow(pT->m_hWnd));
3045
3046 // Restrict minimum size if requested
3047 if(((pT->GetStyle() & WS_CHILD) != 0) && (m_ptMinTrackSize.x != -1) && (m_ptMinTrackSize.y != -1))
3048 {
3049 if(cxWidth < m_ptMinTrackSize.x)
3050 cxWidth = m_ptMinTrackSize.x;
3051 if(cyHeight < m_ptMinTrackSize.y)
3052 cyHeight = m_ptMinTrackSize.y;
3053 }
3054
3055 BOOL bVisible = pT->IsWindowVisible();
3056 if(bVisible)
3057 pT->SetRedraw(FALSE);
3058
3059 for(int i = 0; i < m_arrData.GetSize(); i++)
3060 {
3061 if((m_arrData[i].m_dwResizeFlags & _DLSZ_BEGIN_GROUP) != 0) // start of a group
3062 {
3063 int nGroupCount = m_arrData[i].GetGroupCount();
3064 ATLASSERT((nGroupCount > 0) && ((i + nGroupCount - 1) < m_arrData.GetSize()));
3065 RECT rectGroup = m_arrData[i].m_rect;
3066
3067 int j = 1;
3068 for(j = 1; j < nGroupCount; j++)
3069 {
3070 rectGroup.left = __min(rectGroup.left, m_arrData[i + j].m_rect.left);
3071 rectGroup.top = __min(rectGroup.top, m_arrData[i + j].m_rect.top);
3072 rectGroup.right = __max(rectGroup.right, m_arrData[i + j].m_rect.right);
3073 rectGroup.bottom = __max(rectGroup.bottom, m_arrData[i + j].m_rect.bottom);
3074 }
3075
3076 for(j = 0; j < nGroupCount; j++)
3077 {
3078 _AtlDlgResizeData* pDataPrev = NULL;
3079 if(j > 0)
3080 pDataPrev = &(m_arrData[i + j - 1]);
3081 pT->DlgResize_PositionControl(cxWidth, cyHeight, rectGroup, m_arrData[i + j], true, pDataPrev);
3082 }
3083
3084 i += nGroupCount - 1; // increment to skip all group controls
3085 }
3086 else // one control entry
3087 {
3088 RECT rectGroup = {};
3089 pT->DlgResize_PositionControl(cxWidth, cyHeight, rectGroup, m_arrData[i], false);
3090 }
3091 }
3092
3093 if(bVisible)
3094 pT->SetRedraw(TRUE);
3095
3096 pT->RedrawWindow(NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
3097 }
3098
3099 // Message map and handlers
3100 BEGIN_MSG_MAP(CDialogResize)
3101 MESSAGE_HANDLER(WM_SIZE, OnSize)
3102 MESSAGE_HANDLER(WM_GETMINMAXINFO, OnGetMinMaxInfo)
3103 END_MSG_MAP()
3104
3105 LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
3106 {
3107 T* pT = static_cast<T*>(this);
3108 if(m_bGripper)
3109 {
3110 ATL::CWindow wndGripper = pT->GetDlgItem(ATL_IDW_STATUS_BAR);
3111 if(wParam == SIZE_MAXIMIZED)
3112 wndGripper.ShowWindow(SW_HIDE);
3113 else if(wParam == SIZE_RESTORED)
3114 wndGripper.ShowWindow(SW_SHOW);
3115 }
3116 if(wParam != SIZE_MINIMIZED)
3117 {
3118 ATLASSERT(::IsWindow(pT->m_hWnd));
3119 pT->DlgResize_UpdateLayout(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
3120 }
3121 return 0;
3122 }
3123
3124 LRESULT OnGetMinMaxInfo(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
3125 {
3126 if((m_ptMinTrackSize.x != -1) && (m_ptMinTrackSize.y != -1))
3127 {
3128 LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;
3129 lpMMI->ptMinTrackSize = m_ptMinTrackSize;
3130 }
3131 return 0;
3132 }
3133
3134 // Implementation
3135 bool DlgResize_PositionControl(int cxWidth, int cyHeight, RECT& rectGroup, _AtlDlgResizeData& data, bool bGroup,
3136 _AtlDlgResizeData* pDataPrev = NULL)
3137 {
3138 T* pT = static_cast<T*>(this);
3139 ATLASSERT(::IsWindow(pT->m_hWnd));
3140 ATL::CWindow ctl;
3141 RECT rectCtl = {};
3142
3143 ctl = pT->GetDlgItem(data.m_nCtlID);
3144 if(!ctl.GetWindowRect(&rectCtl))
3145 return false;
3146 ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rectCtl, 2);
3147
3148 if(bGroup)
3149 {
3150 if((data.m_dwResizeFlags & DLSZ_CENTER_X) != 0)
3151 {
3152 int cxRight = rectGroup.right + cxWidth - m_sizeDialog.cx;
3153 int cxCtl = data.m_rect.right - data.m_rect.left;
3154 rectCtl.left = rectGroup.left + (cxRight - rectGroup.left - cxCtl) / 2;
3155 rectCtl.right = rectCtl.left + cxCtl;
3156 }
3157 else if((data.m_dwResizeFlags & (DLSZ_SIZE_X | DLSZ_MOVE_X)) != 0)
3158 {
3159 rectCtl.left = rectGroup.left + ::MulDiv(data.m_rect.left - rectGroup.left, rectGroup.right - rectGroup.left + (cxWidth - m_sizeDialog.cx), rectGroup.right - rectGroup.left);
3160
3161 if((data.m_dwResizeFlags & DLSZ_SIZE_X) != 0)
3162 {
3163 rectCtl.right = rectGroup.left + ::MulDiv(data.m_rect.right - rectGroup.left, rectGroup.right - rectGroup.left + (cxWidth - m_sizeDialog.cx), rectGroup.right - rectGroup.left);
3164
3165 if(pDataPrev != NULL)
3166 {
3167 ATL::CWindow ctlPrev = pT->GetDlgItem(pDataPrev->m_nCtlID);
3168 RECT rcPrev = {};
3169 ctlPrev.GetWindowRect(&rcPrev);
3170 ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rcPrev, 2);
3171 int dxAdjust = (rectCtl.left - rcPrev.right) - (data.m_rect.left - pDataPrev->m_rect.right);
3172 rcPrev.right += dxAdjust;
3173 ctlPrev.SetWindowPos(NULL, &rcPrev, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
3174 }
3175 }
3176 else
3177 {
3178 rectCtl.right = rectCtl.left + (data.m_rect.right - data.m_rect.left);
3179 }
3180 }
3181
3182 if((data.m_dwResizeFlags & DLSZ_CENTER_Y) != 0)
3183 {
3184 int cyBottom = rectGroup.bottom + cyHeight - m_sizeDialog.cy;
3185 int cyCtl = data.m_rect.bottom - data.m_rect.top;
3186 rectCtl.top = rectGroup.top + (cyBottom - rectGroup.top - cyCtl) / 2;
3187 rectCtl.bottom = rectCtl.top + cyCtl;
3188 }
3189 else if((data.m_dwResizeFlags & (DLSZ_SIZE_Y | DLSZ_MOVE_Y)) != 0)
3190 {
3191 rectCtl.top = rectGroup.top + ::MulDiv(data.m_rect.top - rectGroup.top, rectGroup.bottom - rectGroup.top + (cyHeight - m_sizeDialog.cy), rectGroup.bottom - rectGroup.top);
3192
3193 if((data.m_dwResizeFlags & DLSZ_SIZE_Y) != 0)
3194 {
3195 rectCtl.bottom = rectGroup.top + ::MulDiv(data.m_rect.bottom - rectGroup.top, rectGroup.bottom - rectGroup.top + (cyHeight - m_sizeDialog.cy), rectGroup.bottom - rectGroup.top);
3196
3197 if(pDataPrev != NULL)
3198 {
3199 ATL::CWindow ctlPrev = pT->GetDlgItem(pDataPrev->m_nCtlID);
3200 RECT rcPrev = {};
3201 ctlPrev.GetWindowRect(&rcPrev);
3202 ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rcPrev, 2);
3203 int dxAdjust = (rectCtl.top - rcPrev.bottom) - (data.m_rect.top - pDataPrev->m_rect.bottom);
3204 rcPrev.bottom += dxAdjust;
3205 ctlPrev.SetWindowPos(NULL, &rcPrev, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
3206 }
3207 }
3208 else
3209 {
3210 rectCtl.bottom = rectCtl.top + (data.m_rect.bottom - data.m_rect.top);
3211 }
3212 }
3213 }
3214 else // no group
3215 {
3216 if((data.m_dwResizeFlags & DLSZ_CENTER_X) != 0)
3217 {
3218 int cxCtl = data.m_rect.right - data.m_rect.left;
3219 rectCtl.left = (cxWidth - cxCtl) / 2;
3220 rectCtl.right = rectCtl.left + cxCtl;
3221 }
3222 else if((data.m_dwResizeFlags & (DLSZ_SIZE_X | DLSZ_MOVE_X)) != 0)
3223 {
3224 rectCtl.right = data.m_rect.right + (cxWidth - m_sizeDialog.cx);
3225
3226 if((data.m_dwResizeFlags & DLSZ_MOVE_X) != 0)
3227 rectCtl.left = rectCtl.right - (data.m_rect.right - data.m_rect.left);
3228 }
3229
3230 if((data.m_dwResizeFlags & DLSZ_CENTER_Y) != 0)
3231 {
3232 int cyCtl = data.m_rect.bottom - data.m_rect.top;
3233 rectCtl.top = (cyHeight - cyCtl) / 2;
3234 rectCtl.bottom = rectCtl.top + cyCtl;
3235 }
3236 else if((data.m_dwResizeFlags & (DLSZ_SIZE_Y | DLSZ_MOVE_Y)) != 0)
3237 {
3238 rectCtl.bottom = data.m_rect.bottom + (cyHeight - m_sizeDialog.cy);
3239
3240 if((data.m_dwResizeFlags & DLSZ_MOVE_Y) != 0)
3241 rectCtl.top = rectCtl.bottom - (data.m_rect.bottom - data.m_rect.top);
3242 }
3243 }
3244
3245 if((data.m_dwResizeFlags & DLSZ_REPAINT) != 0)
3246 ctl.Invalidate();
3247
3248 if((data.m_dwResizeFlags & (DLSZ_SIZE_X | DLSZ_SIZE_Y | DLSZ_MOVE_X | DLSZ_MOVE_Y | DLSZ_REPAINT | DLSZ_CENTER_X | DLSZ_CENTER_Y)) != 0)
3249 ctl.SetWindowPos(NULL, &rectCtl, SWP_NOZORDER | SWP_NOACTIVATE);
3250
3251 return true;
3252 }
3253 };
3254
3255
3256 ///////////////////////////////////////////////////////////////////////////////
3257 // CDynamicDialogLayout - support for dialog dynamic layout resource info
3258 // (AFX_DIALOG_LAYOUT) in VS2015 and higher
3259
3260 #if (_MSC_VER >= 1900)
3261
3262 template<class T>
3263 class CDynamicDialogLayout
3264 {
3265 public:
3266 // Data declarations
3267 struct _AtlDynamicLayoutData
3268 {
3269 HWND m_hWnd;
3270 char m_nMoveRatioX;
3271 char m_nMoveRatioY;
3272 char m_nSizeRatioX;
3273 char m_nSizeRatioY;
3274 RECT m_rcInit;
3275 };
3276
3277 // Data members
3278 ATL::CSimpleArray<_AtlDynamicLayoutData> m_arrLayoutData;
3279 SIZE m_szParentInit;
3280 POINT m_ptMinTrackSize;
3281 bool m_bGripper;
3282
3283 // Constructor
3284 CDynamicDialogLayout() : m_bGripper(false)
3285 {
3286 m_szParentInit.cx = 0;
3287 m_szParentInit.cy = 0;
3288 m_ptMinTrackSize.x = -1;
3289 m_ptMinTrackSize.y = -1;
3290 }
3291
3292 // Methods
3293 void InitDynamicLayout(bool bAddGripper = true, bool bMinTrackSize = true)
3294 {
3295 T* pT = static_cast<T*>(this);
3296 ATLASSERT(::IsWindow(pT->m_hWnd));
3297
3298 // Cleanup in case of multiple initialization
3299 // block: first check for the gripper control, destroy it if needed
3300 {
3301 ATL::CWindow wndGripper = pT->GetDlgItem(ATL_IDW_STATUS_BAR);
3302 if(wndGripper.IsWindow() != FALSE)
3303 wndGripper.DestroyWindow();
3304 }
3305 // clear out everything else
3306 m_arrLayoutData.RemoveAll();
3307 m_ptMinTrackSize.x = -1;
3308 m_ptMinTrackSize.y = -1;
3309 m_szParentInit.cx = 0;
3310 m_szParentInit.cy = 0;
3311 m_bGripper = false;
3312
3313 CResource rcLayout;
3314 if(rcLayout.Load(_T("AFX_DIALOG_LAYOUT"), pT->IDD))
3315 {
3316 int nCount = rcLayout.GetSize() / sizeof(WORD);
3317 if(nCount > 1)
3318 {
3319 RECT rcParent = {};
3320 pT->GetWindowRect(&rcParent);
3321 m_szParentInit.cx = rcParent.right - rcParent.left;
3322 m_szParentInit.cy = rcParent.bottom - rcParent.top;
3323
3324 WORD* pnData = (WORD*)rcLayout.Lock();
3325 WORD wVersion = *pnData; // AFX_DIALOG_LAYOUT version
3326 ATLASSERT(wVersion == 0);
3327 if(wVersion == 0)
3328 {
3329 pnData++; // skip version
3330 ATL::CWindow wndChild = pT->GetWindow(GW_CHILD);
3331 for(int i = 0; (i < nCount) && (wndChild.m_hWnd != NULL); i += 4)
3332 {
3333 char nMoveRatioX = _GetDataPct(pnData[i]);
3334 char nMoveRatioY = _GetDataPct(pnData[i + 1]);
3335 char nSizeRatioX = _GetDataPct(pnData[i + 2]);
3336 char nSizeRatioY = _GetDataPct(pnData[i + 3]);
3337
3338 bool bValid = ((nMoveRatioX != -1) && (nMoveRatioY != -1) && (nSizeRatioX != -1) && (nSizeRatioY != -1));
3339 bool bAction = ((nMoveRatioX != 0) || (nMoveRatioY != 0) || (nSizeRatioX != 0) || (nSizeRatioY != 0));
3340 if(bValid && bAction)
3341 {
3342 _AtlDynamicLayoutData LayoutData = { wndChild.m_hWnd, nMoveRatioX, nMoveRatioY, nSizeRatioX, nSizeRatioY };
3343 wndChild.GetWindowRect(&LayoutData.m_rcInit);
3344 pT->ScreenToClient(&LayoutData.m_rcInit);
3345 m_arrLayoutData.Add(LayoutData);
3346 }
3347
3348 wndChild = wndChild.GetWindow(GW_HWNDNEXT);
3349 }
3350 }
3351
3352 rcLayout.Release();
3353 }
3354 }
3355
3356 if(bAddGripper)
3357 {
3358 RECT rcDialog = {};
3359 pT->GetClientRect(&rcDialog);
3360
3361 ATL::CWindow wndGripper = pT->GetDlgItem(ATL_IDW_STATUS_BAR);
3362 if(wndGripper.m_hWnd != NULL)
3363 wndGripper.DestroyWindow();
3364
3365 wndGripper.Create(_T("SCROLLBAR"), pT->m_hWnd, rcDialog, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SBS_SIZEBOX | SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN, 0, ATL_IDW_STATUS_BAR);
3366 ATLASSERT(wndGripper.m_hWnd != NULL);
3367 if(wndGripper.m_hWnd != NULL)
3368 {
3369 m_bGripper = true;
3370 _AtlDynamicLayoutData LayoutData = { wndGripper.m_hWnd, 100, 100, 0, 0 };
3371 wndGripper.GetWindowRect(&LayoutData.m_rcInit);
3372 pT->ScreenToClient(&LayoutData.m_rcInit);
3373 m_arrLayoutData.Add(LayoutData);
3374 }
3375 }
3376
3377 if(bMinTrackSize)
3378 {
3379 RECT rcMinTrack = {};
3380 if((pT->GetStyle() & WS_CHILD) != 0)
3381 pT->GetClientRect(&rcMinTrack);
3382 else
3383 pT->GetWindowRect(&rcMinTrack);
3384
3385 m_ptMinTrackSize.x = rcMinTrack.right - rcMinTrack.left;
3386 m_ptMinTrackSize.y = rcMinTrack.bottom - rcMinTrack.top;
3387 }
3388 }
3389
3390 void UpdateDynamicLayout()
3391 {
3392 T* pT = static_cast<T*>(this);
3393 ATLASSERT(::IsWindow(pT->m_hWnd));
3394
3395 int nCount = m_arrLayoutData.GetSize();
3396 if(nCount == 0)
3397 return;
3398
3399 RECT rcParent = {};
3400 pT->GetWindowRect(&rcParent);
3401
3402 int nDeltaWidth = rcParent.right - rcParent.left - m_szParentInit.cx;
3403 int nDeltaHeight = rcParent.bottom - rcParent.top - m_szParentInit.cy;
3404
3405 HDWP hDwp = ::BeginDeferWindowPos(nCount);
3406
3407 for(int i = 0; i < nCount; i++)
3408 {
3409 _AtlDynamicLayoutData& LayoutData = m_arrLayoutData[i];
3410
3411 ATLASSERT(::IsWindow(LayoutData.m_hWnd) != FALSE);
3412
3413 int nID = ::GetDlgCtrlID(LayoutData.m_hWnd);
3414 UINT nFlags = (SWP_NOMOVE | SWP_NOSIZE);
3415 RECT rcItem = LayoutData.m_rcInit;
3416
3417 if(((nID == ATL_IDW_STATUS_BAR) || (nDeltaWidth >= 0)) && (LayoutData.m_nMoveRatioX != 0))
3418 {
3419 rcItem.left += ::MulDiv(nDeltaWidth, LayoutData.m_nMoveRatioX, 100);
3420 rcItem.right += ::MulDiv(nDeltaWidth, LayoutData.m_nMoveRatioX, 100);
3421 nFlags &= ~SWP_NOMOVE;
3422 }
3423
3424 if(((nID == ATL_IDW_STATUS_BAR) || (nDeltaHeight >= 0)) && (LayoutData.m_nMoveRatioY != 0))
3425 {
3426 rcItem.top += ::MulDiv(nDeltaHeight, LayoutData.m_nMoveRatioY, 100);
3427 rcItem.bottom += ::MulDiv(nDeltaHeight, LayoutData.m_nMoveRatioY, 100);
3428 nFlags &= ~SWP_NOMOVE;
3429 }
3430
3431 if((nDeltaWidth >= 0) && (LayoutData.m_nSizeRatioX != 0))
3432 {
3433 rcItem.right += ::MulDiv(nDeltaWidth, LayoutData.m_nSizeRatioX, 100);
3434 nFlags &= ~SWP_NOSIZE;
3435 }
3436
3437 if((nDeltaHeight >= 0) && (LayoutData.m_nSizeRatioY != 0))
3438 {
3439 rcItem.bottom += ::MulDiv(nDeltaHeight, LayoutData.m_nSizeRatioY, 100);
3440 nFlags &= ~SWP_NOSIZE;
3441 }
3442
3443 if(nFlags != (SWP_NOMOVE | SWP_NOSIZE))
3444 ::DeferWindowPos(hDwp, LayoutData.m_hWnd, NULL, rcItem.left, rcItem.top, rcItem.right - rcItem.left, rcItem.bottom - rcItem.top, nFlags | SWP_NOZORDER | SWP_NOREPOSITION | SWP_NOACTIVATE | SWP_NOCOPYBITS);
3445 }
3446
3447 ::EndDeferWindowPos(hDwp);
3448 }
3449
3450 // Message map and handlers
3451 BEGIN_MSG_MAP(CDynamicDialogLayout)
3452 MESSAGE_HANDLER(WM_SIZE, OnSize)
3453 MESSAGE_HANDLER(WM_GETMINMAXINFO, OnGetMinMaxInfo)
3454 END_MSG_MAP()
3455
3456 LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
3457 {
3458 T* pT = static_cast<T*>(this);
3459
3460 if(m_bGripper)
3461 {
3462 ATL::CWindow wndGripper = pT->GetDlgItem(ATL_IDW_STATUS_BAR);
3463 if(wndGripper.m_hWnd != NULL)
3464 {
3465 if(wParam == SIZE_MAXIMIZED)
3466 wndGripper.ShowWindow(SW_HIDE);
3467 else if(wParam == SIZE_RESTORED)
3468 wndGripper.ShowWindow(SW_SHOW);
3469 }
3470 }
3471
3472 if(wParam != SIZE_MINIMIZED)
3473 pT->UpdateDynamicLayout();
3474
3475 return 0;
3476 }
3477
3478 LRESULT OnGetMinMaxInfo(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
3479 {
3480 if((m_ptMinTrackSize.x != -1) && (m_ptMinTrackSize.y != -1))
3481 {
3482 LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;
3483 lpMMI->ptMinTrackSize = m_ptMinTrackSize;
3484 }
3485
3486 return 0;
3487 }
3488
3489 // Implementation
3490 char _GetDataPct(WORD wResData)
3491 {
3492 ATLASSERT((wResData >= 0) && (wResData <= 100));
3493 char nPct = (char)LOBYTE(wResData);
3494 if((nPct < 0) || (nPct > 100))
3495 nPct = -1;
3496
3497 return nPct;
3498 }
3499 };
3500
3501 #endif // (_MSC_VER >= 1900)
3502
3503
3504 ///////////////////////////////////////////////////////////////////////////////
3505 // CDoubleBufferImpl - Provides double-buffer painting support to any window
3506
3507 template <class T>
3508 class CDoubleBufferImpl
3509 {
3510 public:
3511 // Overrideables
3512 void DoPaint(CDCHandle /*dc*/)
3513 {
3514 // must be implemented in a derived class
3515 ATLASSERT(FALSE);
3516 }
3517
3518 // Message map and handlers
3519 BEGIN_MSG_MAP(CDoubleBufferImpl)
3520 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
3521 MESSAGE_HANDLER(WM_PAINT, OnPaint)
3522 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
3523 END_MSG_MAP()
3524
3525 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
3526 {
3527 return 1; // no background painting needed
3528 }
3529
3530 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
3531 {
3532 T* pT = static_cast<T*>(this);
3533 ATLASSERT(::IsWindow(pT->m_hWnd));
3534
3535 if(wParam != NULL)
3536 {
3537 RECT rect = {};
3538 pT->GetClientRect(&rect);
3539 CMemoryDC dcMem((HDC)wParam, rect);
3540 pT->DoPaint(dcMem.m_hDC);
3541 }
3542 else
3543 {
3544 CPaintDC dc(pT->m_hWnd);
3545 CMemoryDC dcMem(dc.m_hDC, dc.m_ps.rcPaint);
3546 pT->DoPaint(dcMem.m_hDC);
3547 }
3548
3549 return 0;
3550 }
3551 };
3552
3553
3554 ///////////////////////////////////////////////////////////////////////////////
3555 // CDoubleBufferWindowImpl - Implements a double-buffer painting window
3556
3557 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
3558 class ATL_NO_VTABLE CDoubleBufferWindowImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CDoubleBufferImpl< T >
3559 {
3560 public:
3561 BEGIN_MSG_MAP(CDoubleBufferWindowImpl)
3562 CHAIN_MSG_MAP(CDoubleBufferImpl< T >)
3563 END_MSG_MAP()
3564 };
3565
3566
3567 // command bar support
3568 #if !defined(__ATLCTRLW_H__)
3569 #undef CBRM_GETMENU
3570 #undef CBRM_TRACKPOPUPMENU
3571 #undef CBRM_GETCMDBAR
3572 #undef CBRPOPUPMENU
3573 #endif // !defined(__ATLCTRLW_H__)
3574
3575 } // namespace WTL
3576
3577 #endif // __ATLFRAME_H__