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