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