1 // Windows/Control/Dialog.cpp
2
3 #include "StdAfx.h"
4
5 // #include "../../Windows/DLL.h"
6
7 #ifndef _UNICODE
8 #include "../../Common/StringConvert.h"
9 #endif
10
11 #include "Dialog.h"
12
13 extern HINSTANCE g_hInstance;
14 #ifndef _UNICODE
15 extern bool g_IsNT;
16 #endif
17
18 namespace NWindows {
19 namespace NControl {
20
21 static
22 #ifdef Z7_OLD_WIN_SDK
23 BOOL
24 #else
25 INT_PTR
26 #endif
27 APIENTRY
DialogProcedure(HWND dialogHWND,UINT message,WPARAM wParam,LPARAM lParam)28 DialogProcedure(HWND dialogHWND, UINT message, WPARAM wParam, LPARAM lParam)
29 {
30 CWindow tempDialog(dialogHWND);
31 if (message == WM_INITDIALOG)
32 tempDialog.SetUserDataLongPtr(lParam);
33 CDialog *dialog = (CDialog *)(tempDialog.GetUserDataLongPtr());
34 if (dialog == NULL)
35 return FALSE;
36 if (message == WM_INITDIALOG)
37 dialog->Attach(dialogHWND);
38
39 /* MSDN: The dialog box procedure should return
40 TRUE - if it processed the message
41 FALSE - if it did not process the message
42 If the dialog box procedure returns FALSE,
43 the dialog manager performs the default dialog operation in response to the message.
44 */
45
46 try { return BoolToBOOL(dialog->OnMessage(message, wParam, lParam)); }
47 catch(...) { return TRUE; }
48 }
49
OnMessage(UINT message,WPARAM wParam,LPARAM lParam)50 bool CDialog::OnMessage(UINT message, WPARAM wParam, LPARAM lParam)
51 {
52 switch (message)
53 {
54 case WM_INITDIALOG: return OnInit();
55 case WM_COMMAND: return OnCommand(HIWORD(wParam), LOWORD(wParam), lParam);
56 case WM_NOTIFY: return OnNotify((UINT)wParam, (LPNMHDR) lParam);
57 case WM_TIMER: return OnTimer(wParam, lParam);
58 case WM_SIZE: return OnSize(wParam, LOWORD(lParam), HIWORD(lParam));
59 case WM_DESTROY: return OnDestroy();
60 case WM_HELP: OnHelp(); return true;
61 /*
62 OnHelp(
63 #ifdef UNDER_CE
64 (void *)
65 #else
66 (LPHELPINFO)
67 #endif
68 lParam);
69 return true;
70 */
71 default: return false;
72 }
73 }
74
75 /*
76 bool CDialog::OnCommand2(WPARAM wParam, LPARAM lParam)
77 {
78 return OnCommand(HIWORD(wParam), LOWORD(wParam), lParam);
79 }
80 */
81
OnCommand(unsigned code,unsigned itemID,LPARAM lParam)82 bool CDialog::OnCommand(unsigned code, unsigned itemID, LPARAM lParam)
83 {
84 if (code == BN_CLICKED)
85 return OnButtonClicked(itemID, (HWND)lParam);
86 return false;
87 }
88
OnButtonClicked(unsigned buttonID,HWND)89 bool CDialog::OnButtonClicked(unsigned buttonID, HWND /* buttonHWND */)
90 {
91 switch (buttonID)
92 {
93 case IDOK: OnOK(); break;
94 case IDCANCEL: OnCancel(); break;
95 case IDCLOSE: OnClose(); break;
96 case IDCONTINUE: OnContinue(); break;
97 case IDHELP: OnHelp(); break;
98 default: return false;
99 }
100 return true;
101 }
102
103 #ifndef UNDER_CE
104 /* in win2000/win98 : monitor functions are supported.
105 We need dynamic linking, if we want nt4/win95 support in program.
106 Even if we compile the code with low (WINVER) value, we still
107 want to use monitor functions. So we declare missing functions here */
108 // #if (WINVER < 0x0500)
109 #ifndef MONITOR_DEFAULTTOPRIMARY
110 extern "C" {
111 DECLARE_HANDLE(HMONITOR);
112 #define MONITOR_DEFAULTTOPRIMARY 0x00000001
113 typedef struct tagMONITORINFO
114 {
115 DWORD cbSize;
116 RECT rcMonitor;
117 RECT rcWork;
118 DWORD dwFlags;
119 } MONITORINFO, *LPMONITORINFO;
120 WINUSERAPI HMONITOR WINAPI MonitorFromWindow(HWND hwnd, DWORD dwFlags);
121 WINUSERAPI BOOL WINAPI GetMonitorInfoA(HMONITOR hMonitor, LPMONITORINFO lpmi);
122 }
123 #endif
124 #endif
125
GetWorkAreaRect(RECT * rect,HWND hwnd)126 static bool GetWorkAreaRect(RECT *rect, HWND hwnd)
127 {
128 if (hwnd)
129 {
130 #ifndef UNDER_CE
131 /* MonitorFromWindow() is supported in Win2000+
132 MonitorFromWindow() : retrieves a handle to the display monitor that has the
133 largest area of intersection with the bounding rectangle of a specified window.
134 dwFlags: Determines the function's return value if the window does not intersect any display monitor.
135 MONITOR_DEFAULTTONEAREST : Returns display that is nearest to the window.
136 MONITOR_DEFAULTTONULL : Returns NULL.
137 MONITOR_DEFAULTTOPRIMARY : Returns the primary display monitor.
138 */
139 const HMONITOR hmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
140 if (hmon)
141 {
142 MONITORINFO mi;
143 memset(&mi, 0, sizeof(mi));
144 mi.cbSize = sizeof(mi);
145 if (GetMonitorInfoA(hmon, &mi))
146 {
147 *rect = mi.rcWork;
148 return true;
149 }
150 }
151 #endif
152 }
153
154 /* Retrieves the size of the work area on the primary display monitor.
155 The work area is the portion of the screen not obscured
156 by the system taskbar or by application desktop toolbars.
157 Any DPI virtualization mode of the caller has no effect on this output. */
158
159 return BOOLToBool(::SystemParametersInfo(SPI_GETWORKAREA, 0, rect, 0));
160 }
161
162
IsDialogSizeOK(int xSize,int ySize,HWND hwnd)163 bool IsDialogSizeOK(int xSize, int ySize, HWND hwnd)
164 {
165 // it returns for system font. Real font uses another values
166 const LONG v = GetDialogBaseUnits();
167 const int x = LOWORD(v);
168 const int y = HIWORD(v);
169
170 RECT rect;
171 GetWorkAreaRect(&rect, hwnd);
172 const int wx = RECT_SIZE_X(rect);
173 const int wy = RECT_SIZE_Y(rect);
174 return
175 xSize / 4 * x <= wx &&
176 ySize / 8 * y <= wy;
177 }
178
GetMargins(int margin,int & x,int & y)179 bool CDialog::GetMargins(int margin, int &x, int &y)
180 {
181 x = margin;
182 y = margin;
183 RECT rect;
184 rect.left = 0;
185 rect.top = 0;
186 rect.right = margin;
187 rect.bottom = margin;
188 if (!MapRect(&rect))
189 return false;
190 x = rect.right - rect.left;
191 y = rect.bottom - rect.top;
192 return true;
193 }
194
Units_To_Pixels_X(int units)195 int CDialog::Units_To_Pixels_X(int units)
196 {
197 RECT rect;
198 rect.left = 0;
199 rect.top = 0;
200 rect.right = units;
201 rect.bottom = units;
202 if (!MapRect(&rect))
203 return units * 3 / 2;
204 return rect.right - rect.left;
205 }
206
GetItemSizes(unsigned id,int & x,int & y)207 bool CDialog::GetItemSizes(unsigned id, int &x, int &y)
208 {
209 RECT rect;
210 if (!::GetWindowRect(GetItem(id), &rect))
211 return false;
212 x = RECT_SIZE_X(rect);
213 y = RECT_SIZE_Y(rect);
214 return true;
215 }
216
GetClientRectOfItem(unsigned id,RECT & rect)217 void CDialog::GetClientRectOfItem(unsigned id, RECT &rect)
218 {
219 ::GetWindowRect(GetItem(id), &rect);
220 ScreenToClient(&rect);
221 }
222
MoveItem(unsigned id,int x,int y,int width,int height,bool repaint)223 bool CDialog::MoveItem(unsigned id, int x, int y, int width, int height, bool repaint)
224 {
225 return BOOLToBool(::MoveWindow(GetItem(id), x, y, width, height, BoolToBOOL(repaint)));
226 }
227
228
229 /*
230 typedef BOOL (WINAPI * Func_DwmGetWindowAttribute)(
231 HWND hwnd, DWORD dwAttribute, PVOID pvAttribute, DWORD cbAttribute);
232
233 static bool GetWindowsRect_DWM(HWND hwnd, RECT *rect)
234 {
235 // dll load and free is too slow : 300 calls in second.
236 NDLL::CLibrary dll;
237 if (!dll.Load(FTEXT("dwmapi.dll")))
238 return false;
239 Func_DwmGetWindowAttribute f = (Func_DwmGetWindowAttribute)dll.GetProc("DwmGetWindowAttribute" );
240 if (f)
241 {
242 #define MY__DWMWA_EXTENDED_FRAME_BOUNDS 9
243 // 30000 per second
244 RECT r;
245 if (f(hwnd, MY__DWMWA_EXTENDED_FRAME_BOUNDS, &r, sizeof(RECT)) == S_OK)
246 {
247 *rect = r;
248 return true;
249 }
250 }
251 return false;
252 }
253 */
254
255
IsRect_Small_Inside_Big(const RECT & sm,const RECT & big)256 static bool IsRect_Small_Inside_Big(const RECT &sm, const RECT &big)
257 {
258 return sm.left >= big.left
259 && sm.right <= big.right
260 && sm.top >= big.top
261 && sm.bottom <= big.bottom;
262 }
263
264
AreRectsOverlapped(const RECT & r1,const RECT & r2)265 static bool AreRectsOverlapped(const RECT &r1, const RECT &r2)
266 {
267 return r1.left < r2.right
268 && r1.right > r2.left
269 && r1.top < r2.bottom
270 && r1.bottom > r2.top;
271 }
272
273
AreRectsEqual(const RECT & r1,const RECT & r2)274 static bool AreRectsEqual(const RECT &r1, const RECT &r2)
275 {
276 return r1.left == r2.left
277 && r1.right == r2.right
278 && r1.top == r2.top
279 && r1.bottom == r2.bottom;
280 }
281
282
NormalizeSize(bool fullNormalize)283 void CDialog::NormalizeSize(bool fullNormalize)
284 {
285 RECT workRect;
286 if (!GetWorkAreaRect(&workRect, *this))
287 return;
288 RECT rect;
289 if (!GetWindowRect(&rect))
290 return;
291 int xs = RECT_SIZE_X(rect);
292 int ys = RECT_SIZE_Y(rect);
293
294 // we don't want to change size using workRect, if window is outside of WorkArea
295 if (!AreRectsOverlapped(rect, workRect))
296 return;
297
298 /* here rect and workRect are overlapped, but it can be false
299 overlapping of small shadow when window in another display. */
300
301 const int xsW = RECT_SIZE_X(workRect);
302 const int ysW = RECT_SIZE_Y(workRect);
303 if (xs <= xsW && ys <= ysW)
304 return; // size of window is OK
305 if (fullNormalize)
306 {
307 Show(SW_SHOWMAXIMIZED);
308 return;
309 }
310 int x = workRect.left;
311 int y = workRect.top;
312 if (xs < xsW) x += (xsW - xs) / 2; else xs = xsW;
313 if (ys < ysW) y += (ysW - ys) / 2; else ys = ysW;
314 Move(x, y, xs, ys, true);
315 }
316
317
NormalizePosition()318 void CDialog::NormalizePosition()
319 {
320 RECT workRect;
321 if (!GetWorkAreaRect(&workRect, *this))
322 return;
323
324 RECT rect2 = workRect;
325 bool useWorkArea = true;
326 const HWND parentHWND = GetParent();
327
328 if (parentHWND)
329 {
330 RECT workRectParent;
331 if (!GetWorkAreaRect(&workRectParent, parentHWND))
332 return;
333
334 // if windows are in different monitors, we use only workArea of current window
335
336 if (AreRectsEqual(workRectParent, workRect))
337 {
338 // RECT rect3; if (GetWindowsRect_DWM(parentHWND, &rect3)) {}
339 CWindow wnd(parentHWND);
340 if (wnd.GetWindowRect(&rect2))
341 {
342 // it's same monitor. So we try to use parentHWND rect.
343 /* we don't want to change position, if parent window is not inside work area.
344 In Win10 : parent window rect is 8 pixels larger for each corner than window size for shadow.
345 In maximize mode : window is outside of workRect.
346 if parent window is inside workRect, we will use parent window instead of workRect */
347 if (IsRect_Small_Inside_Big(rect2, workRect))
348 useWorkArea = false;
349 }
350 }
351 }
352
353 RECT rect;
354 if (!GetWindowRect(&rect))
355 return;
356
357 if (useWorkArea)
358 {
359 // we don't want to move window, if it's already inside.
360 if (IsRect_Small_Inside_Big(rect, workRect))
361 return;
362 // we don't want to move window, if it's outside of workArea
363 if (!AreRectsOverlapped(rect, workRect))
364 return;
365 rect2 = workRect;
366 }
367
368 {
369 const int xs = RECT_SIZE_X(rect);
370 const int ys = RECT_SIZE_Y(rect);
371 const int xs2 = RECT_SIZE_X(rect2);
372 const int ys2 = RECT_SIZE_Y(rect2);
373 // we don't want to change position if parent is smaller.
374 if (xs <= xs2 && ys <= ys2)
375 {
376 const int x = rect2.left + (xs2 - xs) / 2;
377 const int y = rect2.top + (ys2 - ys) / 2;
378
379 if (x != rect.left || y != rect.top)
380 Move(x, y, xs, ys, true);
381 // SetWindowPos(*this, HWND_TOP, x, y, 0, 0, SWP_NOSIZE);
382 return;
383 }
384 }
385 }
386
387
388
Create(LPCTSTR templateName,HWND parentWindow)389 bool CModelessDialog::Create(LPCTSTR templateName, HWND parentWindow)
390 {
391 const HWND aHWND = CreateDialogParam(g_hInstance, templateName, parentWindow, DialogProcedure, (LPARAM)this);
392 if (!aHWND)
393 return false;
394 Attach(aHWND);
395 return true;
396 }
397
Create(LPCTSTR templateName,HWND parentWindow)398 INT_PTR CModalDialog::Create(LPCTSTR templateName, HWND parentWindow)
399 {
400 return DialogBoxParam(g_hInstance, templateName, parentWindow, DialogProcedure, (LPARAM)this);
401 }
402
403 #ifndef _UNICODE
404
Create(LPCWSTR templateName,HWND parentWindow)405 bool CModelessDialog::Create(LPCWSTR templateName, HWND parentWindow)
406 {
407 HWND aHWND;
408 if (g_IsNT)
409 aHWND = CreateDialogParamW(g_hInstance, templateName, parentWindow, DialogProcedure, (LPARAM)this);
410 else
411 {
412 AString name;
413 LPCSTR templateNameA;
414 if (IS_INTRESOURCE(templateName))
415 templateNameA = (LPCSTR)templateName;
416 else
417 {
418 name = GetSystemString(templateName);
419 templateNameA = name;
420 }
421 aHWND = CreateDialogParamA(g_hInstance, templateNameA, parentWindow, DialogProcedure, (LPARAM)this);
422 }
423 if (aHWND == 0)
424 return false;
425 Attach(aHWND);
426 return true;
427 }
428
Create(LPCWSTR templateName,HWND parentWindow)429 INT_PTR CModalDialog::Create(LPCWSTR templateName, HWND parentWindow)
430 {
431 if (g_IsNT)
432 return DialogBoxParamW(g_hInstance, templateName, parentWindow, DialogProcedure, (LPARAM)this);
433 AString name;
434 LPCSTR templateNameA;
435 if (IS_INTRESOURCE(templateName))
436 templateNameA = (LPCSTR)templateName;
437 else
438 {
439 name = GetSystemString(templateName);
440 templateNameA = name;
441 }
442 return DialogBoxParamA(g_hInstance, templateNameA, parentWindow, DialogProcedure, (LPARAM)this);
443 }
444 #endif
445
446 }}
447