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