1 /*----------------------------------------------------------------------
2 Copyright (c) Gipsysoft. All Rights Reserved.
3 File: DialogSizer_Set.cpp
4 Web site: http://gipsysoft.com
5
6 This software is provided 'as-is', without any express or implied warranty.
7
8 In no event will the author be held liable for any damages arising from the
9 use of this software.
10
11 Permission is granted to anyone to use this software for any purpose, including
12 commercial applications, and to alter it and redistribute it freely, subject
13 to the following restrictions:
14
15 1) The origin of this software must not be misrepresented; you must not claim
16 that you wrote the original software. If you use this software in a product,
17 an acknowledgment in the product documentation is requested but not required.
18 2) Altered source versions must be plainly marked as such, and must not be
19 misrepresented as being the original software. Altered source is encouraged
20 to be submitted back to the original author so it can be shared with the
21 community. Please share your changes.
22 3) This notice may not be removed or altered from any source distribution.
23
24 Owner: russf@gipsysoft.com
25 Purpose: Main functionality for sizeable dialogs
26
27 Store a local copy of the user settings
28 Subclass the window
29 Respond to various messages withinn the subclassed window.
30
31 ----------------------------------------------------------------------*/
32 #include "stdafx.h"
33 #include "VBA.h"
34 #include "ResizeDlg.h"
35 #undef ASSERT
36 #include "WinHelper.h"
37
38 // moved functions to this file to reduce number of files
39
40 struct RegistryData
41 {
42 WINDOWPLACEMENT m_wpl;
43 };
44
45
46 struct DialogData // dd
47 {
48 HKEY hkRootSave;
49 LPCTSTR pcszName;
50
51 //
52 // The number of items contained in the psd member.
53 // Used in the DeferWindowPos structure and in allocating memory
54 int nItemCount;
55 DialogSizerSizingItem *psd;
56
57 //
58 // We need the smallest to respond to the WM_GETMINMAXINFO message
59 POINT m_ptSmallest;
60
61 //
62 // We don't strictly speaking need to say how big the biggest can be but
63 POINT m_ptLargest;
64 bool m_bLargestSet;
65
66 //
67 // we need this to decide how much the window has changed size when we get a WM_SIZE message
68 SIZE m_sizeClient;
69
70 //
71 // Draw the sizing grip...or not
72 bool m_bMaximised;
73 BOOL m_bShowSizingGrip;
74
75 WinHelper::CRect m_rcGrip;
76 };
77
78 extern bool regEnabled;
79 extern const char *regGetINIPath();
80
AssertFailed(char * file,int line,char * exp)81 void AssertFailed(char *file, int line, char *exp)
82 {
83 char buffer[1024];
84
85 sprintf(buffer, "File %s\nLine %d\nExpression %s\nPress Retry to debug",
86 file, line, exp);
87 int res = MessageBox(*theApp.m_pMainWnd, buffer, "Assertion failed!",
88 MB_ICONHAND | MB_SETFOREGROUND | MB_TASKMODAL |
89 MB_ABORTRETRYIGNORE);
90
91 if(res == IDRETRY) {
92 __asm int 3;
93 } else if(res == IDABORT)
94 SendMessage(*theApp.m_pMainWnd, WM_QUIT, 0, 0);
95 }
96
ApiFailure(char * pcszFilename,int nLine,char * pcszExpression)97 void ApiFailure(char *pcszFilename, int nLine, char *pcszExpression )
98 {
99 const DWORD dwLastError = ::GetLastError();
100 LPCTSTR lpMsgBuf;
101 (void)::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
102 FORMAT_MESSAGE_FROM_SYSTEM |
103 FORMAT_MESSAGE_IGNORE_INSERTS,
104 NULL, dwLastError,
105 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
106 (LPTSTR) &lpMsgBuf, 0, NULL );
107
108 char szExeName[ MAX_PATH ];
109
110 if( !GetModuleFileName( NULL, szExeName, countof( szExeName ) ) )
111 strcpy( szExeName, "<No Program Name>" );
112
113
114 char szMessage[ 1024 ];
115 _snprintf( szMessage, countof( szMessage )
116 , "API VERIFY Failure!"
117 "\nProgram: %s"
118 "\n"
119 "\nFile %s"
120 "\nLine %d"
121 "\n"
122 "\nExpression %s"
123 "\n"
124 "\nLast Error %d"
125 "\n %s"
126 "\n\nPress Retry to debug the application"
127 , szExeName
128 , pcszFilename
129 , nLine
130 , pcszExpression
131 , dwLastError
132 , lpMsgBuf
133 );
134
135 (void)LocalFree( (LPVOID)lpMsgBuf );
136 HWND hwndParent = ::GetActiveWindow();
137 hwndParent = ::GetLastActivePopup( hwndParent );
138 int nCode = ::MessageBoxA( hwndParent,
139 szMessage,
140 "Debug Helper",
141 MB_TASKMODAL | MB_ICONHAND | MB_ABORTRETRYIGNORE |
142 MB_SETFOREGROUND );
143 if(nCode == IDABORT) {
144 ::SendMessage(*theApp.m_pMainWnd, WM_QUIT, 0, 0);
145 } else if(nCode == IDRETRY)
146 __asm int 3;
147 }
148
RegQueryValueExRecursive(HKEY hKey,LPCTSTR lpValueName,LPDWORD lpReserved,LPDWORD lpType,LPBYTE lpData,LPDWORD lpcbData)149 long FASTCALL RegQueryValueExRecursive( HKEY hKey, LPCTSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData )
150 {
151 TCHAR szBuffer[ 256 ];
152 R_ASSERT( lstrlen( lpValueName ) < countof( szBuffer ) );
153 (void)lstrcpy( szBuffer, lpValueName );
154
155 LPTSTR pszBuffer = szBuffer;
156 LPTSTR pszLast = szBuffer;
157 while( *pszBuffer )
158 {
159 if( *pszBuffer == _T('\\') || *pszBuffer == _T('/') )
160 {
161 pszLast = pszBuffer;
162 lpValueName = pszLast + 1;
163 }
164 pszBuffer++;
165 }
166
167 if(!regEnabled) {
168 if(GetPrivateProfileStruct("Viewer",
169 lpValueName,
170 lpData,
171 *lpcbData,
172 regGetINIPath())) {
173 *lpType = REG_BINARY;
174 return ERROR_SUCCESS;
175 }
176 return -1;
177 }
178
179 bool m_bNeedToCloseKey = false;
180 if( pszLast != szBuffer )
181 {
182 *pszLast = _T('\000');
183 HKEY hkeyTemp;
184 long lRet = RegOpenKey( hKey, szBuffer, &hkeyTemp );
185 if( lRet != ERROR_SUCCESS )
186 {
187 return lRet;
188 }
189 hKey = hkeyTemp;
190 m_bNeedToCloseKey = true;
191 }
192
193 long lRet = RegQueryValueEx( hKey, lpValueName, lpReserved, lpType, lpData, lpcbData );
194 if( m_bNeedToCloseKey )
195 {
196 R_VERIFY( RegCloseKey( hKey ) == ERROR_SUCCESS );
197 }
198 return lRet;
199 }
200
RegSetValueExRecursive(HKEY hKey,LPCTSTR lpValueName,DWORD Reserved,DWORD dwType,CONST BYTE * lpData,DWORD cbData)201 long FASTCALL RegSetValueExRecursive( HKEY hKey, LPCTSTR lpValueName, DWORD Reserved, DWORD dwType, CONST BYTE* lpData, DWORD cbData )
202 {
203 TCHAR szBuffer[ 256 ];
204 R_ASSERT( lstrlen( lpValueName ) < countof( szBuffer ) );
205 (void)lstrcpy( szBuffer, lpValueName );
206
207 LPTSTR pszBuffer = szBuffer;
208 LPTSTR pszLast = szBuffer;
209 while( *pszBuffer )
210 {
211 if( *pszBuffer == _T('\\') || *pszBuffer == _T('/') )
212 {
213 pszLast = pszBuffer;
214 lpValueName = pszLast + 1;
215 }
216 pszBuffer++;
217 }
218
219 if(!regEnabled) {
220 if(WritePrivateProfileStruct("Viewer",
221 lpValueName,
222 (LPVOID)lpData,
223 cbData,
224 regGetINIPath())) {
225 return ERROR_SUCCESS;
226 }
227 return -1;
228 }
229
230 bool m_bNeedToCloseKey = false;
231 if( pszLast != szBuffer )
232 {
233 *pszLast = _T('\000');
234 HKEY hkeyTemp;
235 long lRet = RegOpenKey( hKey, szBuffer, &hkeyTemp );
236 if( lRet != ERROR_SUCCESS )
237 {
238 lRet = RegCreateKey( hKey, szBuffer, &hkeyTemp );
239 if( lRet != ERROR_SUCCESS )
240 return lRet;
241 }
242 hKey = hkeyTemp;
243 m_bNeedToCloseKey = true;
244 }
245
246 long lRet = RegSetValueEx( hKey, lpValueName, Reserved, dwType, lpData, cbData );
247 if( m_bNeedToCloseKey )
248 {
249 R_VERIFY( RegCloseKey( hKey ) == ERROR_SUCCESS );
250 }
251 return lRet;
252 }
253
254
ResizeDlgGetItemCount(const DialogSizerSizingItem * psd)255 int ResizeDlgGetItemCount(const DialogSizerSizingItem *psd)
256 {
257 R_ASSERT( psd );
258 int nCount = 0;
259 while( psd->uSizeInfo != 0xFFFFFFFF )
260 {
261 nCount++;
262 psd++;
263 }
264 return nCount;
265 }
266
ResizeDlgUpdateGripperRect(const int cx,const int cy,WinHelper::CRect & rcGrip)267 void ResizeDlgUpdateGripperRect( const int cx, const int cy, WinHelper::CRect &rcGrip )
268 {
269 const int nGripWidth = GetSystemMetrics( SM_CYVSCROLL );
270 const int nGripHeight = GetSystemMetrics( SM_CXVSCROLL );
271 rcGrip.left = cx - nGripWidth;
272 rcGrip.top = cy - nGripHeight;
273 rcGrip.right = cx;
274 rcGrip.bottom = cy;
275 }
276
ResizeDlgUpdateGripper(HWND hwnd,DialogData * pdd)277 void ResizeDlgUpdateGripper( HWND hwnd, DialogData *pdd )
278 {
279 if( pdd->m_bShowSizingGrip )
280 {
281 WinHelper::CRect rcOld( pdd->m_rcGrip );
282
283 ResizeDlgUpdateGripperRect( pdd->m_sizeClient.cx, pdd->m_sizeClient.cy, pdd->m_rcGrip );
284
285 //
286 // We also need to invalidate the combined area of the old and new rectangles
287 // otherwise we would have trail of grippers when we sized the dialog larger
288 // in any axis
289 (void)UnionRect( &rcOld, &rcOld, &pdd->m_rcGrip );
290 (void)InvalidateRect( hwnd, &rcOld, TRUE );
291 }
292 }
293
ResizeDlgCopyItems(DialogSizerSizingItem * psdDest,const DialogSizerSizingItem * psdSource)294 void ResizeDlgCopyItems( DialogSizerSizingItem *psdDest, const DialogSizerSizingItem *psdSource )
295 //
296 // Will copy all of the items in psdSource into psdDest.
297 {
298 //
299 // Loop til we reach the end
300 while( psdSource->uSizeInfo != 0xFFFFFFFF )
301 {
302 *psdDest = *psdSource;
303 psdDest++;
304 psdSource++;
305 }
306 // And when we do copy the last item
307 *psdDest = *psdSource;
308 }
309
310
ResizeDlg(UINT id,CWnd * parent)311 ResizeDlg::ResizeDlg(UINT id, CWnd *parent)
312 : CDialog(id, parent)
313 {
314 dd = NULL;
315 }
316
AddDialogData()317 void *ResizeDlg::AddDialogData()
318 //
319 // Firstly determine if the data already exists, if it does then return that, if not then we will
320 // create and initialise a brand new structure.
321 {
322 DialogData *pdd = (DialogData*)dd;
323 if( !pdd ) {
324 pdd = (DialogData *)calloc(1, sizeof(DialogData));
325 }
326
327 if( pdd ) {
328 //
329 // Store some sizes etc. for later.
330 CRect rc;
331 GetWindowRect( rc );
332 pdd->m_ptSmallest.x = rc.Width();
333 pdd->m_ptSmallest.y = rc.Height();
334
335
336 GetClientRect( rc );
337 pdd->m_sizeClient = rc.Size();
338 dd = pdd;
339 ResizeDlgUpdateGripperRect( pdd->m_sizeClient.cx, pdd->m_sizeClient.cy, pdd->m_rcGrip );
340 }
341 return pdd;
342 }
343
SetData(const DialogSizerSizingItem * psd,BOOL bShowSizingGrip,HKEY hkRootSave,LPCTSTR pcszName,SIZE * psizeMax)344 BOOL ResizeDlg::SetData(const DialogSizerSizingItem *psd,
345 BOOL bShowSizingGrip,
346 HKEY hkRootSave,
347 LPCTSTR pcszName,
348 SIZE *psizeMax)
349 //
350 // Setting a dialog sizeable involves subclassing the window and handling it's
351 // WM_SIZE messages, if we have a hkRootSave and pcszName then we will also be loading/saving
352 // the size and position of the window from the registry. We load from the registry when we
353 // subclass the window and we save to the registry when we get a WM_DESTROY.
354 //
355 // It will return non-zero for success and zero if it fails
356 {
357 R_ASSERT( psd );
358 R_ASSERT( ( hkRootSave != NULL && pcszName != NULL )
359 || ( hkRootSave == NULL && pcszName == NULL ) );
360 //
361 // Make sure all of the parameters are valid.
362 if( ::IsWindow( *this )
363 && psd
364 && ( ( hkRootSave != NULL && pcszName != NULL &&
365 !IsBadStringPtr( pcszName, 0xFFFF ) ) ||
366 ( hkRootSave == NULL && pcszName == NULL ) )
367 && ( psizeMax == NULL || !IsBadReadPtr( psizeMax, sizeof( SIZE ) ) )
368 ) {
369 DialogData *pdd = (DialogData *)AddDialogData();
370 if( pdd ) {
371 pdd->hkRootSave = hkRootSave;
372 pdd->pcszName = pcszName;
373 pdd->m_bShowSizingGrip = bShowSizingGrip;
374 pdd->nItemCount = ResizeDlgGetItemCount( psd ) + 1;
375 pdd->psd = (DialogSizerSizingItem *)
376 calloc(pdd->nItemCount,
377 sizeof(DialogSizerSizingItem ));
378 if( pdd->psd ) {
379 //
380 // Copy all of the user controls etc. for later, this way the user can quite happily
381 // let the structure go out of scope.
382 ResizeDlgCopyItems( pdd->psd, psd );
383 if( psizeMax ) {
384 pdd->m_ptLargest.x = psizeMax->cx;
385 pdd->m_ptLargest.y = psizeMax->cy;
386 pdd->m_bLargestSet = true;
387 }
388
389 //
390 // If the there was save info passed in then we need to make damn good use of it
391 // by attempting to load the RegistryData structure
392 if( hkRootSave && pcszName ) {
393 RegistryData rd;
394 DWORD dwSize = sizeof( RegistryData );
395 DWORD dwType = REG_BINARY;
396 if( RegQueryValueExRecursive( hkRootSave, pcszName, NULL, &dwType, reinterpret_cast<LPBYTE>( &rd ), &dwSize ) == ERROR_SUCCESS && dwSize == sizeof( rd ) ) {
397 if( !(GetWindowLong( *this, GWL_STYLE ) & WS_VISIBLE) )
398 rd.m_wpl.showCmd = SW_HIDE;
399
400 VAPI( SetWindowPlacement( &rd.m_wpl ) );
401 }
402 }
403 return TRUE;
404 } else {
405 free(pdd);
406 }
407 }
408 }
409 return FALSE;
410 }
411
UpdateWindowSize(const int cx,const int cy,HWND hwnd)412 void ResizeDlg::UpdateWindowSize(const int cx, const int cy, HWND hwnd)
413 {
414 DialogData *pdd = (DialogData*)dd;
415 if( pdd ) {
416 const int nDeltaX = cx - pdd->m_sizeClient.cx;
417 const int nDeltaY = cy - pdd->m_sizeClient.cy;
418 WinHelper::CDeferWindowPos def( pdd->nItemCount );
419 WinHelper::CRect rc;
420 const DialogSizerSizingItem *psd = pdd->psd;
421 while( psd->uSizeInfo != 0xFFFFFFFF ) {
422 HWND hwndChild = ::GetDlgItem( *this, psd->uControlID );
423 if( ::IsWindow( hwndChild ) ) {
424 VAPI( ::GetWindowRect( hwndChild, rc ) );
425 (void)::MapWindowPoints( ::GetDesktopWindow(), hwnd,
426 (LPPOINT)&rc, 2 );
427
428 //
429 // Adjust the window horizontally
430 if( psd->uSizeInfo & DS_MoveX ) {
431 rc.left += nDeltaX;
432 rc.right += nDeltaX;
433 }
434
435 //
436 // Adjust the window vertically
437 if( psd->uSizeInfo & DS_MoveY ) {
438 rc.top += nDeltaY;
439 rc.bottom += nDeltaY;
440 }
441
442 //
443 // Size the window horizontally
444 if( psd->uSizeInfo & DS_SizeX ) {
445 rc.right += nDeltaX;
446 }
447
448 //
449 // Size the window vertically
450 if( psd->uSizeInfo & DS_SizeY ) {
451 rc.bottom += nDeltaY;
452 }
453
454 (void)def.DeferWindowPos( hwndChild, NULL, rc,
455 SWP_NOACTIVATE | SWP_NOZORDER );
456 }
457 psd++;
458 }
459
460 pdd->m_sizeClient.cx = cx;
461 pdd->m_sizeClient.cy = cy;
462
463 //
464 // If we have a sizing grip enabled then adjust it's position
465 ResizeDlgUpdateGripper( hwnd, pdd );
466 }
467 }
468
OnWndMsg(UINT msg,WPARAM wParam,LPARAM lParam,LRESULT * res)469 BOOL ResizeDlg::OnWndMsg(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *res )
470 // Actual window procedure that will handle saving window size/position and moving
471 // the controls whilst the window sizes.
472 {
473 if(dd == NULL) {
474 return CDialog::OnWndMsg(msg, wParam, lParam, res);
475 }
476 switch( msg ) {
477 case WM_ERASEBKGND:
478 {
479 BOOL r = CDialog::OnWndMsg(msg, wParam, lParam, res);
480 DialogData *pdd = (DialogData*)dd;
481 if( pdd && pdd->m_bShowSizingGrip && !pdd->m_bMaximised ) {
482 VAPI( ::DrawFrameControl( reinterpret_cast<HDC>( wParam ),
483 pdd->m_rcGrip,
484 DFC_SCROLL, DFCS_SCROLLSIZEGRIP ) );
485 }
486 return r;
487 }
488 case WM_SIZE:
489 {
490 DialogData *pdd = (DialogData*)dd;
491 if( pdd && wParam != SIZE_MINIMIZED ) {
492 pdd->m_bMaximised = ( wParam == SIZE_MAXIMIZED ? true : false );
493 UpdateWindowSize( LOWORD( lParam ), HIWORD( lParam ), *this);
494 }
495 }
496 break;
497 case WM_NCHITTEST:
498 {
499 //
500 // If the gripper is enabled then perform a simple hit test on our gripper area.
501 DialogData *pdd = (DialogData*)dd;
502 if( pdd && pdd->m_bShowSizingGrip ) {
503 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
504 (void)ScreenToClient( &pt );
505 if( PtInRect( pdd->m_rcGrip, pt ) )
506 return (BOOL)HTBOTTOMRIGHT;
507 }
508 }
509 break;
510 case WM_GETMINMAXINFO:
511 {
512 //
513 // Our opportunity to say that we do not want the dialog to grow or shrink any more.
514 DialogData *pdd = (DialogData*)dd;
515 LPMINMAXINFO lpmmi = reinterpret_cast<LPMINMAXINFO>( lParam );
516 lpmmi->ptMinTrackSize = pdd->m_ptSmallest;
517 if( pdd->m_bLargestSet ) {
518 lpmmi->ptMaxTrackSize = pdd->m_ptLargest;
519 }
520 }
521 return (BOOL)0;
522 case WM_NOTIFY:
523 {
524 if( reinterpret_cast<LPNMHDR>(lParam)->code == PSN_SETACTIVE ) {
525 CRect rc;
526 VAPI( ::GetClientRect( *GetParent( ), &rc ) );
527 UpdateWindowSize( rc.Width(), rc.Height(), *GetParent( ) );
528 }
529 }
530 break;
531 case WM_DESTROY:
532 {
533 //
534 // Our opportunty for cleanup.
535 // Simply acquire all of our objects, free the appropriate memory and remove the
536 // properties from the window. If we do not remove the properties then they will constitute
537 // a resource leak.
538 DialogData *pdd = (DialogData*)dd;
539 if( pdd ) {
540 RegistryData rd;
541 rd.m_wpl.length = sizeof( rd.m_wpl );
542 VAPI( GetWindowPlacement( &rd.m_wpl ) );
543
544 if( pdd->hkRootSave && pdd->pcszName ) {
545 (void)RegSetValueExRecursive( pdd->hkRootSave, pdd->pcszName,
546 NULL, REG_BINARY,
547 reinterpret_cast<LPBYTE>( &rd ),
548 sizeof( rd ) );
549 }
550
551 if( pdd->psd ) {
552 free(pdd->psd);
553 }
554 free(pdd);
555 }
556
557 }
558 break;
559 }
560 return CDialog::OnWndMsg(msg, wParam, lParam, res);
561 }
562