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