-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdialogsizer_set.cpp
428 lines (359 loc) · 11.8 KB
/
dialogsizer_set.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
/*----------------------------------------------------------------------
Copyright (c) Gipsysoft. All Rights Reserved.
File: DialogSizer_Set.cpp
Web site: http://gipsysoft.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the author be held liable for any damages arising from the
use of this software.
Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject
to the following restrictions:
1) The origin of this software must not be misrepresented; you must not claim
that you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation is requested but not required.
2) Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software. Altered source is encouraged
to be submitted back to the original author so it can be shared with the
community. Please share your changes.
3) This notice may not be removed or altered from any source distribution.
Owner: [email protected]
Purpose: Main functionality for sizeable dialogs
Store a local copy of the user settings
Subclass the window
Respond to various messages withinn the subclassed window.
----------------------------------------------------------------------*/
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
#include <commctrl.h>
#include "DialogSizer.h"
static LPCTSTR pcszDialogDataPropertyName = _T("SizerProperty");
static LPCTSTR pcszWindowProcPropertyName = _T("SizerWindowProc");
extern long RegQueryValueExRecursive(HKEY hKey, LPCTSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);
extern long RegSetValueExRecursive(HKEY hKey, LPCTSTR lpValueName, DWORD Reserved, DWORD dwType, CONST BYTE *lpData, DWORD cbData);
WINDOWPLACEMENT m_wpl;
struct DialogData // dd
{
//
// The number of items contained in the psd member.
// Used in the DeferWindowPos structure and in allocating memory
int nItemCount;
DialogSizerSizingItem *psd;
//
// We need the smallest to respond to the WM_GETMINMAXINFO message
POINT m_ptSmallest;
//
// We don't strictly speaking need to say how big the biggest can be but
POINT m_ptLargest;
bool m_bLargestSet;
//
// we need this to decide how much the window has changed size when we get a WM_SIZE message
SIZE m_sizeClient;
//
// Draw the sizing grip...or not
bool m_bMaximised;
BOOL m_bShowSizingGrip;
RECT rcGrip;
};
static LRESULT CALLBACK SizingProc(HWND, UINT, WPARAM, LPARAM);
static inline int GetItemCount(const DialogSizerSizingItem *psd)
//
// Given an array of dialog item structures determine how many of them there
// are by scanning along them until I reach the last.
{
int nCount = 0;
while (psd->uSizeInfo != UINT_MAX)
{
nCount++;
psd++;
}
return nCount;
}
static void UpdateGripperRect(const int cx, const int cy, RECT *rcGrip)
{
const int nGripWidth = GetSystemMetrics(SM_CYVSCROLL);
const int nGripHeight = GetSystemMetrics(SM_CXVSCROLL);
rcGrip->left = cx - nGripWidth;
rcGrip->top = cy - nGripHeight;
rcGrip->right = cx;
rcGrip->bottom = cy;
}
static void UpdateGripper(HWND hwnd, DialogData *pdd)
{
if (pdd->m_bShowSizingGrip)
{
RECT old;
old = pdd->rcGrip;
UpdateGripperRect(pdd->m_sizeClient.cx, pdd->m_sizeClient.cy, &pdd->rcGrip);
//
// We also need to invalidate the combined area of the old and new rectangles
// otherwise we would have trail of grippers when we sized the dialog larger
// in any axis
(void)UnionRect(&old, &old, &pdd->rcGrip);
(void)InvalidateRect(hwnd, &old, TRUE);
}
}
static inline void CopyItems(DialogSizerSizingItem *psdDest, const DialogSizerSizingItem *psdSource)
//
// Will copy all of the items in psdSource into psdDest.
{
//
// Loop til we reach the end
while (psdSource->uSizeInfo != UINT_MAX)
{
*psdDest = *psdSource;
psdDest++;
psdSource++;
}
// And when we do copy the last item
*psdDest = *psdSource;
}
static DialogData *AddDialogData(HWND hwnd)
//
// Firstly determine if the data already exists, if it does then return that, if not then we will
// create and initialise a brand new structure.
{
DialogData *pdd = reinterpret_cast<DialogData *>(GetProp(hwnd, pcszDialogDataPropertyName));
if (!pdd)
{
pdd = reinterpret_cast<DialogData *>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DialogData)));
}
if (pdd)
{
//
// Store some sizes etc. for later.
RECT rc;
GetWindowRect(hwnd, &rc);
pdd->m_ptSmallest.x = rc.right - rc.left;
pdd->m_ptSmallest.y = rc.bottom - rc.top;
GetClientRect(hwnd, &rc);
pdd->m_sizeClient.cx = rc.right - rc.left;
pdd->m_sizeClient.cy = rc.bottom - rc.top;
SetProp(hwnd, pcszDialogDataPropertyName, reinterpret_cast<HANDLE>(pdd));
UpdateGripperRect(pdd->m_sizeClient.cx, pdd->m_sizeClient.cy, &pdd->rcGrip);
//
// Because we have successffuly created our data we need to subclass the control now, if not
// we could end up in a situation where our data was never freed.
SetProp(hwnd, pcszWindowProcPropertyName, reinterpret_cast<HANDLE>(SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SizingProc))));
}
return pdd;
}
extern "C"
{
BOOL DialogSizer_Set(HWND hwnd, const DialogSizerSizingItem *psd, BOOL bShowSizingGrip, SIZE *psizeMax)
//
// Setting a dialog sizeable involves subclassing the window and handling it's
// WM_SIZE messages, if we have a hkRootSave and pcszName then we will also be loading/saving
// the size and position of the window from the registry. We load from the registry when we
// subclass the window and we save to the registry when we get a WM_DESTROY.
//
// It will return non-zero for success and zero if it fails
{
//
// Make sure all of the parameters are valid.
if (IsWindow (hwnd))
{
HANDLE heap = GetProcessHeap();
DialogData *pdd = AddDialogData(hwnd);
if (pdd)
{
pdd->m_bShowSizingGrip = bShowSizingGrip;
pdd->nItemCount = GetItemCount(psd) + 1;
pdd->psd = reinterpret_cast<DialogSizerSizingItem *>(HeapAlloc(heap, 0, sizeof(DialogSizerSizingItem) * pdd->nItemCount));
if (pdd->psd)
{
//
// Copy all of the user controls etc. for later, this way the user can quite happily
// let the structure go out of scope.
CopyItems(pdd->psd, psd);
if (psizeMax)
{
pdd->m_ptLargest.x = psizeMax->cx;
pdd->m_ptLargest.y = psizeMax->cy;
pdd->m_bLargestSet = true;
}
//
// If the there was save info passed in then we need to make damn good use of it
// by attempting to load the RegistryData structure
/*if( hkRootSave && pcszName )
{
WINDOWPLACEMENT rd;
DWORD dwSize = sizeof( rd );
DWORD dwType = REG_BINARY;
if( RegQueryValueExRecursive( hkRootSave, pcszName, NULL, &dwType, reinterpret_cast<LPBYTE>( &rd ), &dwSize ) == ERROR_SUCCESS && dwSize == sizeof( rd ) )
{
if( !(GetWindowStyle( hwnd ) & WS_VISIBLE) )
rd.showCmd = SW_HIDE;
SetWindowPlacement( hwnd, &rd );
}
}*/
return TRUE;
}
else
{
HeapFree(heap, 0, pdd);
}
}
}
return FALSE;
}
}
void UpdateWindowSize(const int cx, const int cy, HWND hwnd)
{
DialogData *pdd = reinterpret_cast<DialogData *>(GetProp(hwnd, pcszDialogDataPropertyName));
if (pdd)
{
const int nDeltaX = cx - pdd->m_sizeClient.cx;
const int nDeltaY = cy - pdd->m_sizeClient.cy;
HDWP defer;
defer = BeginDeferWindowPos (pdd->nItemCount);
RECT rc;
const DialogSizerSizingItem *psd = pdd->psd;
if (!nDeltaX && !nDeltaY)
return;
while (psd->uSizeInfo != UINT_MAX)
{
HWND hwndChild = GetDlgItem(hwnd, psd->uControlID);
if (!hwndChild)
{
int err = GetLastError ();
psd++;
continue;
}
GetWindowRect(hwndChild, &rc);
(void)MapWindowPoints(GetDesktopWindow(), hwnd, (LPPOINT)&rc, 2);
//
// Adjust the window horizontally
if (psd->uSizeInfo & DS_MoveX)
{
rc.left += nDeltaX;
rc.right += nDeltaX;
}
//
// Adjust the window vertically
if (psd->uSizeInfo & DS_MoveY)
{
rc.top += nDeltaY;
rc.bottom += nDeltaY;
}
//
// Size the window horizontally
if (psd->uSizeInfo & DS_SizeX)
{
rc.right += nDeltaX;
}
//
// Size the window vertically
if (psd->uSizeInfo & DS_SizeY)
{
rc.bottom += nDeltaY;
}
DeferWindowPos(defer, hwndChild, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
psd++;
}
EndDeferWindowPos (defer);
pdd->m_sizeClient.cx = cx;
pdd->m_sizeClient.cy = cy;
//
// If we have a sizing grip enabled then adjust it's position
UpdateGripper(hwnd, pdd);
//UpdateWindow (hwnd);
}
}
static LRESULT CALLBACK SizingProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
//
// Actual window procedure that will handle saving window size/position and moving
// the controls whilst the window sizes.
{
WNDPROC pOldProc = reinterpret_cast<WNDPROC>(GetProp(hwnd, pcszWindowProcPropertyName));
switch (msg)
{
case WM_ERASEBKGND:
{
LRESULT lr = CallWindowProc(pOldProc, hwnd, msg, wParam, lParam);
DialogData *pdd = reinterpret_cast<DialogData *>(GetProp(hwnd, pcszDialogDataPropertyName));
if (pdd && pdd->m_bShowSizingGrip && !pdd->m_bMaximised)
{
::DrawFrameControl(reinterpret_cast<HDC>(wParam), &pdd->rcGrip, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
}
return lr;
}
case WM_SIZE:
{
DialogData *pdd = reinterpret_cast<DialogData *>(GetProp(hwnd, pcszDialogDataPropertyName));
if (pdd && wParam != SIZE_MINIMIZED)
{
pdd->m_bMaximised = (wParam == SIZE_MAXIMIZED ? true : false);
UpdateWindowSize(LOWORD(lParam), HIWORD(lParam), hwnd);
//InvalidateRect (hwnd, NULL, TRUE);
}
}
break;
case WM_NCHITTEST:
{
//
// If the gripper is enabled then perform a simple hit test on our gripper area.
DialogData *pdd = reinterpret_cast<DialogData *>(GetProp(hwnd, pcszDialogDataPropertyName));
if (pdd && pdd->m_bShowSizingGrip)
{
POINT pt = { LOWORD(lParam), HIWORD(lParam) };
(void)ScreenToClient(hwnd, &pt);
if (PtInRect(&pdd->rcGrip, pt))
return HTBOTTOMRIGHT;
}
}
break;
case WM_GETMINMAXINFO:
{
//
// Our opportunity to say that we do not want the dialog to grow or shrink any more.
DialogData *pdd = reinterpret_cast<DialogData *>(GetProp(hwnd, pcszDialogDataPropertyName));
LPMINMAXINFO lpmmi = reinterpret_cast<LPMINMAXINFO>(lParam);
lpmmi->ptMinTrackSize = pdd->m_ptSmallest;
if (pdd->m_bLargestSet)
{
lpmmi->ptMaxTrackSize = pdd->m_ptLargest;
}
}
return 0;
case WM_NOTIFY:
{
if (reinterpret_cast<LPNMHDR>(lParam)->code == PSN_SETACTIVE)
{
RECT rc;
GetClientRect(GetParent(hwnd), &rc);
UpdateWindowSize(rc.right - rc.left, rc.bottom - rc.top, GetParent(hwnd));
}
}
break;
case WM_DESTROY:
{
//
// Our opportunty for cleanup.
// Simply acquire all of our objects, free the appropriate memory and remove the
// properties from the window. If we do not remove the properties then they will constitute
// a resource leak.
DialogData *pdd = reinterpret_cast<DialogData *>(GetProp(hwnd, pcszDialogDataPropertyName));
if (pdd)
{
/*RegistryData rd;
rd.m_wpl.length = sizeof( rd.m_wpl );
GetWindowPlacement( hwnd, &rd.m_wpl );
if( pdd->hkRootSave && pdd->pcszName )
{
(void)RegSetValueExRecursive( pdd->hkRootSave, pdd->pcszName, NULL, REG_BINARY, reinterpret_cast<LPBYTE>( &rd ), sizeof( rd ) );
}*/
if (pdd->psd)
{
HeapFree(GetProcessHeap(), 0, pdd->psd);
}
HeapFree(GetProcessHeap(), 0, pdd);
(void)RemoveProp(hwnd, pcszDialogDataPropertyName);
}
(void)SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(pOldProc));
(void)RemoveProp(hwnd, pcszWindowProcPropertyName);
}
break;
}
return CallWindowProc(pOldProc, hwnd, msg, wParam, lParam);
}