visualboyadvance-m/src/win32/ResizeDlg.cpp

562 lines
17 KiB
C++

/*----------------------------------------------------------------------
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: russf@gipsysoft.com
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 "stdafx.h"
#include "VBA.h"
#include "ResizeDlg.h"
#undef ASSERT
#include "WinHelper.h"
// moved functions to this file to reduce number of files
struct RegistryData
{
WINDOWPLACEMENT m_wpl;
};
struct DialogData // dd
{
HKEY hkRootSave;
LPCTSTR pcszName;
//
// 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;
WinHelper::CRect m_rcGrip;
};
extern bool regEnabled;
extern const char *regGetINIPath();
void AssertFailed(char *file, int line, char *exp)
{
char buffer[1024];
sprintf(buffer, "File %s\nLine %d\nExpression %s\nPress Retry to debug",
file, line, exp);
int res = MessageBox(*theApp.m_pMainWnd, buffer, "Assertion failed!",
MB_ICONHAND | MB_SETFOREGROUND | MB_TASKMODAL |
MB_ABORTRETRYIGNORE);
if(res == IDRETRY) {
DebugBreak();
} else if(res == IDABORT)
SendMessage(*theApp.m_pMainWnd, WM_QUIT, 0, 0);
}
void ApiFailure(char *pcszFilename, int nLine, char *pcszExpression )
{
const DWORD dwLastError = ::GetLastError();
LPCTSTR lpMsgBuf;
(void)::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dwLastError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf, 0, NULL );
char szExeName[ MAX_PATH ];
if( !GetModuleFileName( NULL, szExeName, countof( szExeName ) ) )
strcpy( szExeName, "<No Program Name>" );
char szMessage[ 1024 ];
_snprintf( szMessage, countof( szMessage )
, "API VERIFY Failure!"
"\nProgram: %s"
"\n"
"\nFile %s"
"\nLine %d"
"\n"
"\nExpression %s"
"\n"
"\nLast Error %d"
"\n %s"
"\n\nPress Retry to debug the application"
, szExeName
, pcszFilename
, nLine
, pcszExpression
, dwLastError
, lpMsgBuf
);
(void)LocalFree( (LPVOID)lpMsgBuf );
HWND hwndParent = ::GetActiveWindow();
hwndParent = ::GetLastActivePopup( hwndParent );
int nCode = ::MessageBoxA( hwndParent,
szMessage,
"Debug Helper",
MB_TASKMODAL | MB_ICONHAND | MB_ABORTRETRYIGNORE |
MB_SETFOREGROUND );
if(nCode == IDABORT) {
::SendMessage(*theApp.m_pMainWnd, WM_QUIT, 0, 0);
} else if(nCode == IDRETRY)
DebugBreak();
}
long FASTCALL RegQueryValueExRecursive( HKEY hKey, LPCTSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData )
{
TCHAR szBuffer[ 256 ];
R_ASSERT( lstrlen( lpValueName ) < countof( szBuffer ) );
(void)lstrcpy( szBuffer, lpValueName );
LPTSTR pszBuffer = szBuffer;
LPTSTR pszLast = szBuffer;
while( *pszBuffer )
{
if( *pszBuffer == _T('\\') || *pszBuffer == _T('/') )
{
pszLast = pszBuffer;
lpValueName = pszLast + 1;
}
pszBuffer++;
}
if(!regEnabled) {
if(GetPrivateProfileStruct("Viewer",
lpValueName,
lpData,
*lpcbData,
regGetINIPath())) {
*lpType = REG_BINARY;
return ERROR_SUCCESS;
}
return -1;
}
bool m_bNeedToCloseKey = false;
if( pszLast != szBuffer )
{
*pszLast = _T('\000');
HKEY hkeyTemp;
long lRet = RegOpenKey( hKey, szBuffer, &hkeyTemp );
if( lRet != ERROR_SUCCESS )
{
return lRet;
}
hKey = hkeyTemp;
m_bNeedToCloseKey = true;
}
long lRet = RegQueryValueEx( hKey, lpValueName, lpReserved, lpType, lpData, lpcbData );
if( m_bNeedToCloseKey )
{
R_VERIFY( RegCloseKey( hKey ) == ERROR_SUCCESS );
}
return lRet;
}
long FASTCALL RegSetValueExRecursive( HKEY hKey, LPCTSTR lpValueName, DWORD Reserved, DWORD dwType, CONST BYTE* lpData, DWORD cbData )
{
TCHAR szBuffer[ 256 ];
R_ASSERT( lstrlen( lpValueName ) < countof( szBuffer ) );
(void)lstrcpy( szBuffer, lpValueName );
LPTSTR pszBuffer = szBuffer;
LPTSTR pszLast = szBuffer;
while( *pszBuffer )
{
if( *pszBuffer == _T('\\') || *pszBuffer == _T('/') )
{
pszLast = pszBuffer;
lpValueName = pszLast + 1;
}
pszBuffer++;
}
if(!regEnabled) {
if(WritePrivateProfileStruct("Viewer",
lpValueName,
(LPVOID)lpData,
cbData,
regGetINIPath())) {
return ERROR_SUCCESS;
}
return -1;
}
bool m_bNeedToCloseKey = false;
if( pszLast != szBuffer )
{
*pszLast = _T('\000');
HKEY hkeyTemp;
long lRet = RegOpenKey( hKey, szBuffer, &hkeyTemp );
if( lRet != ERROR_SUCCESS )
{
lRet = RegCreateKey( hKey, szBuffer, &hkeyTemp );
if( lRet != ERROR_SUCCESS )
return lRet;
}
hKey = hkeyTemp;
m_bNeedToCloseKey = true;
}
long lRet = RegSetValueEx( hKey, lpValueName, Reserved, dwType, lpData, cbData );
if( m_bNeedToCloseKey )
{
R_VERIFY( RegCloseKey( hKey ) == ERROR_SUCCESS );
}
return lRet;
}
int ResizeDlgGetItemCount(const DialogSizerSizingItem *psd)
{
R_ASSERT( psd );
int nCount = 0;
while( psd->uSizeInfo != 0xFFFFFFFF )
{
nCount++;
psd++;
}
return nCount;
}
void ResizeDlgUpdateGripperRect( const int cx, const int cy, WinHelper::CRect &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;
}
void ResizeDlgUpdateGripper( HWND hwnd, DialogData *pdd )
{
if( pdd->m_bShowSizingGrip )
{
WinHelper::CRect rcOld( pdd->m_rcGrip );
ResizeDlgUpdateGripperRect( pdd->m_sizeClient.cx, pdd->m_sizeClient.cy, pdd->m_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( &rcOld, &rcOld, &pdd->m_rcGrip );
(void)InvalidateRect( hwnd, &rcOld, TRUE );
}
}
void ResizeDlgCopyItems( DialogSizerSizingItem *psdDest, const DialogSizerSizingItem *psdSource )
//
// Will copy all of the items in psdSource into psdDest.
{
//
// Loop til we reach the end
while( psdSource->uSizeInfo != 0xFFFFFFFF )
{
*psdDest = *psdSource;
psdDest++;
psdSource++;
}
// And when we do copy the last item
*psdDest = *psdSource;
}
ResizeDlg::ResizeDlg(UINT id, CWnd *parent)
: CDialog(id, parent)
{
dd = NULL;
}
void *ResizeDlg::AddDialogData()
//
// 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 = (DialogData*)dd;
if( !pdd ) {
pdd = (DialogData *)calloc(1, sizeof(DialogData));
}
if( pdd ) {
//
// Store some sizes etc. for later.
CRect rc;
GetWindowRect( rc );
pdd->m_ptSmallest.x = rc.Width();
pdd->m_ptSmallest.y = rc.Height();
GetClientRect( rc );
pdd->m_sizeClient = rc.Size();
dd = pdd;
ResizeDlgUpdateGripperRect( pdd->m_sizeClient.cx, pdd->m_sizeClient.cy, pdd->m_rcGrip );
}
return pdd;
}
BOOL ResizeDlg::SetData(const DialogSizerSizingItem *psd,
BOOL bShowSizingGrip,
HKEY hkRootSave,
LPCTSTR pcszName,
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
{
R_ASSERT( psd );
R_ASSERT( ( hkRootSave != NULL && pcszName != NULL )
|| ( hkRootSave == NULL && pcszName == NULL ) );
//
// Make sure all of the parameters are valid.
if( ::IsWindow( *this )
&& psd
&& ( ( hkRootSave != NULL && pcszName != NULL &&
!IsBadStringPtr( pcszName, 0xFFFF ) ) ||
( hkRootSave == NULL && pcszName == NULL ) )
&& ( psizeMax == NULL || !IsBadReadPtr( psizeMax, sizeof( SIZE ) ) )
) {
DialogData *pdd = (DialogData *)AddDialogData();
if( pdd ) {
pdd->hkRootSave = hkRootSave;
pdd->pcszName = pcszName;
pdd->m_bShowSizingGrip = bShowSizingGrip;
pdd->nItemCount = ResizeDlgGetItemCount( psd ) + 1;
pdd->psd = (DialogSizerSizingItem *)
calloc(pdd->nItemCount,
sizeof(DialogSizerSizingItem ));
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.
ResizeDlgCopyItems( 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 ) {
RegistryData rd;
DWORD dwSize = sizeof( RegistryData );
DWORD dwType = REG_BINARY;
if( RegQueryValueExRecursive( hkRootSave, pcszName, NULL, &dwType, reinterpret_cast<LPBYTE>( &rd ), &dwSize ) == ERROR_SUCCESS && dwSize == sizeof( rd ) ) {
if( !(GetWindowLong( *this, GWL_STYLE ) & WS_VISIBLE) )
rd.m_wpl.showCmd = SW_HIDE;
VAPI( SetWindowPlacement( &rd.m_wpl ) );
}
}
return TRUE;
} else {
free(pdd);
}
}
}
return FALSE;
}
void ResizeDlg::UpdateWindowSize(const int cx, const int cy, HWND hwnd)
{
DialogData *pdd = (DialogData*)dd;
if( pdd ) {
const int nDeltaX = cx - pdd->m_sizeClient.cx;
const int nDeltaY = cy - pdd->m_sizeClient.cy;
WinHelper::CDeferWindowPos def( pdd->nItemCount );
WinHelper::CRect rc;
const DialogSizerSizingItem *psd = pdd->psd;
while( psd->uSizeInfo != 0xFFFFFFFF ) {
HWND hwndChild = ::GetDlgItem( *this, psd->uControlID );
if( ::IsWindow( hwndChild ) ) {
VAPI( ::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;
}
(void)def.DeferWindowPos( hwndChild, NULL, rc,
SWP_NOACTIVATE | SWP_NOZORDER );
}
psd++;
}
pdd->m_sizeClient.cx = cx;
pdd->m_sizeClient.cy = cy;
//
// If we have a sizing grip enabled then adjust it's position
ResizeDlgUpdateGripper( hwnd, pdd );
}
}
BOOL ResizeDlg::OnWndMsg(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *res )
// Actual window procedure that will handle saving window size/position and moving
// the controls whilst the window sizes.
{
if(dd == NULL) {
return CDialog::OnWndMsg(msg, wParam, lParam, res);
}
switch( msg ) {
case WM_ERASEBKGND:
{
BOOL r = CDialog::OnWndMsg(msg, wParam, lParam, res);
DialogData *pdd = (DialogData*)dd;
if( pdd && pdd->m_bShowSizingGrip && !pdd->m_bMaximised ) {
VAPI( ::DrawFrameControl( reinterpret_cast<HDC>( wParam ),
pdd->m_rcGrip,
DFC_SCROLL, DFCS_SCROLLSIZEGRIP ) );
}
return r;
}
case WM_SIZE:
{
DialogData *pdd = (DialogData*)dd;
if( pdd && wParam != SIZE_MINIMIZED ) {
pdd->m_bMaximised = ( wParam == SIZE_MAXIMIZED ? true : false );
UpdateWindowSize( LOWORD( lParam ), HIWORD( lParam ), *this);
}
}
break;
case WM_NCHITTEST:
{
//
// If the gripper is enabled then perform a simple hit test on our gripper area.
DialogData *pdd = (DialogData*)dd;
if( pdd && pdd->m_bShowSizingGrip ) {
POINT pt = { LOWORD(lParam), HIWORD(lParam) };
(void)ScreenToClient( &pt );
if( PtInRect( pdd->m_rcGrip, pt ) )
return (BOOL)HTBOTTOMRIGHT;
}
}
break;
case WM_GETMINMAXINFO:
{
//
// Our opportunity to say that we do not want the dialog to grow or shrink any more.
DialogData *pdd = (DialogData*)dd;
LPMINMAXINFO lpmmi = reinterpret_cast<LPMINMAXINFO>( lParam );
lpmmi->ptMinTrackSize = pdd->m_ptSmallest;
if( pdd->m_bLargestSet ) {
lpmmi->ptMaxTrackSize = pdd->m_ptLargest;
}
}
return (BOOL)0;
case WM_NOTIFY:
{
if( reinterpret_cast<LPNMHDR>(lParam)->code == PSN_SETACTIVE ) {
CRect rc;
VAPI( ::GetClientRect( *GetParent( ), &rc ) );
UpdateWindowSize( rc.Width(), rc.Height(), *GetParent( ) );
}
}
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 = (DialogData*)dd;
if( pdd ) {
RegistryData rd;
rd.m_wpl.length = sizeof( rd.m_wpl );
VAPI( GetWindowPlacement( &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 ) {
free(pdd->psd);
}
free(pdd);
}
}
break;
}
return CDialog::OnWndMsg(msg, wParam, lParam, res);
}