From 4235b477488982cd3df1c63510eb69355fb4f865 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Mon, 10 May 2021 14:04:50 -0700 Subject: [PATCH] Save and restore main window placement Windows' default behavior is apparently to fill the display with the app window, capped at 1920x1200. This is annoyingly large for most situations. We now save the main window rect (LTRB) and maximization status in the configuration area of the registry. The window placement calls are supposed to do something reasonable when the window would be completely off-screen (e.g. because a secondary monitor was disabled). --- app/Main.cpp | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++ app/Main.h | 11 ++++++- app/MyApp.cpp | 18 ++++++----- 3 files changed, 105 insertions(+), 9 deletions(-) diff --git a/app/Main.cpp b/app/Main.cpp index b74b9a6..9e7d2e5 100644 --- a/app/Main.cpp +++ b/app/Main.cpp @@ -133,6 +133,7 @@ static const UINT gFindReplaceID = RegisterWindowMessage(FINDMSGSTRING); BEGIN_MESSAGE_MAP(MainWindow, CFrameWnd) ON_WM_CREATE() + ON_WM_CLOSE() ON_MESSAGE(WMU_LATE_INIT, OnLateInit) //ON_MESSAGE(WMU_CLOSE_MAIN_DIALOG, OnCloseMainDialog) ON_WM_SIZE() @@ -306,6 +307,12 @@ MainWindow::~MainWindow() LOGI("MainWindow destructor complete"); } +void MainWindow::OnClose() +{ + SaveWinPlacement(); + CFrameWnd::OnClose(); +} + BOOL MainWindow::PreCreateWindow(CREATESTRUCT& cs) { BOOL res = CFrameWnd::PreCreateWindow(cs); @@ -563,6 +570,8 @@ int MainWindow::OnCreate(LPCREATESTRUCT lpcs) fStatusBar.SetPaneText(kProgressPane, L""); + RestoreWinPlacement(); + return 0; } @@ -1889,6 +1898,82 @@ void MainWindow::EventPause(int duration) * =================================== */ +static const WCHAR kMainWinSection[] = L"mainwin"; +static const WCHAR kMainWin_Left[] = L"left"; +static const WCHAR kMainWin_Top[] = L"top"; +static const WCHAR kMainWin_Right[] = L"right"; +static const WCHAR kMainWin_Bottom[] = L"bottom"; +static const WCHAR kMainWin_Cmd[] = L"cmd"; + +void MainWindow::SaveWinPlacement() +{ + // Capture the current window position. At the point where the destructor + // fires, too much has been shut down, so we need to do it here. + WINDOWPLACEMENT wndpl; + wndpl.length = sizeof(wndpl); + if (!GetWindowPlacement(&wndpl)) { + LOGW("Unable to get window placement"); + return; + } + + LOGD("Window is at ltrb=%ld,%ld %ldx%ld showCmd=%d", + wndpl.rcNormalPosition.left, + wndpl.rcNormalPosition.top, + wndpl.rcNormalPosition.right - wndpl.rcNormalPosition.left, + wndpl.rcNormalPosition.bottom - wndpl.rcNormalPosition.top, + wndpl.showCmd); + gMyApp.WriteProfileInt(kMainWinSection, + kMainWin_Left, wndpl.rcNormalPosition.left); + gMyApp.WriteProfileInt(kMainWinSection, + kMainWin_Top, wndpl.rcNormalPosition.top); + gMyApp.WriteProfileInt(kMainWinSection, + kMainWin_Right, wndpl.rcNormalPosition.right); + gMyApp.WriteProfileInt(kMainWinSection, + kMainWin_Bottom, wndpl.rcNormalPosition.bottom); + gMyApp.WriteProfileInt(kMainWinSection, + kMainWin_Cmd, wndpl.showCmd); +} + +void MainWindow::RestoreWinPlacement() +{ + RECT normPos; + normPos.left = gMyApp.GetProfileInt(kMainWinSection, kMainWin_Left, -1); + normPos.top = gMyApp.GetProfileInt(kMainWinSection, kMainWin_Top, -1); + normPos.right = gMyApp.GetProfileInt(kMainWinSection, kMainWin_Right, -1); + normPos.bottom = gMyApp.GetProfileInt(kMainWinSection, kMainWin_Bottom, -1); + int cmd = gMyApp.GetProfileInt(kMainWinSection, kMainWin_Cmd, -1); + + if (normPos.left < 0 || normPos.top < 0 || normPos.right < 0 || + normPos.bottom < 0 || cmd < 0) { + LOGI("Previous window placement not found."); + return; + } + + POINT nopt; + nopt.x = nopt.y = -1; + + // We need to set the placement for "normal", and then deal with + // maximized windows by having the MyApp ShowWindow() call pass the + // appropriate command. If we pass the correct normal rect here with + // the "maximized" command, we end up with a non-maximized window of + // maximum size. (If done correctly, you can maximize, quit, restart, + // and un-maximize back to the original size.) + WINDOWPLACEMENT wndpl; + wndpl.length = sizeof(wndpl); + wndpl.flags = 0; + wndpl.showCmd = SW_SHOWNORMAL; + wndpl.ptMinPosition = nopt; + wndpl.ptMaxPosition = nopt; + wndpl.rcNormalPosition = normPos; + + LOGD("Restoring previous placement, cmd=%d", wndpl.showCmd); + SetWindowPlacement(&wndpl); + + // Stomp on MyApp's show command. We don't want to start minimized, + // so switch to "normal" if that's set. + gMyApp.m_nCmdShow = (cmd == SW_SHOWMINIMIZED ? SW_SHOWNORMAL : cmd); +} + void MainWindow::DrawEmptyClientArea(CDC* pDC, const CRect& clientRect) { CBrush brush; diff --git a/app/Main.h b/app/Main.h index 3dd9e72..9351d01 100644 --- a/app/Main.h +++ b/app/Main.h @@ -54,7 +54,7 @@ public: /* * Override the pre-create function to tweak the window style. */ - BOOL PreCreateWindow(CREATESTRUCT& cs) override; + virtual BOOL PreCreateWindow(CREATESTRUCT& cs) override; /* * Override GetClientRect so we can factor in the status and tool bars. @@ -268,6 +268,7 @@ private: // Command handlers afx_msg int OnCreate(LPCREATESTRUCT lpcs); + afx_msg void OnClose(); afx_msg LONG OnLateInit(UINT, LONG); afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg void OnGetMinMaxInfo(MINMAXINFO* pMMI); @@ -489,6 +490,14 @@ private: */ void DrawEmptyClientArea(CDC* pDC, const CRect& clientRect); + /* + * Save/restore main window placement. The restore function will move + * the window if the previous placement is no longer visible (e.g. a + * secondary monitor was removed). + */ + void SaveWinPlacement(); + void RestoreWinPlacement(); + /* * Extract a record to the temp folder and open it with a new instance of * CiderPress. We might want to extract disk images as 2MG files to take diff --git a/app/MyApp.cpp b/app/MyApp.cpp index 87dac70..b7425bf 100644 --- a/app/MyApp.cpp +++ b/app/MyApp.cpp @@ -64,6 +64,16 @@ MyApp::~MyApp(void) BOOL MyApp::InitInstance(void) { + // This causes functions like SetProfileInt to use the registry rather + // than a .INI file. The registry key is "usually the name of a company". + // (Must do this before creating main window so we can restore the + // previous window position.) +#ifdef CAN_UPDATE_FILE_ASSOC + SetRegistryKey(fRegistry.GetAppRegistryKey()); +#else + SetRegistryKey(L"faddenSoft"); +#endif + // Create the main window. m_pMainWnd = new MainWindow; m_pMainWnd->ShowWindow(m_nCmdShow); @@ -92,14 +102,6 @@ BOOL MyApp::InitInstance(void) LogModuleLocation(L"riched32.dll"); LogModuleLocation(L"msftedit.dll"); - // This causes functions like SetProfileInt to use the registry rather - // than a .INI file. The registry key is "usually the name of a company". -#ifdef CAN_UPDATE_FILE_ASSOC - SetRegistryKey(fRegistry.GetAppRegistryKey()); -#else - SetRegistryKey(L"faddenSoft"); -#endif - //LOGI("Registry key is '%ls'", m_pszRegistryKey); //LOGI("Profile name is '%ls'", m_pszProfileName); LOGI("Short command line is '%ls'", m_lpCmdLine);