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