Skip to content

Commit 3d89efa

Browse files
committed
Update StaticDialog from plugintemplate (2025-10-11):
https://github.com/npp-plugins/plugintemplate/tree/master/src/DockingFeature
1 parent 46b5cb3 commit 3d89efa

File tree

2 files changed

+234
-74
lines changed

2 files changed

+234
-74
lines changed

external/npp/StaticDialog.cpp

Lines changed: 189 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,56 @@
1-
//this file is part of notepad++
2-
//Copyright (C)2003 Don HO ( [email protected] )
3-
//
4-
//This program is free software; you can redistribute it and/or
5-
//modify it under the terms of the GNU General Public License
6-
//as published by the Free Software Foundation; either
7-
//version 2 of the License, or (at your option) any later version.
1+
// This file is part of Notepad++ project
2+
// Copyright (C)2022 Don HO <[email protected]>
3+
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// at your option any later version.
88
//
9-
//This program is distributed in the hope that it will be useful,
10-
//but WITHOUT ANY WARRANTY; without even the implied warranty of
11-
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-
//GNU General Public License for more details.
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
1313
//
14-
//You should have received a copy of the GNU General Public License
15-
//along with this program; if not, write to the Free Software
16-
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
14+
// You should have received a copy of the GNU General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
1716

1817
#include <stdio.h>
18+
#include <string>
19+
#include <windows.h>
1920
#include "StaticDialog.h"
2021

22+
StaticDialog::~StaticDialog()
23+
{
24+
if (isCreated())
25+
{
26+
// Prevent run_dlgProc from doing anything, since its virtual
27+
::SetWindowLongPtr(_hSelf, GWLP_USERDATA, NULL);
28+
destroy();
29+
}
30+
}
31+
32+
void StaticDialog::destroy()
33+
{
34+
::SendMessage(_hParent, NPPM_MODELESSDIALOG, MODELESSDIALOGREMOVE, reinterpret_cast<WPARAM>(_hSelf));
35+
::DestroyWindow(_hSelf);
36+
}
37+
38+
POINT StaticDialog::getTopPoint(HWND hwnd, bool isLeft) const
39+
{
40+
RECT rc;
41+
::GetWindowRect(hwnd, &rc);
42+
43+
POINT p;
44+
if (isLeft)
45+
p.x = rc.left;
46+
else
47+
p.x = rc.right;
48+
49+
p.y = rc.top;
50+
::ScreenToClient(_hSelf, &p);
51+
return p;
52+
}
53+
2154
void StaticDialog::goToCenter()
2255
{
2356
RECT rc;
@@ -33,6 +66,111 @@ void StaticDialog::goToCenter()
3366
::SetWindowPos(_hSelf, HWND_TOP, x, y, _rc.right - _rc.left, _rc.bottom - _rc.top, SWP_SHOWWINDOW);
3467
}
3568

69+
void StaticDialog::display(bool toShow, bool enhancedPositioningCheckWhenShowing) const
70+
{
71+
if (toShow)
72+
{
73+
if (enhancedPositioningCheckWhenShowing)
74+
{
75+
RECT testPositionRc, candidateRc;
76+
77+
getWindowRect(testPositionRc);
78+
79+
candidateRc = getViewablePositionRect(testPositionRc);
80+
81+
if ((testPositionRc.left != candidateRc.left) || (testPositionRc.top != candidateRc.top))
82+
{
83+
::MoveWindow(_hSelf, candidateRc.left, candidateRc.top,
84+
candidateRc.right - candidateRc.left, candidateRc.bottom - candidateRc.top, TRUE);
85+
}
86+
}
87+
else
88+
{
89+
// If the user has switched from a dual monitor to a single monitor since we last
90+
// displayed the dialog, then ensure that it's still visible on the single monitor.
91+
RECT workAreaRect = { 0 };
92+
RECT rc = { 0 };
93+
::SystemParametersInfo(SPI_GETWORKAREA, 0, &workAreaRect, 0);
94+
::GetWindowRect(_hSelf, &rc);
95+
int newLeft = rc.left;
96+
int newTop = rc.top;
97+
int margin = ::GetSystemMetrics(SM_CYSMCAPTION);
98+
99+
if (newLeft > ::GetSystemMetrics(SM_CXVIRTUALSCREEN) - margin)
100+
newLeft -= rc.right - workAreaRect.right;
101+
if (newLeft + (rc.right - rc.left) < ::GetSystemMetrics(SM_XVIRTUALSCREEN) + margin)
102+
newLeft = workAreaRect.left;
103+
if (newTop > ::GetSystemMetrics(SM_CYVIRTUALSCREEN) - margin)
104+
newTop -= rc.bottom - workAreaRect.bottom;
105+
if (newTop + (rc.bottom - rc.top) < ::GetSystemMetrics(SM_YVIRTUALSCREEN) + margin)
106+
newTop = workAreaRect.top;
107+
108+
if ((newLeft != rc.left) || (newTop != rc.top)) // then the virtual screen size has shrunk
109+
// Remember that MoveWindow wants width/height.
110+
::MoveWindow(_hSelf, newLeft, newTop, rc.right - rc.left, rc.bottom - rc.top, TRUE);
111+
}
112+
}
113+
114+
Window::display(toShow);
115+
}
116+
117+
RECT StaticDialog::getViewablePositionRect(RECT testPositionRc) const
118+
{
119+
HMONITOR hMon = ::MonitorFromRect(&testPositionRc, MONITOR_DEFAULTTONULL);
120+
121+
MONITORINFO mi;
122+
mi.cbSize = sizeof(MONITORINFO);
123+
124+
bool rectPosViewableWithoutChange = false;
125+
126+
if (hMon != NULL)
127+
{
128+
// rect would be at least partially visible on a monitor
129+
130+
::GetMonitorInfo(hMon, &mi);
131+
132+
int margin = ::GetSystemMetrics(SM_CYBORDER) + ::GetSystemMetrics(SM_CYSIZEFRAME) + ::GetSystemMetrics(SM_CYCAPTION);
133+
134+
// 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
135+
if ((testPositionRc.top >= mi.rcWork.top) && (testPositionRc.top + margin <= mi.rcWork.bottom) &&
136+
// require that some reasonable amount of width of the title bar be in the viewable area:
137+
(testPositionRc.right - (margin * 2) > mi.rcWork.left) && (testPositionRc.left + (margin * 2) < mi.rcWork.right))
138+
{
139+
rectPosViewableWithoutChange = true;
140+
}
141+
}
142+
else
143+
{
144+
// rect would not have been visible on a monitor; get info about the nearest monitor to it
145+
146+
hMon = ::MonitorFromRect(&testPositionRc, MONITOR_DEFAULTTONEAREST);
147+
148+
::GetMonitorInfo(hMon, &mi);
149+
}
150+
151+
RECT returnRc = testPositionRc;
152+
153+
if (!rectPosViewableWithoutChange)
154+
{
155+
// reposition rect so that it would be viewable on current/nearest monitor, centering if reasonable
156+
157+
LONG testRectWidth = testPositionRc.right - testPositionRc.left;
158+
LONG testRectHeight = testPositionRc.bottom - testPositionRc.top;
159+
LONG monWidth = mi.rcWork.right - mi.rcWork.left;
160+
LONG monHeight = mi.rcWork.bottom - mi.rcWork.top;
161+
162+
returnRc.left = mi.rcWork.left;
163+
if (testRectWidth < monWidth) returnRc.left += (monWidth - testRectWidth) / 2;
164+
returnRc.right = returnRc.left + testRectWidth;
165+
166+
returnRc.top = mi.rcWork.top;
167+
if (testRectHeight < monHeight) returnRc.top += (monHeight - testRectHeight) / 2;
168+
returnRc.bottom = returnRc.top + testRectHeight;
169+
}
170+
171+
return returnRc;
172+
}
173+
36174
HGLOBAL StaticDialog::makeRTLResource(int dialogID, DLGTEMPLATE **ppMyDlgTemplate)
37175
{
38176
// Get Dlg Template resource
@@ -44,32 +182,49 @@ HGLOBAL StaticDialog::makeRTLResource(int dialogID, DLGTEMPLATE **ppMyDlgTemplat
44182
if (!hDlgTemplate)
45183
return NULL;
46184

47-
DLGTEMPLATE *pDlgTemplate = reinterpret_cast<DLGTEMPLATE *>(::LockResource(hDlgTemplate));
185+
DLGTEMPLATE *pDlgTemplate = static_cast<DLGTEMPLATE *>(::LockResource(hDlgTemplate));
48186
if (!pDlgTemplate)
49187
return NULL;
50188

51189
// Duplicate Dlg Template resource
52190
unsigned long sizeDlg = ::SizeofResource(_hInst, hDialogRC);
53191
HGLOBAL hMyDlgTemplate = ::GlobalAlloc(GPTR, sizeDlg);
54-
if (hMyDlgTemplate)
55-
{
56-
*ppMyDlgTemplate = reinterpret_cast<DLGTEMPLATE *>(::GlobalLock(hMyDlgTemplate));
57-
if (*ppMyDlgTemplate)
58-
{
59-
::memcpy(*ppMyDlgTemplate, pDlgTemplate, sizeDlg);
60-
61-
DLGTEMPLATEEX *pMyDlgTemplateEx = reinterpret_cast<DLGTEMPLATEEX *>(*ppMyDlgTemplate);
62-
if (pMyDlgTemplateEx->signature == 0xFFFF)
63-
pMyDlgTemplateEx->exStyle |= WS_EX_LAYOUTRTL;
64-
else
65-
(*ppMyDlgTemplate)->dwExtendedStyle |= WS_EX_LAYOUTRTL;
66-
}
67-
}
192+
*ppMyDlgTemplate = static_cast<DLGTEMPLATE *>(::GlobalLock(hMyDlgTemplate));
193+
194+
::memcpy(*ppMyDlgTemplate, pDlgTemplate, sizeDlg);
195+
196+
DLGTEMPLATEEX *pMyDlgTemplateEx = reinterpret_cast<DLGTEMPLATEEX *>(*ppMyDlgTemplate);
197+
if (pMyDlgTemplateEx->signature == 0xFFFF)
198+
pMyDlgTemplateEx->exStyle |= WS_EX_LAYOUTRTL;
199+
else
200+
(*ppMyDlgTemplate)->dwExtendedStyle |= WS_EX_LAYOUTRTL;
68201

69202
return hMyDlgTemplate;
70203
}
71204

72-
void StaticDialog::create(int dialogID, bool isRTL)
205+
std::wstring GetLastErrorAsString(DWORD errorCode)
206+
{
207+
std::wstring errorMsg(L"");
208+
// Get the error message, if any.
209+
// If both error codes (passed error n GetLastError) are 0, then return empty
210+
if (errorCode == 0)
211+
errorCode = GetLastError();
212+
if (errorCode == 0)
213+
return errorMsg; //No error message has been recorded
214+
215+
LPWSTR messageBuffer = nullptr;
216+
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
217+
nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr);
218+
219+
errorMsg += messageBuffer;
220+
221+
//Free the buffer.
222+
LocalFree(messageBuffer);
223+
224+
return errorMsg;
225+
}
226+
227+
void StaticDialog::create(int dialogID, bool isRTL, bool msgDestParent)
73228
{
74229
if (isRTL)
75230
{
@@ -83,15 +238,14 @@ void StaticDialog::create(int dialogID, bool isRTL)
83238

84239
if (!_hSelf)
85240
{
86-
DWORD err = ::GetLastError();
87-
char errMsg[256] {};
88-
sprintf_s(errMsg, "CreateDialogParam() return NULL.\rGetLastError() == %lu", err);
89-
::MessageBoxA(NULL, errMsg, "In StaticDialog::create()", MB_OK);
241+
std::wstring errMsg = TEXT("CreateDialogParam() return NULL.\rGetLastError(): ");
242+
errMsg += GetLastErrorAsString(0);
243+
::MessageBox(NULL, errMsg.c_str(), TEXT("In StaticDialog::create()"), MB_OK);
90244
return;
91245
}
92246

93247
// if the destination of message NPPM_MODELESSDIALOG is not its parent, then it's the grand-parent
94-
::SendMessage(_hParent, NPPM_MODELESSDIALOG, MODELESSDIALOGADD, reinterpret_cast<WPARAM>(_hSelf));
248+
::SendMessage(msgDestParent ? _hParent : (::GetParent(_hParent)), NPPM_MODELESSDIALOG, MODELESSDIALOGADD, reinterpret_cast<WPARAM>(_hSelf));
95249
}
96250

97251
INT_PTR CALLBACK StaticDialog::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
@@ -157,4 +311,3 @@ void StaticDialog::alignWith(HWND handle, HWND handle2Align, PosAlign pos, POINT
157311

158312
::ScreenToClient(_hSelf, &point);
159313
}
160-

external/npp/StaticDialog.h

Lines changed: 45 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,77 @@
1-
//this file is part of notepad++
2-
//Copyright (C)2003 Don HO ( [email protected] )
3-
//
4-
//This program is free software; you can redistribute it and/or
5-
//modify it under the terms of the GNU General Public License
6-
//as published by the Free Software Foundation; either
7-
//version 2 of the License, or (at your option) any later version.
1+
// This file is part of Notepad++ project
2+
// Copyright (C)2022 Don HO <[email protected]>
3+
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// at your option any later version.
88
//
9-
//This program is distributed in the hope that it will be useful,
10-
//but WITHOUT ANY WARRANTY; without even the implied warranty of
11-
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-
//GNU General Public License for more details.
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
1313
//
14-
//You should have received a copy of the GNU General Public License
15-
//along with this program; if not, write to the Free Software
16-
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17-
14+
// You should have received a copy of the GNU General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
1816
#pragma once
19-
20-
#include "Window.h"
2117
#include "Notepad_plus_msgs.h"
22-
typedef HRESULT(WINAPI* ETDTProc) (HWND, DWORD);
18+
#include "Window.h"
19+
20+
typedef HRESULT (WINAPI * ETDTProc) (HWND, DWORD);
2321

2422
enum class PosAlign { left, right, top, bottom };
2523

26-
struct DLGTEMPLATEEX {
24+
struct DLGTEMPLATEEX
25+
{
2726
WORD dlgVer;
2827
WORD signature;
2928
DWORD helpID;
3029
DWORD exStyle;
31-
DWORD style;
30+
DWORD style;
3231
WORD cDlgItems;
3332
short x;
34-
short y;
33+
short y;
3534
short cx;
3635
short cy;
3736
// The structure has more fields but are variable length
38-
} ;
37+
};
3938

4039
class StaticDialog : public Window
4140
{
4241
public :
43-
StaticDialog() : Window() {};
44-
~StaticDialog(){
45-
if (isCreated()) {
46-
::SetWindowLongPtr(_hSelf, GWLP_USERDATA, (long)NULL); //Prevent run_dlgProc from doing anything, since its virtual
47-
destroy();
48-
}
49-
};
50-
virtual void create(int dialogID, bool isRTL = false);
42+
virtual ~StaticDialog();
43+
44+
virtual void create(int dialogID, bool isRTL = false, bool msgDestParent = true);
5145

5246
virtual bool isCreated() const {
5347
return (_hSelf != NULL);
54-
};
48+
}
5549

5650
void goToCenter();
57-
void destroy() {
58-
::SendMessage(_hParent, NPPM_MODELESSDIALOG, MODELESSDIALOGREMOVE, (WPARAM)_hSelf);
59-
::DestroyWindow(_hSelf);
60-
};
6151

62-
protected :
63-
RECT _rc{};
52+
void display(bool toShow = true, bool enhancedPositioningCheckWhenShowing = false) const;
53+
54+
RECT getViewablePositionRect(RECT testRc) const;
55+
56+
POINT getTopPoint(HWND hwnd, bool isLeft = true) const;
57+
58+
bool isCheckedOrNot(int checkControlID) const
59+
{
60+
return (BST_CHECKED == ::SendMessage(::GetDlgItem(_hSelf, checkControlID), BM_GETCHECK, 0, 0));
61+
}
62+
63+
void setChecked(int checkControlID, bool checkOrNot = true) const
64+
{
65+
::SendDlgItemMessage(_hSelf, checkControlID, BM_SETCHECK, checkOrNot ? BST_CHECKED : BST_UNCHECKED, 0);
66+
}
67+
68+
virtual void destroy() override;
69+
70+
protected:
71+
RECT _rc;
6472
static INT_PTR CALLBACK dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
6573
virtual INT_PTR CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) = 0;
6674

6775
void alignWith(HWND handle, HWND handle2Align, PosAlign pos, POINT & point);
6876
HGLOBAL makeRTLResource(int dialogID, DLGTEMPLATE **ppMyDlgTemplate);
6977
};
70-

0 commit comments

Comments
 (0)