// This file is part of Notepad++ project // Copyright (C)2024 Don HO // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // at your option any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "stdafx.h" //ADDED BY PYTHONSCRIPT #include #include #include "StaticDialog.h" #include "Common.h" #include "NppDarkMode.h" StaticDialog::~StaticDialog() { if (isCreated()) { // Prevent run_dlgProc from doing anything, since its virtual ::SetWindowLongPtr(_hSelf, GWLP_USERDATA, 0); destroy(); } } void StaticDialog::destroy() { ::SendMessage(_hParent, NPPM_MODELESSDIALOG, MODELESSDIALOGREMOVE, reinterpret_cast(_hSelf)); ::DestroyWindow(_hSelf); } void StaticDialog::getMappedChildRect(HWND hChild, RECT& rcChild) const { ::GetClientRect(hChild, &rcChild); ::MapWindowPoints(hChild, _hSelf, reinterpret_cast(&rcChild), 2); } void StaticDialog::getMappedChildRect(int idChild, RECT& rcChild) const { const HWND hChild = ::GetDlgItem(_hSelf, idChild); getMappedChildRect(hChild, rcChild); } void StaticDialog::redrawDlgItem(const int nIDDlgItem, bool forceUpdate) const { RECT rcDlgItem{}; const HWND hDlgItem = ::GetDlgItem(_hSelf, nIDDlgItem); getMappedChildRect(hDlgItem, rcDlgItem); ::InvalidateRect(_hSelf, &rcDlgItem, TRUE); if (forceUpdate) ::UpdateWindow(hDlgItem); } POINT StaticDialog::getTopPoint(HWND hwnd, bool isLeft) const { RECT rc{}; ::GetWindowRect(hwnd, &rc); POINT p{}; if (isLeft) p.x = rc.left; else p.x = rc.right; p.y = rc.top; ::ScreenToClient(_hSelf, &p); return p; } void StaticDialog::goToCenter(UINT swpFlags) { RECT rc{}; ::GetClientRect(_hParent, &rc); if ((rc.left == rc.right) || (rc.top == rc.bottom)) swpFlags |= SWP_NOSIZE; // sizing has no sense here POINT center{}; center.x = rc.left + (rc.right - rc.left)/2; center.y = rc.top + (rc.bottom - rc.top)/2; ::ClientToScreen(_hParent, ¢er); if ((center.x == -32000) && (center.y == -32000)) // https://devblogs.microsoft.com/oldnewthing/20041028-00/?p=37453 swpFlags |= SWP_NOMOVE; // moving has no sense here (owner wnd is minimized) int x = center.x - (_rc.right - _rc.left)/2; int y = center.y - (_rc.bottom - _rc.top)/2; ::SetWindowPos(_hSelf, HWND_TOP, x, y, _rc.right - _rc.left, _rc.bottom - _rc.top, swpFlags); } void StaticDialog::display(bool toShow, bool enhancedPositioningCheckWhenShowing) const { if (toShow) { if (enhancedPositioningCheckWhenShowing) { RECT testPositionRc{}, candidateRc{}; getWindowRect(testPositionRc); candidateRc = getViewablePositionRect(testPositionRc); if ((testPositionRc.left != candidateRc.left) || (testPositionRc.top != candidateRc.top)) { ::MoveWindow(_hSelf, candidateRc.left, candidateRc.top, candidateRc.right - candidateRc.left, candidateRc.bottom - candidateRc.top, TRUE); } } else { // If the user has switched from a dual monitor to a single monitor since we last // displayed the dialog, then ensure that it's still visible on the single monitor. RECT workAreaRect{}; RECT rc{}; ::SystemParametersInfo(SPI_GETWORKAREA, 0, &workAreaRect, 0); ::GetWindowRect(_hSelf, &rc); int newLeft = rc.left; int newTop = rc.top; int margin = ::GetSystemMetrics(SM_CYSMCAPTION); if (newLeft > ::GetSystemMetrics(SM_CXVIRTUALSCREEN) - margin) newLeft -= rc.right - workAreaRect.right; if (newLeft + (rc.right - rc.left) < ::GetSystemMetrics(SM_XVIRTUALSCREEN) + margin) newLeft = workAreaRect.left; if (newTop > ::GetSystemMetrics(SM_CYVIRTUALSCREEN) - margin) newTop -= rc.bottom - workAreaRect.bottom; if (newTop + (rc.bottom - rc.top) < ::GetSystemMetrics(SM_YVIRTUALSCREEN) + margin) newTop = workAreaRect.top; if ((newLeft != rc.left) || (newTop != rc.top)) // then the virtual screen size has shrunk // Remember that MoveWindow wants width/height. ::MoveWindow(_hSelf, newLeft, newTop, rc.right - rc.left, rc.bottom - rc.top, TRUE); } } Window::display(toShow); } RECT StaticDialog::getViewablePositionRect(RECT testPositionRc) const { HMONITOR hMon = ::MonitorFromRect(&testPositionRc, MONITOR_DEFAULTTONULL); MONITORINFO mi{}; mi.cbSize = sizeof(MONITORINFO); bool rectPosViewableWithoutChange = false; if (hMon != NULL) { // rect would be at least partially visible on a monitor ::GetMonitorInfo(hMon, &mi); int margin = ::GetSystemMetrics(SM_CYBORDER) + ::GetSystemMetrics(SM_CYSIZEFRAME) + ::GetSystemMetrics(SM_CYCAPTION); // require that the title bar of the window be in a viewable place so the user can see it to grab it with the mouse if ((testPositionRc.top >= mi.rcWork.top) && (testPositionRc.top + margin <= mi.rcWork.bottom) && // require that some reasonable amount of width of the title bar be in the viewable area: (testPositionRc.right - (margin * 2) > mi.rcWork.left) && (testPositionRc.left + (margin * 2) < mi.rcWork.right)) { rectPosViewableWithoutChange = true; } } else { // rect would not have been visible on a monitor; get info about the nearest monitor to it hMon = ::MonitorFromRect(&testPositionRc, MONITOR_DEFAULTTONEAREST); ::GetMonitorInfo(hMon, &mi); } RECT returnRc = testPositionRc; if (!rectPosViewableWithoutChange) { // reposition rect so that it would be viewable on current/nearest monitor, centering if reasonable LONG testRectWidth = testPositionRc.right - testPositionRc.left; LONG testRectHeight = testPositionRc.bottom - testPositionRc.top; LONG monWidth = mi.rcWork.right - mi.rcWork.left; LONG monHeight = mi.rcWork.bottom - mi.rcWork.top; returnRc.left = mi.rcWork.left; if (testRectWidth < monWidth) returnRc.left += (monWidth - testRectWidth) / 2; returnRc.right = returnRc.left + testRectWidth; returnRc.top = mi.rcWork.top; if (testRectHeight < monHeight) returnRc.top += (monHeight - testRectHeight) / 2; returnRc.bottom = returnRc.top + testRectHeight; } return returnRc; } HGLOBAL StaticDialog::makeRTLResource(int dialogID, DLGTEMPLATE **ppMyDlgTemplate) { // Get Dlg Template resource HRSRC hDialogRC = ::FindResource(_hInst, MAKEINTRESOURCE(dialogID), RT_DIALOG); if (!hDialogRC) return NULL; HGLOBAL hDlgTemplate = ::LoadResource(_hInst, hDialogRC); if (!hDlgTemplate) return NULL; DLGTEMPLATE *pDlgTemplate = static_cast(::LockResource(hDlgTemplate)); if (!pDlgTemplate) return NULL; // Duplicate Dlg Template resource unsigned long sizeDlg = ::SizeofResource(_hInst, hDialogRC); HGLOBAL hMyDlgTemplate = ::GlobalAlloc(GPTR, sizeDlg); if (!hMyDlgTemplate) return nullptr; *ppMyDlgTemplate = static_cast(::GlobalLock(hMyDlgTemplate)); if (!*ppMyDlgTemplate) return nullptr; ::memcpy(*ppMyDlgTemplate, pDlgTemplate, sizeDlg); DLGTEMPLATEEX* pMyDlgTemplateEx = reinterpret_cast(*ppMyDlgTemplate); if (!pMyDlgTemplateEx) return nullptr; if (pMyDlgTemplateEx->signature == 0xFFFF) pMyDlgTemplateEx->exStyle |= WS_EX_LAYOUTRTL; else (*ppMyDlgTemplate)->dwExtendedStyle |= WS_EX_LAYOUTRTL; return hMyDlgTemplate; } void StaticDialog::create(int dialogID, bool isRTL, bool msgDestParent) { if (isRTL) { DLGTEMPLATE *pMyDlgTemplate = NULL; HGLOBAL hMyDlgTemplate = makeRTLResource(dialogID, &pMyDlgTemplate); _hSelf = ::CreateDialogIndirectParam(_hInst, pMyDlgTemplate, _hParent, dlgProc, reinterpret_cast(this)); ::GlobalFree(hMyDlgTemplate); } else _hSelf = ::CreateDialogParam(_hInst, MAKEINTRESOURCE(dialogID), _hParent, dlgProc, reinterpret_cast(this)); if (!_hSelf) { generic_string errMsg = TEXT("CreateDialogParam() return NULL.\rGetLastError(): "); errMsg += GetLastErrorAsString(); ::MessageBox(NULL, errMsg.c_str(), TEXT("In StaticDialog::create()"), MB_OK); return; } NppDarkMode::setDarkTitleBar(_hSelf); // if the destination of message NPPM_MODELESSDIALOG is not its parent, then it's the grand-parent ::SendMessage(msgDestParent ? _hParent : (::GetParent(_hParent)), NPPM_MODELESSDIALOG, MODELESSDIALOGADD, reinterpret_cast(_hSelf)); } intptr_t CALLBACK StaticDialog::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: { NppDarkMode::setDarkTitleBar(hwnd); StaticDialog *pStaticDlg = reinterpret_cast(lParam); pStaticDlg->_hSelf = hwnd; ::SetWindowLongPtr(hwnd, GWLP_USERDATA, static_cast(lParam)); ::GetWindowRect(hwnd, &(pStaticDlg->_rc)); pStaticDlg->run_dlgProc(message, wParam, lParam); return TRUE; } default: { StaticDialog *pStaticDlg = reinterpret_cast(::GetWindowLongPtr(hwnd, GWLP_USERDATA)); if (!pStaticDlg) return FALSE; return pStaticDlg->run_dlgProc(message, wParam, lParam); } } }