WindowMinMaxSubclass.cpp
Go to the documentation of this file.
1 /*
2 //
3 // BEGIN SONGBIRD GPL
4 //
5 // This file is part of the Songbird web player.
6 //
7 // Copyright(c) 2005-2008 POTI, Inc.
8 // http://songbirdnest.com
9 //
10 // This file may be licensed under the terms of of the
11 // GNU General Public License Version 2 (the "GPL").
12 //
13 // Software distributed under the License is distributed
14 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
15 // express or implied. See the GPL for the specific language
16 // governing rights and limitations.
17 //
18 // You should have received a copy of the GPL along with this
19 // program. If not, go to http://www.gnu.org/licenses/gpl.html
20 // or write to the Free Software Foundation, Inc.,
21 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 // END SONGBIRD GPL
24 //
25  */
26 
32 #include "WindowMinMaxSubclass.h"
33 #include "WindowMinMax.h"
34 
35 #ifdef XP_WIN
36 #include <dbt.h>
37 #include <cguid.h>
38 #include <objbase.h>
39 #include "MultiMonitor.h"
40 #endif
41 
42 #include <nscore.h>
43 #include <nsCOMPtr.h>
44 #include <nsXPCOM.h>
45 #include <nsComponentManagerUtils.h>
46 #include <nsServiceManagerUtils.h>
47 #include <nsStringGlue.h>
48 
49 #include "sbIDeviceManager.h"
50 #include <sbIDeviceBase.h>
51 
52 //#define SB_ENABLE_CD_DEVICE
53 #if defined(SB_ENABLE_CD_DEVICE)
54  #include "sbICDDevice.h"
55 #endif
56 
57 // CLASSES ====================================================================
58 //=============================================================================
59 // CWindowMinMaxSubclass Class
60 //=============================================================================
61 
62 #ifdef XP_WIN
63 
64 char FirstDriveFromMask (ULONG unitmask)
65 {
66  char i;
67 
68  for (i = 0; i < 26; ++i)
69  {
70  if (unitmask & 0x1)
71  break;
72  unitmask = unitmask >> 1;
73  }
74 
75  return (i + 'A');
76 }
77 
78 //-----------------------------------------------------------------------------
79 static LRESULT CALLBACK WindowMinMaxSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
80 {
81  if (!IsWindow(hWnd)) return 0;
82  CWindowMinMaxSubclass *_this = reinterpret_cast<CWindowMinMaxSubclass *>(GetWindowLong(hWnd, GWL_USERDATA));
83  return _this->WndProc(hWnd, uMsg, wParam, lParam);
84 } // WindowMinMaxSubclassProc
85 #endif
86 
87 //-----------------------------------------------------------------------------
89 {
90  m_dx = m_ix = m_dy = m_iy = 0;
91  m_window = window;
93  NS_ADDREF(cb);
94  m_callback = cb;
96 } // ctor
97 
98 //-----------------------------------------------------------------------------
100 {
101 #ifdef XP_WIN
103  NS_RELEASE(m_callback);
104 #endif
105 } // dtor
106 
107 #ifdef XP_WIN
108 //-----------------------------------------------------------------------------
109 LRESULT CWindowMinMaxSubclass::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
110 {
111  if (!IsWindow(hWnd))
112  return 0;
113  switch (uMsg)
114  {
115  case WM_SETCURSOR:
116  {
117  // a WM_SETCURSOR message with WM_LBUTTONDOWN is issued each time the mouse is clicked on a resizer.
118  // it's also done plenty of other times, but it doesn't matter, this is merely used to reset the compensation
119  // variables between two resizing sessions (see comments below)
120  if (HIWORD(lParam) == WM_LBUTTONDOWN)
121  {
122  m_dx = m_ix = m_dy = m_iy = 0;
123  }
124  break;
125  }
126 
127  // XXXlone> this is a temporary fix, until mozilla handles maximizing (and restoring to a maximized state) on secondary screens
128  case WM_GETMINMAXINFO:
129  {
130  MINMAXINFO* mi = (MINMAXINFO *) lParam;
131  RECT workArea;
132  RECT monitorArea;
133 
134  // Need both the area with and without taskbar, because ptMaxPosition uses local coordinates
135  CMultiMonitor::GetMonitorFromWindow(&workArea, hWnd, TRUE);
136  CMultiMonitor::GetMonitorFromWindow(&monitorArea, hWnd, FALSE);
137 
138  mi->ptMaxSize.x = workArea.right - workArea.left;
139  mi->ptMaxSize.y = workArea.bottom - workArea.top;
140  mi->ptMaxPosition.x = workArea.left - monitorArea.left;
141  mi->ptMaxPosition.y = workArea.top - monitorArea.top;
142  }
143  break;
144 
145  case WM_WINDOWPOSCHANGING:
146  {
147  WINDOWPOS *wp = (WINDOWPOS*)lParam;
148  if (m_callback && wp && !(wp->flags & SWP_NOSIZE))
149  {
150  // previous window position on screen
151  RECT r;
152  GetWindowRect(hWnd, &r);
153 
154  // determine which way the window is being dragged, because mozilla does not honor WM_SIZING/WM_EXITSIZEMOVE
155  int _r, _t, _l, _b;
156  _r = r.right != wp->x + wp->cx;
157  _b = r.bottom != wp->y + wp->cy;
158  _l = r.left != wp->x;
159  _t = r.top != wp->y;
160 
161  // if all sides are moving, this is not a user-initiated resize, let moz handle it
162  if (_r && _b && _l && _t) break;
163 
164  // ask the script callback for current min/max values
165  // since technically, any of the callbacks could have caused our window to be destroyed,
166  // check that the window is still valid after any callback returns
167  int _minwidth = -1, _minheight = -1, _maxwidth = -1, _maxheight = -1;
168  m_callback->GetMaxWidth(&_maxwidth);
169  if (!IsWindow(hWnd)) return 0;
170  m_callback->GetMaxHeight(&_maxheight);
171  if (!IsWindow(hWnd)) return 0;
172  m_callback->GetMinWidth(&_minwidth);
173  if (!IsWindow(hWnd)) return 0;
174  m_callback->GetMinHeight(&_minheight);
175  if (!IsWindow(hWnd)) return 0;
176 
177  // fill default resize values from mozilla's resizer object
178  LRESULT ret = CallWindowProc(m_prevWndProc, hWnd, uMsg, wParam, lParam);
179 
180  // original size to which mozilla would like to resize the window
181  int ocx = wp->cx;
182  int ocy = wp->cy;
183 
184  // because the moz resize object handles resizing using relative mouse tracking, we need to compensate for the motion of the mouse outside of the allowed values.
185  // for instance, when going past the minimum size allowed, the mouse will naturally travel further than the side of the window (the mouse will no longer be in front of
186  // the resizer anymore, but will move over the content of the window). this is normal, it's what we want, but because of the use of relative tracking, moving the mouse back will
187  // immediately start to resize the window in the other direction instead of waiting until the mouse has come back on top of the resizer. that's what you get for using relative
188  // tracking... well, that and bugs, too.
189  //
190  // anyway, to fix this (definitely unwanted) behavior, we keep track of four values, which respectively describe how much the mouse has traveled past the horizontal minimum,
191  // horizontal maximum, vertical minimum and vertical maximum on the left and bottom sides (fotunately, the resizer object does not provoke the same problem with top/left tracking
192  // or we would need 8 of these).
193  //
194  // using the size of the window mozilla would like to set, and the previous size of the window, we determine which way the mouse has been tracked, and use this to update
195  // the four compensation variables. we use their values when the mouse moves the other direction and decrease or increase the size of the window so that it looks like
196  // it is never resized at all when the mouse is past the min/max values (ie, it looks like an absolute mouse tracking).
197  //
198  // this of course has to work in conjunction with the normal tailoring of the window position and size that we're here to implement in the first place.
199  //
200  // fun.
201 
202  if (_r && _minwidth != -1)
203  {
204  // compensate -- increment left motion past minimum width
205  int v = _minwidth-ocx;
206  if (v > 0) m_dx += v;
207  }
208 
209  if (_r && _maxwidth != -1)
210  {
211  // compensate -- increment right motion past maximum width
212  int v = ocx-_maxwidth;
213  if (v > 0) m_ix += v;
214  }
215 
216  if (_b && _minheight != -1)
217  {
218  // compensate -- increment top motion past minimum height
219  int v = _minheight-ocy;
220  if (v > 0) m_dy += v;
221  }
222 
223  if (_b && _maxheight != -1)
224  {
225  // compensate -- increment bottom motion past maximum height
226  int v = ocy-_maxheight;
227  if (v > 0) m_iy += v;
228  }
229 
230  if (_r) // the user is dragging the window by the right side (or one of the corners on the right), we only need to modify the width of the window to constrain it
231  {
232  if (_minwidth != -1 && wp->cx < _minwidth) wp->cx = _minwidth;
233  else
234  {
235  // compensate -- decrement left motion past minimum width, decrease resulting width so it looks like nothing happened
236  int v = ocx-(r.right-r.left);
237  if (v > 0)
238  {
239  if (m_dx >= v) { wp->cx -= v; m_dx -= v; } else { wp->cx -= m_dx; m_dx = 0; }
240  }
241  }
242  if (_maxwidth != -1 && wp->cx > _maxwidth) wp->cx = _maxwidth;
243  else
244  {
245  // compensate -- decrement right motion past maximum width, increase resulting width so it looks like nothing happened
246  int v = (r.right-r.left)-ocx;
247  if (v > 0)
248  {
249  if (m_ix >= v) { wp->cx += v; m_ix -= v; } else { wp->cx += m_ix; m_ix = 0; }
250  }
251  }
252  }
253  if (_l) // the user is dragging the window by the left side (or one of the corners on the left), we need to modify both the width and the right anchor of the window to constrain it
254  {
255  if (_maxwidth != -1 && wp->cx > _maxwidth)
256  {
257  wp->cx = _maxwidth;
258  wp->x = r.right - _maxwidth;
259  }
260  if (_minwidth != -1 && wp->cx < _minwidth)
261  {
262  wp->cx = _minwidth;
263  wp->x = r.right - _minwidth;
264  }
265  }
266  if (_b) // the user is dragging the window by the bottom side (or one of the corners on the bottom), we only need to modify the height of the window to constrain it
267  {
268  if (_minheight != -1 && wp->cy < _minheight) wp->cy = _minheight;
269  else
270  {
271  // compensate -- decrement upward motion past minimum height, decrease resulting height so it looks like nothing happened
272  int v = ocy-(r.bottom-r.top);
273  if (v > 0)
274  {
275  if (m_dy >= v) { wp->cy -= v; m_dy -= v; } else { wp->cy -= m_dy; m_dy = 0; }
276  }
277  }
278  if (_maxheight != -1 && wp->cy > _maxheight) wp->cy = _maxheight;
279  else
280  {
281  // compensate -- decrement downward motion past maximum height, increase resulting height so it looks like nothing happened
282  int v = (r.bottom-r.top)-ocy;
283  if (v > 0)
284  {
285  if (m_iy >= v) { wp->cy += v; m_iy -= v; } else { wp->cy += m_iy; m_iy = 0; }
286  }
287  }
288  }
289  if (_t) // the user is dragging the window by the top side (or one of the corners on the top), we need to modify both the height and the top anchor of the window to constrain it
290  {
291  if (_maxheight != -1 && wp->cy > _maxheight)
292  {
293  wp->cy = _maxheight;
294  wp->y = r.bottom - _maxheight;
295  }
296  if (_minheight != -1 && wp->cy < _minheight)
297  {
298  wp->cy = _minheight;
299  wp->y = r.bottom - _minheight;
300  }
301  }
302 
303  // all done, return value we got from moz
304  return ret;
305  }
306  }
307  break;
308 
309  // Added WM_DEVICECHANGE handler for CDDevice object, as a Windows specific
310  // implementation, for receiving media insert and removal notifications.
311  case WM_DEVICECHANGE:
312  {
313  nsresult rv;
314  nsCOMPtr<sbIDeviceManager> deviceManager =
315  do_GetService("@songbirdnest.com/Songbird/DeviceManager;1", &rv);
316  if (NS_FAILED(rv))
317  {
318  NS_WARNING("Failed to get the DeviceManager!");
319  break;
320  }
321 
322  // Send an event to the CDDevice, if present.
323  PRBool hasCDDevice;
324  NS_NAMED_LITERAL_STRING(cdCategory, "Songbird CD Device");
325 
326  rv = deviceManager->HasDeviceForCategory(cdCategory, &hasCDDevice);
327  if (NS_SUCCEEDED(rv) && hasCDDevice)
328  {
329  nsCOMPtr<sbIDeviceBase> baseDevice;
330  rv = deviceManager->GetDeviceByCategory(cdCategory,
331  getter_AddRefs(baseDevice));
332  if (NS_SUCCEEDED(rv))
333  {
334 
335 #if defined(SB_ENABLE_CD_DEVICE)
336  nsCOMPtr<sbICDDevice> cdDevice = do_QueryInterface(baseDevice, &rv);
337  if (NS_SUCCEEDED(rv))
338  {
339  PRBool retVal;
340  PRBool mediaInserted = DBT_DEVICEARRIVAL == wParam;
341  rv = cdDevice->OnCDDriveEvent(mediaInserted, &retVal);
342  }
343 #endif
344 
345  }
346  }
347  break;
348  }
349  // Added WM_SYSCOMMAND handler for taskbar buttons' close command
350  case WM_SYSCOMMAND:
351  {
352  if (wParam == SC_CLOSE && m_callback)
353  {
355  return 0;
356  }
357  }
358  break;
359  }
360  if (!IsWindow(hWnd))
361  return 0;
362  return CallWindowProc(m_prevWndProc, hWnd, uMsg, wParam, lParam);
363 } // WndProc
364 #endif
365 
366 //-----------------------------------------------------------------------------
368 {
369  return m_window;
370 } // getWindow
371 
372 //-----------------------------------------------------------------------------
374 {
375  return m_callback;
376 } // getCallback
377 
378 //-----------------------------------------------------------------------------
380 {
381  return m_hwnd;
382 } // getNativeWindow
383 
384 //-----------------------------------------------------------------------------
386 {
387 #ifdef XP_WIN
388  if (m_prevWndProc != NULL) UnsubclassWindow();
389  m_prevWndProc = (WNDPROC)GetWindowLong(m_hwnd, GWL_WNDPROC);
390  SetWindowLong(m_hwnd, GWL_USERDATA, (LONG)this);
391  SetWindowLong(m_hwnd, GWL_WNDPROC, (LONG)WindowMinMaxSubclassProc);
392 
393  {
394  DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
395  char szMsg[80];
396 
397  ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
398  NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
399  NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
400  NotificationFilter.dbcc_classguid = GUID_NULL;
401 
402  HDEVNOTIFY hDevNotify = RegisterDeviceNotification( m_hwnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES );
403 
404  if(!hDevNotify)
405  {
406  ::wsprintfA(szMsg, "RegisterDeviceNotification failed: %d\n", ::GetLastError());
407  ::MessageBoxA(m_hwnd, szMsg, "Registration", MB_OK);
408  }
409  else
410  {
411  m_hDevNotify = hDevNotify;
412  }
413  }
414 
415 #endif
416 }
417 
418 //-----------------------------------------------------------------------------
420 {
421 #ifdef XP_WIN
422  if (m_prevWndProc == NULL) return;
423  SetWindowLong(m_hwnd, GWL_WNDPROC, (LONG)m_prevWndProc);
424  SetWindowLong(m_hwnd, GWL_USERDATA, 0);
425  m_prevWndProc = NULL;
426 
427  if(m_hDevNotify != NULL)
428  {
429  UnregisterDeviceNotification(m_hDevNotify);
430  m_hDevNotify = NULL;
431  }
432 #endif
433 }
434 
Service for setting min/max limit callbacks to a window position and size - Prototypes.
LONG left
Definition: MultiMonitor.h:48
PRInt32 GetMaxHeight()
Get maximum window height This method is called by the WindowMinMax hook to query the application for...
PRInt32 GetMaxWidth()
Get maximum window width This method is called by the WindowMinMax hook to query the application for ...
Songbird Multiple Monitor Support - Definition.
LONG bottom
Definition: MultiMonitor.h:51
let window
PRInt32 GetMinWidth()
Get minimum window width This method is called by the WindowMinMax hook to query the application for ...
long LONG
Definition: MultiMonitor.h:38
Window subclasser object for WindowMinMax service - Prototypes.
var _this
WindowMinMax callback interface This interface describes a callback for the WindowMinMax service...
sbIWindowMinMaxCallback * m_callback
LONG top
Definition: MultiMonitor.h:49
CWindowMinMaxSubclass(nsISupports *window, sbIWindowMinMaxCallback *cb)
static void * get(nsISupports *window)
sbIWindowMinMaxCallback * getCallback()
return ret
void OnWindowClose()
Window closing callback This method is called by the WindowMinMax hook to notify the callback object ...
LONG right
Definition: MultiMonitor.h:50
PRInt32 GetMinHeight()
Get minimum window height This method is called by the WindowMinMax hook to query the application for...
static void GetMonitorFromWindow(RECT *r, void *wnd, bool excludeTaskbar)
_getSelectedPageStyle s i