• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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