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