1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/toplvcmn.cpp
3 // Purpose: common (for all platforms) wxTopLevelWindow functions
4 // Author: Julian Smart, Vadim Zeitlin
5 // Created: 01/02/97
6 // Copyright: (c) 1998 Robert Roebling and Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // ============================================================================
11 // declarations
12 // ============================================================================
13
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20
21
22 #include "wx/toplevel.h"
23
24 #ifndef WX_PRECOMP
25 #include "wx/dcclient.h"
26 #include "wx/app.h"
27 #endif // WX_PRECOMP
28
29 #include "wx/display.h"
30
31 #include "wx/private/tlwgeom.h"
32
33 // ----------------------------------------------------------------------------
34 // event table
35 // ----------------------------------------------------------------------------
36
37 wxBEGIN_EVENT_TABLE(wxTopLevelWindowBase, wxWindow)
38 EVT_CLOSE(wxTopLevelWindowBase::OnCloseWindow)
39 EVT_SIZE(wxTopLevelWindowBase::OnSize)
40 wxEND_EVENT_TABLE()
41
42 // ============================================================================
43 // implementation
44 // ============================================================================
45
46 wxIMPLEMENT_ABSTRACT_CLASS(wxTopLevelWindow, wxWindow);
47
48 // ----------------------------------------------------------------------------
49 // construction/destruction
50 // ----------------------------------------------------------------------------
51
wxTopLevelWindowBase()52 wxTopLevelWindowBase::wxTopLevelWindowBase()
53 {
54 // Unlike windows, top level windows are created hidden by default.
55 m_isShown = false;
56 }
57
~wxTopLevelWindowBase()58 wxTopLevelWindowBase::~wxTopLevelWindowBase()
59 {
60 // don't let wxTheApp keep any stale pointers to us
61 if ( wxTheApp && wxTheApp->GetTopWindow() == this )
62 wxTheApp->SetTopWindow(NULL);
63
64 wxTopLevelWindows.DeleteObject(this);
65
66 // delete any our top level children which are still pending for deletion
67 // immediately: this could happen if a child (e.g. a temporary dialog
68 // created with this window as parent) was Destroy()'d) while this window
69 // was deleted directly (with delete, or maybe just because it was created
70 // on the stack) immediately afterwards and before the child TLW was really
71 // destroyed -- not destroying it now would leave it alive with a dangling
72 // parent pointer and result in a crash later
73 for ( wxObjectList::iterator i = wxPendingDelete.begin();
74 i != wxPendingDelete.end();
75 )
76 {
77 wxWindow * const win = wxDynamicCast(*i, wxWindow);
78 if ( win && wxGetTopLevelParent(win->GetParent()) == this )
79 {
80 wxPendingDelete.erase(i);
81
82 delete win;
83
84 // deleting it invalidated the list (and not only one node because
85 // it could have resulted in deletion of other objects to)
86 i = wxPendingDelete.begin();
87 }
88 else
89 {
90 ++i;
91 }
92 }
93
94 if ( IsLastBeforeExit() )
95 {
96 // no other (important) windows left, quit the app
97 wxTheApp->ExitMainLoop();
98 }
99 }
100
Destroy()101 bool wxTopLevelWindowBase::Destroy()
102 {
103 // We can't delay the destruction if our parent is being already destroyed
104 // as we will be deleted anyhow during its destruction and the pointer
105 // stored in wxPendingDelete would become invalid, so just delete ourselves
106 // immediately in this case.
107 wxWindow* parent = GetParent();
108 if ( (parent && parent->IsBeingDeleted()) || !GetHandle() )
109 {
110 return wxNonOwnedWindow::Destroy();
111 }
112
113 // delayed destruction: the frame will be deleted during the next idle
114 // loop iteration
115 if ( !wxPendingDelete.Member(this) )
116 wxPendingDelete.Append(this);
117
118 // normally we want to hide the window immediately so that it doesn't get
119 // stuck on the screen while it's being destroyed, however we shouldn't
120 // hide the last visible window as then we might not get any idle events
121 // any more as no events will be sent to the hidden window and without idle
122 // events we won't prune wxPendingDelete list and the application won't
123 // terminate
124 for ( wxWindowList::const_iterator i = wxTopLevelWindows.begin(),
125 end = wxTopLevelWindows.end();
126 i != end;
127 ++i )
128 {
129 wxTopLevelWindow * const win = static_cast<wxTopLevelWindow *>(*i);
130 if ( win != this && win->IsShown() )
131 {
132 // there remains at least one other visible TLW, we can hide this
133 // one
134 Hide();
135
136 break;
137 }
138 }
139
140 return true;
141 }
142
IsLastBeforeExit() const143 bool wxTopLevelWindowBase::IsLastBeforeExit() const
144 {
145 // first of all, automatically exiting the app on last window close can be
146 // completely disabled at wxTheApp level
147 if ( !wxTheApp || !wxTheApp->GetExitOnFrameDelete() )
148 return false;
149
150 // second, never terminate the application after closing a child TLW
151 // because this would close its parent unexpectedly -- notice that this
152 // check is not redundant with the loop below, as the parent might return
153 // false from its ShouldPreventAppExit() -- except if the child is being
154 // deleted as part of the parent destruction
155 if ( GetParent() && !GetParent()->IsBeingDeleted() )
156 return false;
157
158 wxWindowList::const_iterator i;
159 const wxWindowList::const_iterator end = wxTopLevelWindows.end();
160
161 // then decide whether we should exit at all
162 for ( i = wxTopLevelWindows.begin(); i != end; ++i )
163 {
164 wxTopLevelWindow * const win = static_cast<wxTopLevelWindow *>(*i);
165 if ( win->ShouldPreventAppExit() )
166 {
167 // there remains at least one important TLW, don't exit
168 return false;
169 }
170 }
171
172 // if yes, close all the other windows: this could still fail
173 for ( i = wxTopLevelWindows.begin(); i != end; ++i )
174 {
175 // don't close twice the windows which are already marked for deletion
176 wxTopLevelWindow * const win = static_cast<wxTopLevelWindow *>(*i);
177 if ( !wxPendingDelete.Member(win) && !win->Close() )
178 {
179 // one of the windows refused to close, don't exit
180 //
181 // NB: of course, by now some other windows could have been already
182 // closed but there is really nothing we can do about it as we
183 // have no way just to ask the window if it can close without
184 // forcing it to do it
185 return false;
186 }
187 }
188
189 return true;
190 }
191
192 // ----------------------------------------------------------------------------
193 // wxTopLevelWindow geometry
194 // ----------------------------------------------------------------------------
195
SetMinSize(const wxSize & minSize)196 void wxTopLevelWindowBase::SetMinSize(const wxSize& minSize)
197 {
198 SetSizeHints(minSize, GetMaxSize());
199 }
200
SetMaxSize(const wxSize & maxSize)201 void wxTopLevelWindowBase::SetMaxSize(const wxSize& maxSize)
202 {
203 SetSizeHints(GetMinSize(), maxSize);
204 }
205
GetRectForTopLevelChildren(int * x,int * y,int * w,int * h)206 void wxTopLevelWindowBase::GetRectForTopLevelChildren(int *x, int *y, int *w, int *h)
207 {
208 GetPosition(x,y);
209 GetSize(w,h);
210 }
211
212 /* static */
GetDefaultSize()213 wxSize wxTopLevelWindowBase::GetDefaultSize()
214 {
215 wxSize size = wxGetClientDisplayRect().GetSize();
216 #ifndef __WXOSX_IPHONE__
217 // create proportionally bigger windows on small screens
218 if ( size.x >= 1024 )
219 size.x = 400;
220 else if ( size.x >= 800 )
221 size.x = 300;
222 else if ( size.x >= 320 )
223 size.x = 240;
224
225 if ( size.y >= 768 )
226 size.y = 250;
227 else if ( size.y > 200 )
228 {
229 size.y *= 2;
230 size.y /= 3;
231 }
232 #endif
233 return size;
234 }
235
DoCentre(int dir)236 void wxTopLevelWindowBase::DoCentre(int dir)
237 {
238 // on some platforms centering top level windows is impossible
239 // because they are always maximized by guidelines or limitations
240 //
241 // and centering a maximized window doesn't make sense as its position
242 // can't change
243 if ( IsAlwaysMaximized() || IsMaximized() )
244 return;
245
246 // we need the display rect anyhow so store it first: notice that we should
247 // be centered on the same display as our parent window, the display of
248 // this window itself is not really defined yet
249 wxDisplay dpy(GetParent() ? GetParent() : this);
250 const wxRect rectDisplay(dpy.GetClientArea());
251
252 // what should we centre this window on?
253 wxRect rectParent;
254 if ( !(dir & wxCENTRE_ON_SCREEN) && GetParent() )
255 {
256 // centre on parent window: notice that we need screen coordinates for
257 // positioning this TLW
258 rectParent = GetParent()->GetScreenRect();
259
260 // if the parent is entirely off screen (happens at least with MDI
261 // parent frame under Mac but could happen elsewhere too if the frame
262 // was hidden/moved away for some reason), don't use it as otherwise
263 // this window wouldn't be visible at all
264 if ( !rectParent.Intersects(rectDisplay) )
265 {
266 // just centre on screen then
267 rectParent = rectDisplay;
268 }
269 }
270 else
271 {
272 // we were explicitly asked to centre this window on the entire screen
273 // or if we have no parent anyhow and so can't centre on it
274 rectParent = rectDisplay;
275 }
276
277 if ( !(dir & wxBOTH) )
278 dir |= wxBOTH; // if neither is specified, center in both directions
279
280 // the new window rect candidate
281 wxRect rect = GetRect().CentreIn(rectParent, dir & ~wxCENTRE_ON_SCREEN);
282
283 // we don't want to place the window off screen if Centre() is called as
284 // this is (almost?) never wanted and it would be very difficult to prevent
285 // it from happening from the user code if we didn't check for it here
286 if ( !rectDisplay.Contains(rect.GetTopLeft()) )
287 {
288 // move the window just enough to make the corner visible
289 int dx = rectDisplay.GetLeft() - rect.GetLeft();
290 int dy = rectDisplay.GetTop() - rect.GetTop();
291 rect.Offset(dx > 0 ? dx : 0, dy > 0 ? dy : 0);
292 }
293
294 if ( !rectDisplay.Contains(rect.GetBottomRight()) )
295 {
296 // do the same for this corner too
297 int dx = rectDisplay.GetRight() - rect.GetRight();
298 int dy = rectDisplay.GetBottom() - rect.GetBottom();
299 rect.Offset(dx < 0 ? dx : 0, dy < 0 ? dy : 0);
300 }
301
302 // the window top left and bottom right corner are both visible now and
303 // although the window might still be not entirely on screen (with 2
304 // staggered displays for example) we wouldn't be able to improve the
305 // layout much in such case, so we stop here
306
307 // -1 could be valid coordinate here if there are several displays
308 SetSize(rect, wxSIZE_ALLOW_MINUS_ONE);
309 }
310
311 // ----------------------------------------------------------------------------
312 // Saving/restoring geometry
313 // ----------------------------------------------------------------------------
314
SaveGeometry(const GeometrySerializer & ser) const315 bool wxTopLevelWindowBase::SaveGeometry(const GeometrySerializer& ser) const
316 {
317 wxTLWGeometry geom;
318 if ( !geom.GetFrom(static_cast<const wxTopLevelWindow*>(this)) )
319 return false;
320
321 return geom.Save(ser);
322 }
323
RestoreToGeometry(GeometrySerializer & ser)324 bool wxTopLevelWindowBase::RestoreToGeometry(GeometrySerializer& ser)
325 {
326 wxTLWGeometry geom;
327 if ( !geom.Restore(ser) )
328 return false;
329
330 return geom.ApplyTo(static_cast<wxTopLevelWindow*>(this));
331 }
332
333 // ----------------------------------------------------------------------------
334 // wxTopLevelWindow size management: we exclude the areas taken by
335 // menu/status/toolbars from the client area, so the client area is what's
336 // really available for the frame contents
337 // ----------------------------------------------------------------------------
338
DoScreenToClient(int * x,int * y) const339 void wxTopLevelWindowBase::DoScreenToClient(int *x, int *y) const
340 {
341 wxWindow::DoScreenToClient(x, y);
342
343 // translate the wxWindow client coords to our client coords
344 wxPoint pt(GetClientAreaOrigin());
345 if ( x )
346 *x -= pt.x;
347 if ( y )
348 *y -= pt.y;
349 }
350
DoClientToScreen(int * x,int * y) const351 void wxTopLevelWindowBase::DoClientToScreen(int *x, int *y) const
352 {
353 // our client area origin (0, 0) may be really something like (0, 30) for
354 // wxWindow if we have a toolbar, account for it before translating
355 wxPoint pt(GetClientAreaOrigin());
356 if ( x )
357 *x += pt.x;
358 if ( y )
359 *y += pt.y;
360
361 wxWindow::DoClientToScreen(x, y);
362 }
363
IsAlwaysMaximized() const364 bool wxTopLevelWindowBase::IsAlwaysMaximized() const
365 {
366 return false;
367 }
368
369 // ----------------------------------------------------------------------------
370 // icons
371 // ----------------------------------------------------------------------------
372
GetIcon() const373 wxIcon wxTopLevelWindowBase::GetIcon() const
374 {
375 return m_icons.IsEmpty() ? wxIcon() : m_icons.GetIcon( -1 );
376 }
377
SetIcon(const wxIcon & icon)378 void wxTopLevelWindowBase::SetIcon(const wxIcon& icon)
379 {
380 // passing wxNullIcon to SetIcon() is possible (it means that we shouldn't
381 // have any icon), but adding an invalid icon to wxIconBundle is not
382 wxIconBundle icons;
383 if ( icon.IsOk() )
384 icons.AddIcon(icon);
385
386 SetIcons(icons);
387 }
388
389 // ----------------------------------------------------------------------------
390 // event handlers
391 // ----------------------------------------------------------------------------
392
IsTopNavigationDomain(NavigationKind kind) const393 bool wxTopLevelWindowBase::IsTopNavigationDomain(NavigationKind kind) const
394 {
395 // This switch only exists to generate a compiler warning and force us to
396 // revisit this code if any new kinds of navigation are added in the
397 // future, but for now we block of them by default (some derived classes
398 // relax this however).
399 switch ( kind )
400 {
401 case Navigation_Tab:
402 case Navigation_Accel:
403 break;
404 }
405
406 return true;
407 }
408
409 // default resizing behaviour - if only ONE subwindow, resize to fill the
410 // whole client area
Layout()411 bool wxTopLevelWindowBase::Layout()
412 {
413 // We are called during the window destruction several times, e.g. as
414 // wxFrame tries to adjust to its tool/status bars disappearing. But
415 // actually doing the layout is pretty useless in this case as the window
416 // will disappear anyhow -- so just don't bother.
417 if ( IsBeingDeleted() )
418 return false;
419
420
421 // if we're using sizers or constraints - do use them
422 if ( GetAutoLayout()
423 || GetSizer()
424 #if wxUSE_CONSTRAINTS
425 || GetConstraints()
426 #endif
427 )
428 {
429 return wxNonOwnedWindow::Layout();
430 }
431 else
432 {
433 // do we have _exactly_ one child?
434 wxWindow *child = NULL;
435 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
436 node;
437 node = node->GetNext() )
438 {
439 wxWindow *win = node->GetData();
440
441 // exclude top level and managed windows (status bar isn't
442 // currently in the children list except under wxMac anyhow, but
443 // it makes no harm to test for it)
444 if ( !win->IsTopLevel() && !IsOneOfBars(win) )
445 {
446 if ( child )
447 {
448 return false; // it's our second subwindow - nothing to do
449 }
450
451 child = win;
452 }
453 }
454
455 // do we have any children at all?
456 if ( child && child->IsShown() )
457 {
458 // exactly one child - set it's size to fill the whole frame
459 int clientW, clientH;
460 DoGetClientSize(&clientW, &clientH);
461
462 child->SetSize(0, 0, clientW, clientH);
463
464 return true;
465 }
466 }
467
468 return false;
469 }
470
471 // The default implementation for the close window event.
OnCloseWindow(wxCloseEvent & WXUNUSED (event))472 void wxTopLevelWindowBase::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
473 {
474 Destroy();
475 }
476
SendIconizeEvent(bool iconized)477 bool wxTopLevelWindowBase::SendIconizeEvent(bool iconized)
478 {
479 wxIconizeEvent event(GetId(), iconized);
480 event.SetEventObject(this);
481
482 return GetEventHandler()->ProcessEvent(event);
483 }
484
485 // do the window-specific processing after processing the update event
DoUpdateWindowUI(wxUpdateUIEvent & event)486 void wxTopLevelWindowBase::DoUpdateWindowUI(wxUpdateUIEvent& event)
487 {
488 // call inherited, but skip the wxControl's version, and call directly the
489 // wxWindow's one instead, because the only reason why we are overriding this
490 // function is that we want to use SetTitle() instead of wxControl::SetLabel()
491 wxWindowBase::DoUpdateWindowUI(event);
492
493 // update title
494 if ( event.GetSetText() )
495 {
496 if ( event.GetText() != GetTitle() )
497 SetTitle(event.GetText());
498 }
499 }
500
RequestUserAttention(int WXUNUSED (flags))501 void wxTopLevelWindowBase::RequestUserAttention(int WXUNUSED(flags))
502 {
503 // it's probably better than do nothing, isn't it?
504 Raise();
505 }
506