1 // This file is part of Notepad++ project
2 // Copyright (C)2003 Don HO <don.h@free.fr>
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.
8 //
9 // Note that the GPL places important restrictions on "derived works", yet
10 // it does not provide a detailed definition of that term.  To avoid
11 // misunderstandings, we consider an application to constitute a
12 // "derivative work" for the purpose of this license if it does any of the
13 // following:
14 // 1. Integrates source code from Notepad++.
15 // 2. Integrates/includes/aggregates Notepad++ into a proprietary executable
16 //    installer, such as those produced by InstallShield.
17 // 3. Links to a library or executes a program that does any of the above.
18 //
19 // This program is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 // GNU General Public License for more details.
23 //
24 // You should have received a copy of the GNU General Public License
25 // along with this program; if not, write to the Free Software
26 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 /**
28  *
29  *
30  * License: GPL-2.0 or later
31  */
32 module npp_api.powereditor.wincontrols.staticdialog.staticdialog;
33 
34 
35 version (Windows):
36 version (Not_betterC):
37 
38 pragma(lib, "kernel32.lib");
39 pragma(lib, "user32.lib");
40 
41 private static import core.sys.windows.basetsd;
42 private static import core.sys.windows.winbase;
43 private static import core.sys.windows.windef;
44 private static import core.sys.windows.winuser;
45 private static import core.stdc.config;
46 private static import core.stdc.string;
47 private static import std.utf;
48 private static import std.format;
49 private static import npp_api.powereditor.misc.pluginsmanager.notepad_plus_msgs;
50 private static import npp_api.powereditor.wincontrols.window;
51 private static import npp_api.pluginfunc.npp_msgs;
52 
53 enum PosAlign
54 {
55 	left,
56 	right,
57 	top,
58 	bottom,
59 }
60 
61 /**
62  * The structure has more fields but are variable length
63  */
64 extern (C)
65 struct DLGTEMPLATEEX
66 {
67 	static import core.sys.windows.windef;
68 
69 	core.sys.windows.windef.WORD dlgVer;
70 	core.sys.windows.windef.WORD signature;
71 	core.sys.windows.windef.DWORD helpID;
72 	core.sys.windows.windef.DWORD exStyle;
73 	core.sys.windows.windef.DWORD style;
74 	core.sys.windows.windef.WORD cDlgItems;
75 	short x;
76 	short y;
77 	short cx;
78 	short cy;
79 }
80 
81 abstract class StaticDialog : npp_api.powereditor.wincontrols.window.Window
82 {
83 	static import core.sys.windows.basetsd;
84 	static import core.sys.windows.winbase;
85 	static import core.sys.windows.windef;
86 	static import core.sys.windows.winuser;
87 	static import core.stdc.config;
88 	static import core.stdc.string;
89 	static import std.utf;
90 	static import std.format;
91 	static import npp_api.powereditor.misc.pluginsmanager.notepad_plus_msgs;
92 	static import npp_api.pluginfunc.npp_msgs;
93 
94 public:
95 	nothrow @nogc
96 	~this()
97 
98 		do
99 		{
100 			if (this.isCreated()) {
101 				// Prevent run_dlgProc from doing anything, since its virtual
102 				core.sys.windows.winuser.SetWindowLongPtrW(this._hSelf, core.sys.windows.winuser.GWLP_USERDATA, 0);
103 
104 				this.destroy();
105 			}
106 		}
107 
108 	void create(void* dialog_p, int dialogID, bool isRTL = false, bool msgDestParent = true)
109 
110 		in
111 		{
112 			static assert(core.sys.windows.windef.LPARAM.sizeof >= dialog_p.sizeof);
113 			assert(dialog_p != null);
114 		}
115 
116 		do
117 		{
118 			if (isRTL) {
119 				core.sys.windows.winuser.DLGTEMPLATE* pMyDlgTemplate = core.sys.windows.windef.NULL;
120 				core.sys.windows.windef.HGLOBAL hMyDlgTemplate = this.makeRTLResource(dialogID, &pMyDlgTemplate);
121 				this._hSelf = core.sys.windows.winuser.CreateDialogIndirectParamW(this._hInst, cast(core.sys.windows.winuser.LPCDLGTEMPLATEW)(pMyDlgTemplate), this._hParent, &this.dlgProc, cast(core.sys.windows.windef.LPARAM)(dialog_p));
122 				core.sys.windows.winbase.GlobalFree(hMyDlgTemplate);
123 			} else {
124 				this._hSelf = core.sys.windows.winuser.CreateDialogParamW(this._hInst, core.sys.windows.winuser.MAKEINTRESOURCEW(dialogID), this._hParent, &this.dlgProc, cast(core.sys.windows.windef.LPARAM)(dialog_p));
125 			}
126 
127 			if (this._hSelf == core.sys.windows.windef.NULL) {
128 				throw new Exception(std.utf.toUTF8(std.format.format!("CreateDialogParamW() return NULL.\rGetLastError(): %d")(core.sys.windows.winbase.GetLastError())));
129 			}
130 
131 			// if the destination of message NPPM_MODELESSDIALOG is not its parent, then it's the grand-parent
132 			core.sys.windows.winuser.SendMessageW(((msgDestParent) ? (this._hParent) : (core.sys.windows.winuser.GetParent(this._hParent))), npp_api.powereditor.misc.pluginsmanager.notepad_plus_msgs.NPPM_MODELESSDIALOG, npp_api.powereditor.misc.pluginsmanager.notepad_plus_msgs.MODELESSDIALOGADD, cast(core.sys.windows.windef.WPARAM)(this._hSelf));
133 		}
134 
135 	pure nothrow @safe @nogc
136 	bool isCreated() const
137 
138 		do
139 		{
140 			return (this._hSelf != core.sys.windows.windef.NULL);
141 		}
142 
143 	nothrow @nogc
144 	void goToCenter()
145 
146 		do
147 		{
148 			core.sys.windows.windef.RECT rc;
149 			core.sys.windows.winuser.GetClientRect(this._hParent, &rc);
150 			core.sys.windows.windef.POINT center;
151 			center.x = rc.left + (rc.right - rc.left) / 2;
152 			center.y = rc.top + (rc.bottom - rc.top) / 2;
153 			core.sys.windows.winuser.ClientToScreen(this._hParent, &center);
154 
155 			int x = center.x - (this._rc.right - this._rc.left) / 2;
156 			int y = center.y - (this._rc.bottom - this._rc.top) / 2;
157 
158 			core.sys.windows.winuser.SetWindowPos(this._hSelf, core.sys.windows.winuser.HWND_TOP, x, y, this._rc.right - this._rc.left, this._rc.bottom - this._rc.top, core.sys.windows.winuser.SWP_SHOWWINDOW);
159 		}
160 
161 	nothrow @nogc
162 	override void destroy()
163 
164 		do
165 		{
166 			npp_api.pluginfunc.npp_msgs.send_NPPM_MODELESSDIALOG(this._hParent, npp_api.powereditor.misc.pluginsmanager.notepad_plus_msgs.MODELESSDIALOGREMOVE, this._hSelf);
167 			core.sys.windows.winuser.DestroyWindow(this._hSelf);
168 		}
169 
170 	nothrow @nogc
171 	override void display(bool toShow)
172 
173 		do
174 		{
175 			if (toShow) {
176 				// If the user has switched from a dual monitor to a single monitor since we last
177 				// displayed the dialog, then ensure that it's still visible on the single monitor.
178 				core.sys.windows.windef.RECT workAreaRect = {0};
179 				core.sys.windows.windef.RECT rc = {0};
180 				core.sys.windows.winuser.SystemParametersInfoW(core.sys.windows.winuser.SPI_GETWORKAREA, 0, &workAreaRect, 0);
181 				core.sys.windows.winuser.GetWindowRect(this._hSelf, &rc);
182 				int newLeft = rc.left;
183 				int newTop = rc.top;
184 				int margin = core.sys.windows.winuser.GetSystemMetrics(core.sys.windows.winuser.SM_CYSMCAPTION);
185 
186 				if (newLeft > core.sys.windows.winuser.GetSystemMetrics(core.sys.windows.winuser.SM_CXVIRTUALSCREEN) - margin) {
187 					newLeft -= rc.right - workAreaRect.right;
188 				}
189 
190 				if (newLeft + (rc.right - rc.left) < core.sys.windows.winuser.GetSystemMetrics(core.sys.windows.winuser.SM_XVIRTUALSCREEN) + margin) {
191 					newLeft = workAreaRect.left;
192 				}
193 
194 				if (newTop > core.sys.windows.winuser.GetSystemMetrics(core.sys.windows.winuser.SM_CYVIRTUALSCREEN) - margin) {
195 					newTop -= rc.bottom - workAreaRect.bottom;
196 				}
197 
198 				if (newTop + (rc.bottom - rc.top) < core.sys.windows.winuser.GetSystemMetrics(core.sys.windows.winuser.SM_YVIRTUALSCREEN) + margin) {
199 					newTop = workAreaRect.top;
200 				}
201 
202 				if ((newLeft != rc.left) || (newTop != rc.top)) // then the virtual screen size has shrunk
203 					// Remember that core.sys.windows.winuser.MoveWindow wants width/height.
204 				{
205 					core.sys.windows.winuser.MoveWindow(this._hSelf, newLeft, newTop, rc.right - rc.left, rc.bottom - rc.top, core.sys.windows.windef.TRUE);
206 				}
207 			}
208 
209 			super.display(toShow);
210 		}
211 
212 	nothrow @nogc
213 	core.sys.windows.windef.POINT getTopPoint(core.sys.windows.windef.HWND hwnd, bool isLeft)
214 
215 		do
216 		{
217 			core.sys.windows.windef.RECT rc;
218 			core.sys.windows.winuser.GetWindowRect(hwnd, &rc);
219 
220 			core.sys.windows.windef.POINT p;
221 
222 			if (isLeft) {
223 				p.x = rc.left;
224 			} else {
225 				p.x = rc.right;
226 			}
227 
228 			p.y = rc.top;
229 			core.sys.windows.winuser.ScreenToClient(this._hSelf, &p);
230 
231 			return p;
232 		}
233 
234 protected:
235 	core.sys.windows.windef.RECT _rc;
236 
237 	extern (Windows)
238 	nothrow @nogc
239 	static core.sys.windows.basetsd.INT_PTR dlgProc(core.sys.windows.windef.HWND hwnd, core.sys.windows.windef.UINT message, core.sys.windows.windef.WPARAM wParam, core.sys.windows.windef.LPARAM lParam)
240 
241 		in
242 		{
243 		}
244 
245 		do
246 		{
247 			switch (message) {
248 				case core.sys.windows.winuser.WM_INITDIALOG:
249 					StaticDialog* pStaticDlg = cast(StaticDialog*)(lParam);
250 
251 					(*pStaticDlg)._hSelf = hwnd;
252 					core.sys.windows.winuser.SetWindowLongPtrW(hwnd, core.sys.windows.winuser.GWLP_USERDATA, cast(core.sys.windows.basetsd.LONG_PTR)(lParam));
253 					core.sys.windows.winuser.GetWindowRect(hwnd, &((*pStaticDlg)._rc));
254 					(*pStaticDlg).run_dlgProc(message, wParam, lParam);
255 
256 					return core.sys.windows.windef.TRUE;
257 
258 				default:
259 					StaticDialog* pStaticDlg = cast(StaticDialog*)(core.sys.windows.winuser.GetWindowLongPtrW(hwnd, core.sys.windows.winuser.GWLP_USERDATA));
260 
261 					if (pStaticDlg == core.sys.windows.windef.NULL) {
262 						return core.sys.windows.windef.FALSE;
263 					}
264 
265 					return (*pStaticDlg).run_dlgProc(message, wParam, lParam);
266 			}
267 		}
268 
269 	extern (Windows)
270 	nothrow @nogc
271 	core.sys.windows.basetsd.INT_PTR run_dlgProc(core.sys.windows.windef.UINT message, core.sys.windows.windef.WPARAM wParam, core.sys.windows.windef.LPARAM lParam);
272 
273 	nothrow @nogc
274 	void alignWith(core.sys.windows.windef.HWND handle, core.sys.windows.windef.HWND handle2Align, .PosAlign pos, ref core.sys.windows.windef.POINT point)
275 
276 		in
277 		{
278 		}
279 
280 		do
281 		{
282 			core.sys.windows.windef.RECT rc, rc2;
283 			core.sys.windows.winuser.GetWindowRect(handle, &rc);
284 
285 			point.x = rc.left;
286 			point.y = rc.top;
287 
288 			switch (pos) {
289 				case .PosAlign.left:
290 					core.sys.windows.winuser.GetWindowRect(handle2Align, &rc2);
291 					point.x -= rc2.right - rc2.left;
292 
293 					break;
294 
295 				case .PosAlign.right:
296 					core.sys.windows.winuser.GetWindowRect(handle, &rc2);
297 					point.x += rc2.right - rc2.left;
298 
299 					break;
300 
301 				case .PosAlign.top:
302 					core.sys.windows.winuser.GetWindowRect(handle2Align, &rc2);
303 					point.y -= rc2.bottom - rc2.top;
304 
305 					break;
306 
307 				case .PosAlign.bottom:
308 					core.sys.windows.winuser.GetWindowRect(handle, &rc2);
309 					point.y += rc2.bottom - rc2.top;
310 
311 					break;
312 
313 				default:
314 					break;
315 			}
316 
317 			core.sys.windows.winuser.ScreenToClient(this._hSelf, &point);
318 		}
319 
320 	nothrow @nogc
321 	core.sys.windows.windef.HGLOBAL makeRTLResource(int dialogID, core.sys.windows.winuser.DLGTEMPLATE** ppMyDlgTemplate)
322 
323 		in
324 		{
325 			assert(ppMyDlgTemplate != null);
326 		}
327 
328 		do
329 		{
330 			// Get Dlg Template resource
331 			core.sys.windows.windef.HRSRC hDialogRC = core.sys.windows.winbase.FindResourceW(this._hInst, core.sys.windows.winuser.MAKEINTRESOURCEW(dialogID), core.sys.windows.winuser.RT_DIALOG);
332 
333 			if (hDialogRC == core.sys.windows.windef.NULL) {
334 				return core.sys.windows.windef.NULL;
335 			}
336 
337 			core.sys.windows.windef.HGLOBAL hDlgTemplate = core.sys.windows.winbase.LoadResource(this._hInst, hDialogRC);
338 
339 			if (hDlgTemplate == core.sys.windows.windef.NULL) {
340 				return core.sys.windows.windef.NULL;
341 			}
342 
343 			core.sys.windows.winuser.DLGTEMPLATE* pDlgTemplate = cast(core.sys.windows.winuser.DLGTEMPLATE*)(core.sys.windows.winbase.LockResource(hDlgTemplate));
344 
345 			if (pDlgTemplate == core.sys.windows.windef.NULL) {
346 				return core.sys.windows.windef.NULL;
347 			}
348 
349 			// Duplicate Dlg Template resource
350 			core.stdc.config.c_ulong sizeDlg = core.sys.windows.winbase.SizeofResource(this._hInst, hDialogRC);
351 			core.sys.windows.windef.HGLOBAL hMyDlgTemplate = core.sys.windows.winbase.GlobalAlloc(core.sys.windows.winbase.GPTR, sizeDlg);
352 			*ppMyDlgTemplate = cast(core.sys.windows.winuser.DLGTEMPLATE*)(core.sys.windows.winbase.GlobalLock(hMyDlgTemplate));
353 
354 			core.stdc..string.memcpy(*ppMyDlgTemplate, pDlgTemplate, sizeDlg);
355 
356 			.DLGTEMPLATEEX* pMyDlgTemplateEx = cast(.DLGTEMPLATEEX*)(*ppMyDlgTemplate);
357 
358 			if ((*pMyDlgTemplateEx).signature == 0xFFFF) {
359 				(*pMyDlgTemplateEx).exStyle |= core.sys.windows.winuser.WS_EX_LAYOUTRTL;
360 			} else {
361 				(*ppMyDlgTemplate).dwExtendedStyle |= core.sys.windows.winuser.WS_EX_LAYOUTRTL;
362 			}
363 
364 			return hMyDlgTemplate;
365 		}
366 }