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-f19f813537c7aea1c20749c914e756b54a9c3cf5.tar.gz 7zip-f19f813537c7aea1c20749c914e756b54a9c3cf5.tar.bz2 7zip-f19f813537c7aea1c20749c914e756b54a9c3cf5.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 | }} | ||
