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