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