foobar2000 SDK  2015-08-03
WindowPositionUtils.h
Go to the documentation of this file.
1 static BOOL AdjustWindowRectHelper(CWindow wnd, CRect & rc) {
2  const DWORD style = wnd.GetWindowLong(GWL_STYLE), exstyle = wnd.GetWindowLong(GWL_EXSTYLE);
3  return AdjustWindowRectEx(&rc,style,(style & WS_POPUP) ? wnd.GetMenu() != NULL : FALSE, exstyle);
4 }
5 
6 static void AdjustRectToScreenArea(CRect & rc, CRect rcParent) {
7  HMONITOR monitor = MonitorFromRect(rcParent,MONITOR_DEFAULTTONEAREST);
8  MONITORINFO mi = {sizeof(MONITORINFO)};
9  if (GetMonitorInfo(monitor,&mi)) {
10  const CRect clip = mi.rcWork;
11  if (rc.right > clip.right) rc.OffsetRect(clip.right - rc.right, 0);
12  if (rc.bottom > clip.bottom) rc.OffsetRect(0, clip.bottom - rc.bottom);
13  if (rc.left < clip.left) rc.OffsetRect(clip.left - rc.left, 0);
14  if (rc.top < clip.top) rc.OffsetRect(0, clip.top - rc.top);
15  }
16 }
17 
18 static BOOL GetClientRectAsSC(CWindow wnd, CRect & rc) {
19  CRect temp;
20  if (!wnd.GetClientRect(temp)) return FALSE;
21  if (temp.IsRectNull()) return FALSE;
22  if (!wnd.ClientToScreen(temp)) return FALSE;
23  rc = temp;
24  return TRUE;
25 }
26 
27 
28 static BOOL CenterWindowGetRect(CWindow wnd, CWindow wndParent, CRect & out) {
29  CRect parent, child;
30  if (!wndParent.GetWindowRect(&parent) || !wnd.GetWindowRect(&child)) return FALSE;
31  {
32  CPoint origin = parent.CenterPoint();
33  origin.Offset( - child.Width() / 2, - child.Height() / 2);
34  child.OffsetRect( origin - child.TopLeft() );
35  }
36  AdjustRectToScreenArea(child, parent);
37  out = child;
38  return TRUE;
39 }
40 
41 static BOOL CenterWindowAbove(CWindow wnd, CWindow wndParent) {
42  CRect rc;
43  if (!CenterWindowGetRect(wnd, wndParent, rc)) return FALSE;
44  return wnd.SetWindowPos(NULL,rc,SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
45 }
46 
47 static BOOL ShowWindowCentered(CWindow wnd,CWindow wndParent) {
48  CRect rc;
49  if (!CenterWindowGetRect(wnd, wndParent, rc)) return FALSE;
50  return wnd.SetWindowPos(HWND_TOP,rc,SWP_NOSIZE | SWP_SHOWWINDOW);
51 }
52 
53 class cfgWindowSize : public cfg_var {
54 public:
55  cfgWindowSize(const GUID & p_guid) : cfg_var(p_guid), m_width(~0), m_height(~0) {}
56  void get_data_raw(stream_writer * p_stream,abort_callback & p_abort) {
57  stream_writer_formatter<>(*p_stream,p_abort) << m_width << m_height;
58  }
59  void set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort) {
60  stream_reader_formatter<>(*p_stream,p_abort) >> m_width >> m_height;
61  }
62 
64 };
65 
67 public:
68  cfgWindowSizeTracker(cfgWindowSize & p_var) : m_var(p_var), m_applied(false) {}
69 
70  bool Apply(HWND p_wnd) {
71  bool retVal = false;
72  m_applied = false;
73  if (m_var.m_width != ~0 && m_var.m_height != ~0) {
74  CRect rect (0,0,m_var.m_width,m_var.m_height);
75  if (AdjustWindowRectHelper(p_wnd, rect)) {
76  SetWindowPos(p_wnd,NULL,0,0,rect.right-rect.left,rect.bottom-rect.top,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
77  retVal = true;
78  }
79  }
80  m_applied = true;
81  return retVal;
82  }
83 
84  BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT & lResult) {
85  if (uMsg == WM_SIZE && m_applied) {
86  if (lParam != 0) {
87  m_var.m_width = (short)LOWORD(lParam); m_var.m_height = (short)HIWORD(lParam);
88  }
89  }
90  return FALSE;
91  }
92 private:
94  bool m_applied;
95 };
96 
98 public:
100  BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT & lResult) {
101  if (cfgWindowSizeTracker::ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult)) return TRUE;
102  if (uMsg == WM_INITDIALOG) Apply(hWnd);
103  return FALSE;
104  }
105 };
106 
108 public:
109  cfgDialogPositionData() : m_width(sizeInvalid), m_height(sizeInvalid), m_posX(posInvalid), m_posY(posInvalid) {}
110 
111  void OverrideDefaultSize(t_uint32 width, t_uint32 height) {
112  if (m_width == sizeInvalid && m_height == sizeInvalid) {
113  m_width = width; m_height = height; m_posX = m_posY = posInvalid;
114  m_dpiX = m_dpiY = 96;
115  }
116  }
117 
118  void AddWindow(CWindow wnd) {
119  TryFetchConfig();
120  m_windows += wnd;
121  ApplyConfig(wnd);
122  }
123  void RemoveWindow(CWindow wnd) {
124  if (m_windows.contains(wnd)) {
125  StoreConfig(wnd); m_windows -= wnd;
126  }
127  }
128  void FetchConfig() {TryFetchConfig();}
129 
130 private:
131  BOOL ApplyConfig(CWindow wnd) {
132  ApplyDPI();
133  CWindow wndParent = wnd.GetParent();
134  UINT flags = SWP_NOACTIVATE | SWP_NOZORDER;
135  CRect rc;
136  if (!GetClientRectAsSC(wnd,rc)) return FALSE;
137  if (m_width != sizeInvalid && m_height != sizeInvalid && (wnd.GetWindowLong(GWL_STYLE) & WS_SIZEBOX) != 0) {
138  rc.right = rc.left + m_width;
139  rc.bottom = rc.top + m_height;
140  } else {
141  flags |= SWP_NOSIZE;
142  }
143  if (wndParent != NULL) {
144  CRect rcParent;
145  if (GetParentWndRect(wndParent, rcParent)) {
146  if (m_posX != posInvalid && m_posY != posInvalid) {
147  rc.MoveToXY( rcParent.TopLeft() + CPoint(m_posX, m_posY) );
148  } else {
149  CPoint center = rcParent.CenterPoint();
150  rc.MoveToXY( center.x - rc.Width() / 2, center.y - rc.Height() / 2);
151  }
152  }
153  }
154  if (!AdjustWindowRectHelper(wnd, rc)) return FALSE;
155 
156  DeOverlap(wnd, rc);
157 
158  {
159  CRect rcAdjust(0,0,1,1);
160  if (wndParent != NULL) {
161  CRect temp;
162  if (wndParent.GetWindowRect(temp)) rcAdjust = temp;
163  }
164  AdjustRectToScreenArea(rc, rcAdjust);
165  }
166 
167 
168  return wnd.SetWindowPos(NULL, rc, flags);
169  }
170  struct DeOverlapState {
171  CWindow m_thisWnd;
172  CPoint m_topLeft;
173  bool m_match;
174  };
175  static BOOL CALLBACK MyEnumChildProc(HWND wnd, LPARAM param) {
176  DeOverlapState * state = reinterpret_cast<DeOverlapState*>(param);
177  if (wnd != state->m_thisWnd && IsWindowVisible(wnd) ) {
178  CRect rc;
179  if (GetWindowRect(wnd, rc)) {
180  if (rc.TopLeft() == state->m_topLeft) {
181  state->m_match = true; return FALSE;
182  }
183  }
184  }
185  return TRUE;
186  }
187  static bool DeOverlapTest(CWindow wnd, CPoint topLeft) {
188  DeOverlapState state = {};
189  state.m_thisWnd = wnd; state.m_topLeft = topLeft; state.m_match = false;
190  EnumThreadWindows(GetCurrentThreadId(), MyEnumChildProc, reinterpret_cast<LPARAM>(&state));
191  return state.m_match;
192  }
193  static int DeOverlapDelta() {
194  return pfc::max_t<int>(GetSystemMetrics(SM_CYCAPTION),1);
195  }
196  static void DeOverlap(CWindow wnd, CRect & rc) {
197  const int delta = DeOverlapDelta();
198  for(;;) {
199  if (!DeOverlapTest(wnd, rc.TopLeft())) break;
200  rc.OffsetRect(delta,delta);
201  }
202  }
203  BOOL StoreConfig(CWindow wnd) {
204  CRect rc;
205  if (!GetClientRectAsSC(wnd, rc)) return FALSE;
206  const CSize DPI = QueryScreenDPIEx();
207  m_dpiX = DPI.cx; m_dpiY = DPI.cy;
208  m_width = rc.Width(); m_height = rc.Height();
209  m_posX = m_posY = posInvalid;
210  CWindow parent = wnd.GetParent();
211  if (parent != NULL) {
212  CRect rcParent;
213  if (GetParentWndRect(parent, rcParent)) {
214  m_posX = rc.left - rcParent.left;
215  m_posY = rc.top - rcParent.top;
216  }
217  }
218  return TRUE;
219  }
220  void TryFetchConfig() {
221  for(pfc::const_iterator<CWindow> walk = m_windows.first(); walk.is_valid(); ++walk) {
222  if (StoreConfig(*walk)) break;
223  }
224  }
225 
226  void ApplyDPI() {
227  const CSize screenDPI = QueryScreenDPIEx();
228  if (screenDPI.cx == 0 || screenDPI.cy == 0) {
229  PFC_ASSERT(!"Should not get here - something seriously wrong with the OS");
230  return;
231  }
232  if (m_dpiX != dpiInvalid && m_dpiX != screenDPI.cx) {
233  if (m_width != sizeInvalid) m_width = MulDiv(m_width, screenDPI.cx, m_dpiX);
234  if (m_posX != posInvalid) m_posX = MulDiv(m_posX, screenDPI.cx, m_dpiX);
235  }
236  if (m_dpiY != dpiInvalid && m_dpiY != screenDPI.cy) {
237  if (m_height != sizeInvalid) m_height = MulDiv(m_height, screenDPI.cy, m_dpiY);
238  if (m_posY != posInvalid) m_posY = MulDiv(m_posY, screenDPI.cy, m_dpiY);
239  }
240  m_dpiX = screenDPI.cx;
241  m_dpiY = screenDPI.cy;
242  }
243  CSize GrabDPI() const {
244  CSize DPI(96,96);
245  if (m_dpiX != dpiInvalid) DPI.cx = m_dpiX;
246  if (m_dpiY != dpiInvalid) DPI.cy = m_dpiY;
247  return DPI;
248  }
249 
250  static BOOL GetParentWndRect(CWindow wndParent, CRect & rc) {
251  if (!wndParent.IsIconic()) {
252  return wndParent.GetWindowRect(rc);
253  }
254  WINDOWPLACEMENT pl = {sizeof(pl)};
255  if (!wndParent.GetWindowPlacement(&pl)) return FALSE;
256  rc = pl.rcNormalPosition;
257  return TRUE;
258  }
259 
261 public:
263  t_int32 m_posX, m_posY;
264  t_uint32 m_dpiX, m_dpiY;
265  enum {
266  posInvalid = 0x80000000,
267  sizeInvalid = 0xFFFFFFFF,
268  dpiInvalid = 0,
269  };
270 };
271 
273  stream >> value.m_width >> value.m_height;
274  try {
275  stream >> value.m_posX >> value.m_posY >> value.m_dpiX >> value.m_dpiY;
276  } catch(exception_io_data) {
277  value.m_posX = value.m_posY = cfgDialogPositionData::posInvalid;
278  value.m_dpiX = value.m_dpiY = cfgDialogPositionData::dpiInvalid;
279  }
280  return stream;
281 }
283  return stream << value.m_width << value.m_height << value.m_posX << value.m_posY << value.m_dpiX << value.m_dpiY;
284 }
285 
287 public:
288  cfgDialogPosition(const GUID & id) : cfg_var(id) {}
289  void get_data_raw(stream_writer * p_stream,abort_callback & p_abort) {FetchConfig(); stream_writer_formatter<>(*p_stream, p_abort) << *pfc::implicit_cast<cfgDialogPositionData*>(this);}
290  void set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort) {stream_reader_formatter<>(*p_stream, p_abort) >> *pfc::implicit_cast<cfgDialogPositionData*>(this);}
291 };
292 
294 public:
295  cfgDialogPositionTracker(cfgDialogPosition & p_var) : m_var(p_var) {}
297 
298  BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT & lResult) {
299  if (uMsg == WM_CREATE || uMsg == WM_INITDIALOG) {
300  Cleanup();
301  m_wnd = hWnd;
302  m_var.AddWindow(m_wnd);
303  } else if (uMsg == WM_DESTROY) {
304  PFC_ASSERT( hWnd == m_wnd );
305  Cleanup();
306  }
307  return FALSE;
308  }
309 
310 private:
311  void Cleanup() {
312  if (m_wnd != NULL) {
313  m_var.RemoveWindow(m_wnd);
314  m_wnd = NULL;
315  }
316  }
318  CWindow m_wnd;
319 };
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult)
static void AdjustRectToScreenArea(CRect &rc, CRect rcParent)
pfc::avltree_t< CWindow > m_windows
static BOOL GetParentWndRect(CWindow wndParent, CRect &rc)
static BOOL GetClientRectAsSC(CWindow wnd, CRect &rc)
void AddWindow(CWindow wnd)
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult)
Definition: pfc.h:71
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult)
typedef BOOL(WINAPI *pPowerSetRequest_t)(__in HANDLE PowerRequest
static BOOL AdjustWindowRectHelper(CWindow wnd, CRect &rc)
FB2K_STREAM_WRITER_OVERLOAD(cfgDialogPositionData)
void RemoveWindow(CWindow wnd)
bool Apply(HWND p_wnd)
bool is_valid() const
Definition: iterators.h:24
static void DeOverlap(CWindow wnd, CRect &rc)
BOOL StoreConfig(CWindow wnd)
int32_t t_int32
Definition: int_types.h:4
void get_data_raw(stream_writer *p_stream, abort_callback &p_abort)
Retrieves state of the variable. Called only from main thread, when writing configuration file...
static BOOL ShowWindowCentered(CWindow wnd, CWindow wndParent)
void set_data_raw(stream_reader *p_stream, t_size p_sizehint, abort_callback &p_abort)
Sets state of the variable. Called only from main thread, when reading configuration file...
cfgDialogPosition(const GUID &id)
void get_data_raw(stream_writer *p_stream, abort_callback &p_abort)
Retrieves state of the variable. Called only from main thread, when writing configuration file...
void set_data_raw(stream_reader *p_stream, t_size p_sizehint, abort_callback &p_abort)
Sets state of the variable. Called only from main thread, when reading configuration file...
size_t t_size
Definition: int_types.h:48
SIZE QueryScreenDPIEx()
Definition: win32_misc.cpp:35
static BOOL CALLBACK MyEnumChildProc(HWND wnd, LPARAM param)
static bool DeOverlapTest(CWindow wnd, CPoint topLeft)
void OverrideDefaultSize(t_uint32 width, t_uint32 height)
cfgWindowSize(const GUID &p_guid)
static BOOL CenterWindowAbove(CWindow wnd, CWindow wndParent)
static BOOL CenterWindowGetRect(CWindow wnd, CWindow wndParent, CRect &out)
Base class for configuration variable classes; provides self-registration mechaisms and methods to se...
Definition: cfg_var.h:54
cfgDialogPositionTracker(cfgDialogPosition &p_var)
FB2K_STREAM_READER_OVERLOAD(cfgDialogPositionData)
uint32_t t_uint32
Definition: int_types.h:5
cfgWindowSizeTracker(cfgWindowSize &p_var)
BOOL ApplyConfig(CWindow wnd)
cfgDialogSizeTracker(cfgWindowSize &p_var)