1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        msw/toplevel.cpp 
   3 // Purpose:     implements wxTopLevelWindow for MSW 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 2001 SciTech Software, Inc. (www.scitechsoft.com) 
   9 // License:     wxWindows license 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  21     #pragma implementation "toplevel.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  33     #include "wx/toplevel.h" 
  34     #include "wx/string.h" 
  38     #include "wx/containr.h"        // wxSetFocusToChild() 
  41 #include "wx/msw/private.h" 
  43 #include "wx/popupwin.h" 
  53 // ---------------------------------------------------------------------------- 
  54 // stubs for missing functions under MicroWindows 
  55 // ---------------------------------------------------------------------------- 
  59 // static inline bool IsIconic(HWND WXUNUSED(hwnd)) { return FALSE; } 
  60 static inline bool IsZoomed(HWND 
WXUNUSED(hwnd
)) { return FALSE
; } 
  62 #endif // __WXMICROWIN__ 
  64 // NB: wxDlgProc must be defined here and not in dialog.cpp because the latter 
  65 //     is not included by wxUniv build which does need wxDlgProc 
  67 wxDlgProc(HWND hDlg
, UINT message
, WPARAM wParam
, LPARAM lParam
); 
  69 // ---------------------------------------------------------------------------- 
  71 // ---------------------------------------------------------------------------- 
  73 // list of all frames and modeless dialogs 
  74 wxWindowList wxModelessWindows
; 
  76 // the name of the default wxWindows class 
  77 extern const wxChar 
*wxCanvasClassName
; 
  79 // the hidden parent for wxFRAME_NO_TASKBAR unowned frames 
  80 wxWindow 
*wxTopLevelWindowMSW::ms_hiddenParent 
= NULL
; 
  82 // ============================================================================ 
  83 // wxTopLevelWindowMSW implementation 
  84 // ============================================================================ 
  86 BEGIN_EVENT_TABLE(wxTopLevelWindowMSW
, wxTopLevelWindowBase
) 
  87     EVT_ACTIVATE(wxTopLevelWindowMSW::OnActivate
) 
  90 // ---------------------------------------------------------------------------- 
  91 // wxTopLevelWindowMSW creation 
  92 // ---------------------------------------------------------------------------- 
  94 void wxTopLevelWindowMSW::Init() 
  97     m_maximizeOnShow 
= FALSE
; 
  99     // unlike (almost?) all other windows, frames are created hidden 
 102     // Data to save/restore when calling ShowFullScreen 
 104     m_fsOldWindowStyle 
= 0; 
 105     m_fsIsMaximized 
= FALSE
; 
 106     m_fsIsShowing 
= FALSE
; 
 108     m_winLastFocused 
= (wxWindow 
*)NULL
; 
 111 WXDWORD 
wxTopLevelWindowMSW::MSWGetStyle(long style
, WXDWORD 
*exflags
) const 
 113     // let the base class deal with the common styles but fix the ones which 
 114     // don't make sense for us (we also deal with the borders ourselves) 
 115     WXDWORD msflags 
= wxWindow::MSWGetStyle
 
 117                         (style 
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exflags
 
 120     // first select the kind of window being created 
 122     // note that if we don't set WS_POPUP, Windows assumes WS_OVERLAPPED and 
 123     // creates a window with both caption and border, hence we also test it 
 124     // below in some other cases 
 125     if ( style 
& wxFRAME_TOOL_WINDOW 
) 
 128         msflags 
|= WS_OVERLAPPED
; 
 130     // border and caption styles 
 131     if ( style 
& wxRESIZE_BORDER 
) 
 132         msflags 
|= WS_THICKFRAME
; 
 133     else if ( !(style 
& wxBORDER_NONE
) ) 
 134         msflags 
|= WS_BORDER
; 
 138     if ( style 
& wxCAPTION 
) 
 139         msflags 
|= WS_CAPTION
; 
 143     // next translate the individual flags 
 144     if ( style 
& wxMINIMIZE_BOX 
) 
 145         msflags 
|= WS_MINIMIZEBOX
; 
 146     if ( style 
& wxMAXIMIZE_BOX 
) 
 147         msflags 
|= WS_MAXIMIZEBOX
; 
 148     if ( style 
& wxSYSTEM_MENU 
) 
 149         msflags 
|= WS_SYSMENU
; 
 150     if ( style 
& wxMINIMIZE 
) 
 151         msflags 
|= WS_MINIMIZE
; 
 152     if ( style 
& wxMAXIMIZE 
) 
 153         msflags 
|= WS_MAXIMIZE
; 
 155     // Keep this here because it saves recoding this function in wxTinyFrame 
 156 #if wxUSE_ITSY_BITSY && !defined(__WIN32__) 
 157     if ( style 
& wxTINY_CAPTION_VERT 
) 
 158         msflags 
|= IBS_VERTCAPTION
; 
 159     if ( style 
& wxTINY_CAPTION_HORIZ 
) 
 160         msflags 
|= IBS_HORZCAPTION
; 
 162     if ( style 
& (wxTINY_CAPTION_VERT 
| wxTINY_CAPTION_HORIZ
) ) 
 163         msflags 
|= WS_CAPTION
; 
 168 #if !defined(__WIN16__) && !defined(__SC__) 
 169         if ( !(GetExtraStyle() & wxTOPLEVEL_EX_DIALOG
) ) 
 171             if ( style 
& wxFRAME_TOOL_WINDOW 
) 
 173                 // create the palette-like window 
 174                 *exflags 
|= WS_EX_TOOLWINDOW
; 
 177             // We have to solve 2 different problems here: 
 179             // 1. frames with wxFRAME_NO_TASKBAR flag shouldn't appear in the 
 180             //    taskbar even if they don't have a parent 
 182             // 2. frames without this style should appear in the taskbar even 
 183             //    if they're owned (Windows only puts non owned windows into 
 184             //    the taskbar normally) 
 186             // The second one is solved here by using WS_EX_APPWINDOW flag, the 
 187             // first one is dealt with in our MSWGetParent() method 
 189             if ( !(style 
& wxFRAME_NO_TASKBAR
) && GetParent() ) 
 191                 // need to force the frame to appear in the taskbar 
 192                 *exflags 
|= WS_EX_APPWINDOW
; 
 194             //else: nothing to do [here] 
 198         if ( style 
& wxSTAY_ON_TOP 
) 
 199             *exflags 
|= WS_EX_TOPMOST
; 
 202         if ( GetExtraStyle() & wxFRAME_EX_CONTEXTHELP 
) 
 203             *exflags 
|= WS_EX_CONTEXTHELP
; 
 210 WXHWND 
wxTopLevelWindowMSW::MSWGetParent() const 
 212     // for the frames without wxFRAME_FLOAT_ON_PARENT style we should use NULL 
 213     // parent HWND or it would be always on top of its parent which is not what 
 214     // we usually want (in fact, we only want it for frames with the 
 215     // wxFRAME_FLOAT_ON_PARENT flag) 
 217     if ( HasFlag(wxFRAME_FLOAT_ON_PARENT
) ) 
 219         parent 
= GetParent(); 
 221         // this flag doesn't make sense then and will be ignored 
 222         wxASSERT_MSG( parent
, 
 223                       _T("wxFRAME_FLOAT_ON_PARENT but no parent?") ); 
 225     else // don't float on parent, must not be owned 
 230     // now deal with the 2nd taskbar-related problem (see comments above in 
 232     if ( HasFlag(wxFRAME_NO_TASKBAR
) && !parent 
) 
 234         if ( !ms_hiddenParent 
) 
 236             ms_hiddenParent 
= new wxTopLevelWindowMSW(NULL
, -1, _T("")); 
 238             // we shouldn't leave it in wxTopLevelWindows or we wouldn't 
 239             // terminate the app when the last user-created frame is deleted -- 
 240             // see ~wxTopLevelWindowMSW 
 241             wxTopLevelWindows
.DeleteObject(ms_hiddenParent
); 
 244         parent 
= ms_hiddenParent
; 
 247     return parent 
? parent
->GetHWND() : WXHWND(NULL
); 
 250 bool wxTopLevelWindowMSW::CreateDialog(const void *dlgTemplate
, 
 251                                        const wxString
& title
, 
 255 #ifdef __WXMICROWIN__ 
 256     // no dialogs support under MicroWin yet 
 257     return CreateFrame(title
, pos
, size
); 
 258 #else // !__WXMICROWIN__ 
 259     wxWindow 
*parent 
= GetParent(); 
 261     // for the dialogs without wxDIALOG_NO_PARENT style, use the top level 
 262     // app window as parent - this avoids creating modal dialogs without 
 264     if ( !parent 
&& !(GetWindowStyleFlag() & wxDIALOG_NO_PARENT
) ) 
 266         parent 
= wxTheApp
->GetTopWindow(); 
 270             // don't use transient windows as parents, this is dangerous as it 
 271             // can lead to a crash if the parent is destroyed before the child 
 273             // also don't use the window which is currently hidden as then the 
 274             // dialog would be hidden as well 
 275             if ( (parent
->GetExtraStyle() & wxWS_EX_TRANSIENT
) || 
 283     m_hWnd 
= (WXHWND
)::CreateDialogIndirect
 
 286                         (DLGTEMPLATE
*)dlgTemplate
, 
 287                         parent 
? GetHwndOf(parent
) : NULL
, 
 293         wxFAIL_MSG(_("Failed to create dialog. Incorrect DLGTEMPLATE?")); 
 295         wxLogSysError(_("Can't create dialog using memory template")); 
 301     (void)MSWGetCreateWindowFlags(&exflags
); 
 305         ::SetWindowLong(GetHwnd(), GWL_EXSTYLE
, exflags
); 
 306         ::SetWindowPos(GetHwnd(), NULL
, 0, 0, 0, 0, 
 313 #if defined(__WIN95__) 
 314     // For some reason, the system menu is activated when we use the 
 315     // WS_EX_CONTEXTHELP style, so let's set a reasonable icon 
 316     if ( exflags 
& WS_EX_CONTEXTHELP 
) 
 318         wxFrame 
*winTop 
= wxDynamicCast(wxTheApp
->GetTopWindow(), wxFrame
); 
 321             wxIcon icon 
= winTop
->GetIcon(); 
 324                 ::SendMessage(GetHwnd(), WM_SETICON
, 
 326                               (LPARAM
)GetHiconOf(icon
)); 
 332     // move the dialog to its initial position without forcing repainting 
 334     if ( !MSWGetCreateWindowCoords(pos
, size
, x
, y
, w
, h
) ) 
 337         w 
= (int)CW_USEDEFAULT
; 
 340     // we can't use CW_USEDEFAULT here as we're not calling CreateWindow() 
 341     // and passing CW_USEDEFAULT to MoveWindow() results in resizing the 
 342     // window to (0, 0) size which breaks quite a lot of things, e.g. the 
 343     // sizer calculation in wxSizer::Fit() 
 344     if ( w 
== (int)CW_USEDEFAULT 
) 
 346         // the exact number doesn't matter, the dialog will be resized 
 347         // again soon anyhow but it should be big enough to allow 
 348         // calculation relying on "totalSize - clientSize > 0" work, i.e. 
 349         // at least greater than the title bar height 
 354     if ( x 
== (int)CW_USEDEFAULT 
) 
 356         // centre it on the screen - what else can we do? 
 357         wxSize sizeDpy 
= wxGetDisplaySize(); 
 359         x 
= (sizeDpy
.x 
- w
) / 2; 
 360         y 
= (sizeDpy
.y 
- h
) / 2; 
 363     if ( !::MoveWindow(GetHwnd(), x
, y
, w
, h
, FALSE
) ) 
 365         wxLogLastError(wxT("MoveWindow")); 
 368     if ( !title
.empty() ) 
 370         ::SetWindowText(GetHwnd(), title
); 
 376 #endif // __WXMICROWIN__/!__WXMICROWIN__ 
 379 bool wxTopLevelWindowMSW::CreateFrame(const wxString
& title
, 
 384     WXDWORD flags 
= MSWGetCreateWindowFlags(&exflags
); 
 386     return MSWCreate(wxCanvasClassName
, title
, pos
, size
, flags
, exflags
); 
 389 bool wxTopLevelWindowMSW::Create(wxWindow 
*parent
, 
 391                                  const wxString
& title
, 
 395                                  const wxString
& name
) 
 400     m_windowStyle 
= style
; 
 404     m_windowId 
= id 
== -1 ? NewControlId() : id
; 
 406     wxTopLevelWindows
.Append(this); 
 409         parent
->AddChild(this); 
 411     if ( GetExtraStyle() & wxTOPLEVEL_EX_DIALOG 
) 
 413         // we have different dialog templates to allows creation of dialogs 
 414         // with & without captions under MSWindows, resizeable or not (but a 
 415         // resizeable dialog always has caption - otherwise it would look too 
 418         // we need 3 additional WORDs for dialog menu, class and title (as we 
 419         // don't use DS_SETFONT we don't need the fourth WORD for the font) 
 420         static const int dlgsize 
= sizeof(DLGTEMPLATE
) + (sizeof(WORD
) * 3); 
 421         DLGTEMPLATE 
*dlgTemplate 
= (DLGTEMPLATE 
*)malloc(dlgsize
); 
 422         memset(dlgTemplate
, 0, dlgsize
); 
 424         // these values are arbitrary, they won't be used normally anyhow 
 427         dlgTemplate
->cx 
= 144; 
 428         dlgTemplate
->cy 
= 75; 
 430         // reuse the code in MSWGetStyle() but correct the results slightly for 
 432         dlgTemplate
->style 
= MSWGetStyle(style
, NULL
); 
 434         // all dialogs are popups 
 435         dlgTemplate
->style 
|= WS_POPUP
; 
 437         // force 3D-look if necessary, it looks impossibly ugly otherwise 
 438         if ( style 
& (wxRESIZE_BORDER 
| wxCAPTION
) ) 
 439             dlgTemplate
->style 
|= DS_MODALFRAME
; 
 441         bool ret 
= CreateDialog(dlgTemplate
, title
, pos
, size
); 
 448         return CreateFrame(title
, pos
, size
); 
 452 wxTopLevelWindowMSW::~wxTopLevelWindowMSW() 
 454     if ( this == ms_hiddenParent 
) 
 456         // stop [infinite] recursion which would otherwise happen when we do 
 457         // "delete ms_hiddenParent" below -- and we're not interested in doing 
 458         // anything of the rest below for that window because the rest of 
 459         // wxWindows doesn't even know about it 
 463     if ( wxModelessWindows
.Find(this) ) 
 464         wxModelessWindows
.DeleteObject(this); 
 466     // after destroying an owned window, Windows activates the next top level 
 467     // window in Z order but it may be different from our owner (to reproduce 
 468     // this simply Alt-TAB to another application and back before closing the 
 469     // owned frame) whereas we always want to yield activation to our parent 
 470     if ( HasFlag(wxFRAME_FLOAT_ON_PARENT
) ) 
 472         wxWindow 
*parent 
= GetParent(); 
 475             ::BringWindowToTop(GetHwndOf(parent
)); 
 479     // if this is the last top-level window, we're going to exit and we should 
 480     // delete ms_hiddenParent now to avoid leaking it 
 481     if ( IsLastBeforeExit() ) 
 483         if ( ms_hiddenParent 
) 
 485             delete ms_hiddenParent
; 
 486             ms_hiddenParent 
= NULL
; 
 491 // ---------------------------------------------------------------------------- 
 492 // wxTopLevelWindowMSW showing 
 493 // ---------------------------------------------------------------------------- 
 495 void wxTopLevelWindowMSW::DoShowWindow(int nShowCmd
) 
 497     ::ShowWindow(GetHwnd(), nShowCmd
); 
 499     m_iconized 
= nShowCmd 
== SW_MINIMIZE
; 
 502 bool wxTopLevelWindowMSW::Show(bool show
) 
 504     // don't use wxWindow version as we want to call DoShowWindow() ourselves 
 505     if ( !wxWindowBase::Show(show
) ) 
 511         if ( m_maximizeOnShow 
) 
 514             nShowCmd 
= SW_MAXIMIZE
; 
 516             m_maximizeOnShow 
= FALSE
; 
 528     DoShowWindow(nShowCmd
); 
 532         ::BringWindowToTop(GetHwnd()); 
 534         wxActivateEvent 
event(wxEVT_ACTIVATE
, TRUE
, m_windowId
); 
 535         event
.SetEventObject( this ); 
 536         GetEventHandler()->ProcessEvent(event
); 
 540         // Try to highlight the correct window (the parent) 
 543             HWND hWndParent 
= GetHwndOf(GetParent()); 
 545                 ::BringWindowToTop(hWndParent
); 
 552 // ---------------------------------------------------------------------------- 
 553 // wxTopLevelWindowMSW maximize/minimize 
 554 // ---------------------------------------------------------------------------- 
 556 void wxTopLevelWindowMSW::Maximize(bool maximize
) 
 560         // just maximize it directly 
 561         DoShowWindow(maximize 
? SW_MAXIMIZE 
: SW_RESTORE
); 
 565         // we can't maximize the hidden frame because it shows it as well, so 
 566         // just remember that we should do it later in this case 
 567         m_maximizeOnShow 
= TRUE
; 
 571 bool wxTopLevelWindowMSW::IsMaximized() const 
 573     return ::IsZoomed(GetHwnd()) != 0; 
 576 void wxTopLevelWindowMSW::Iconize(bool iconize
) 
 578     DoShowWindow(iconize 
? SW_MINIMIZE 
: SW_RESTORE
); 
 581 bool wxTopLevelWindowMSW::IsIconized() const 
 583     // also update the current state 
 584     ((wxTopLevelWindowMSW 
*)this)->m_iconized 
= ::IsIconic(GetHwnd()) != 0; 
 589 void wxTopLevelWindowMSW::Restore() 
 591     DoShowWindow(SW_RESTORE
); 
 594 // ---------------------------------------------------------------------------- 
 595 // wxTopLevelWindowMSW fullscreen 
 596 // ---------------------------------------------------------------------------- 
 598 bool wxTopLevelWindowMSW::ShowFullScreen(bool show
, long style
) 
 605         m_fsIsShowing 
= TRUE
; 
 608         // zap the frame borders 
 610         // save the 'normal' window style 
 611         m_fsOldWindowStyle 
= GetWindowLong((HWND
)GetHWND(), GWL_STYLE
); 
 613         // save the old position, width & height, maximize state 
 614         m_fsOldSize 
= GetRect(); 
 615         m_fsIsMaximized 
= IsMaximized(); 
 617         // decide which window style flags to turn off 
 618         LONG newStyle 
= m_fsOldWindowStyle
; 
 621         if (style 
& wxFULLSCREEN_NOBORDER
) 
 622             offFlags 
|= WS_BORDER 
| WS_THICKFRAME
; 
 623         if (style 
& wxFULLSCREEN_NOCAPTION
) 
 624             offFlags 
|= (WS_CAPTION 
| WS_SYSMENU
); 
 626         newStyle 
&= (~offFlags
); 
 628         // change our window style to be compatible with full-screen mode 
 629         ::SetWindowLong((HWND
)GetHWND(), GWL_STYLE
, newStyle
); 
 631         // resize to the size of the desktop 
 634         RECT rect 
= wxGetWindowRect(::GetDesktopWindow()); 
 635         width 
= rect
.right 
- rect
.left
; 
 636         height 
= rect
.bottom 
- rect
.top
; 
 638         SetSize(width
, height
); 
 640         // now flush the window style cache and actually go full-screen 
 641         SetWindowPos((HWND
)GetHWND(), HWND_TOP
, 0, 0, width
, height
, SWP_FRAMECHANGED
); 
 643         wxSizeEvent 
event(wxSize(width
, height
), GetId()); 
 644         GetEventHandler()->ProcessEvent(event
); 
 653         m_fsIsShowing 
= FALSE
; 
 655         Maximize(m_fsIsMaximized
); 
 656         SetWindowLong((HWND
)GetHWND(),GWL_STYLE
, m_fsOldWindowStyle
); 
 657         SetWindowPos((HWND
)GetHWND(),HWND_TOP
,m_fsOldSize
.x
, m_fsOldSize
.y
, 
 658             m_fsOldSize
.width
, m_fsOldSize
.height
, SWP_FRAMECHANGED
); 
 664 // ---------------------------------------------------------------------------- 
 665 // wxTopLevelWindowMSW misc 
 666 // ---------------------------------------------------------------------------- 
 668 void wxTopLevelWindowMSW::SetIcon(const wxIcon
& icon
) 
 670     SetIcons( wxIconBundle( icon 
) ); 
 673 void wxTopLevelWindowMSW::SetIcons(const wxIconBundle
& icons
) 
 675     wxTopLevelWindowBase::SetIcons(icons
); 
 677 #if defined(__WIN95__) && !defined(__WXMICROWIN__) 
 678     const wxIcon
& sml 
= icons
.GetIcon( wxSize( 16, 16 ) ); 
 679     if( sml
.Ok() && sml
.GetWidth() == 16 && sml
.GetHeight() == 16 ) 
 681         ::SendMessage( GetHwndOf( this ), WM_SETICON
, ICON_SMALL
, 
 682                        (LPARAM
)GetHiconOf(sml
) ); 
 685     const wxIcon
& big 
= icons
.GetIcon( wxSize( 32, 32 ) ); 
 686     if( big
.Ok() && big
.GetWidth() == 32 && big
.GetHeight() == 32 ) 
 688         ::SendMessage( GetHwndOf( this ), WM_SETICON
, ICON_BIG
, 
 689                        (LPARAM
)GetHiconOf(big
) ); 
 694 bool wxTopLevelWindowMSW::EnableCloseButton(bool enable
) 
 696 #ifndef __WXMICROWIN__ 
 697     // get system (a.k.a. window) menu 
 698     HMENU hmenu 
= ::GetSystemMenu(GetHwnd(), FALSE 
/* get it */); 
 701         wxLogLastError(_T("GetSystemMenu")); 
 706     // enabling/disabling the close item from it also automatically 
 707     // disables/enables the close title bar button 
 708     if ( ::EnableMenuItem(hmenu
, SC_CLOSE
, 
 710                           (enable 
? MF_ENABLED 
: MF_GRAYED
)) == -1 ) 
 712         wxLogLastError(_T("EnableMenuItem(SC_CLOSE)")); 
 717     // update appearance immediately 
 718     if ( !::DrawMenuBar(GetHwnd()) ) 
 720         wxLogLastError(_T("DrawMenuBar")); 
 722 #endif // !__WXMICROWIN__ 
 727 // ---------------------------------------------------------------------------- 
 728 // wxTopLevelWindow event handling 
 729 // ---------------------------------------------------------------------------- 
 731 // Default activation behaviour - set the focus for the first child 
 733 void wxTopLevelWindowMSW::OnActivate(wxActivateEvent
& event
) 
 735     if ( event
.GetActive() ) 
 737         // restore focus to the child which was last focused 
 738         wxLogTrace(_T("focus"), _T("wxTLW %08x activated."), m_hWnd
); 
 740         wxWindow 
*parent 
= m_winLastFocused 
? m_winLastFocused
->GetParent() 
 747         wxSetFocusToChild(parent
, &m_winLastFocused
); 
 751         // remember the last focused child if it is our child 
 752         m_winLastFocused 
= FindFocus(); 
 754         // so we NULL it out if it's a child from some other frame 
 755         wxWindow 
*win 
= m_winLastFocused
; 
 758             if ( win
->IsTopLevel() ) 
 762                     m_winLastFocused 
= NULL
; 
 768             win 
= win
->GetParent(); 
 771         wxLogTrace(_T("focus"), 
 772                    _T("wxTLW %08x deactivated, last focused: %08x."), 
 774                    m_winLastFocused 
? GetHwndOf(m_winLastFocused
) 
 781 // the DialogProc for all wxWindows dialogs 
 782 LONG APIENTRY _EXPORT
 
 783 wxDlgProc(HWND hDlg
, UINT message
, WPARAM wParam
, LPARAM lParam
) 
 788             // for this message, returning TRUE tells system to set focus to 
 789             // the first control in the dialog box, but as we set the focus 
 790             // ourselves, we return FALSE from here as well, so fall through 
 793             // for all the other ones, FALSE means that we didn't process the