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"
40 #include "wx/msw/private.h"
42 #include "wx/popupwin.h"
52 // ----------------------------------------------------------------------------
53 // stubs for missing functions under MicroWindows
54 // ----------------------------------------------------------------------------
58 // static inline bool IsIconic(HWND WXUNUSED(hwnd)) { return FALSE; }
59 static inline bool IsZoomed(HWND
WXUNUSED(hwnd
)) { return FALSE
; }
61 #endif // __WXMICROWIN__
63 // ----------------------------------------------------------------------------
65 // ----------------------------------------------------------------------------
67 // list of all frames and modeless dialogs
68 wxWindowList wxModelessWindows
;
70 // the name of the default wxWindows class
71 extern const wxChar
*wxCanvasClassName
;
73 // the hidden parent for wxFRAME_NO_TASKBAR unowned frames
74 wxWindow
*wxTopLevelWindowMSW::ms_hiddenParent
= NULL
;
76 // ============================================================================
77 // wxTopLevelWindowMSW implementation
78 // ============================================================================
80 // ----------------------------------------------------------------------------
82 // ----------------------------------------------------------------------------
86 wxDlgProc(HWND hDlg
, UINT message
, WPARAM wParam
, LPARAM lParam
)
91 // for this message, returning TRUE tells system to set focus to
92 // the first control in the dialog box, but as we set the focus
93 // ourselves, we return FALSE from here as well, so fall through
96 // for all the other ones, FALSE means that we didn't process the
102 // ----------------------------------------------------------------------------
103 // wxTopLevelWindowMSW creation
104 // ----------------------------------------------------------------------------
106 void wxTopLevelWindowMSW::Init()
109 m_maximizeOnShow
= FALSE
;
111 // unlike (almost?) all other windows, frames are created hidden
114 // Data to save/restore when calling ShowFullScreen
116 m_fsOldWindowStyle
= 0;
117 m_fsIsMaximized
= FALSE
;
118 m_fsIsShowing
= FALSE
;
121 WXDWORD
wxTopLevelWindowMSW::MSWGetStyle(long style
, WXDWORD
*exflags
) const
123 // let the base class deal with the common styles but fix the ones which
124 // don't make sense for us (we also deal with the borders ourselves)
125 WXDWORD msflags
= wxWindow::MSWGetStyle
127 (style
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exflags
130 // first select the kind of window being created
132 // note that if we don't set WS_POPUP, Windows assumes WS_OVERLAPPED and
133 // creates a window with both caption and border, hence we also test it
134 // below in some other cases
135 if ( style
& wxFRAME_TOOL_WINDOW
)
138 msflags
|= WS_OVERLAPPED
;
140 // border and caption styles
141 if ( style
& wxRESIZE_BORDER
)
142 msflags
|= WS_THICKFRAME
;
143 else if ( !(style
& wxBORDER_NONE
) )
144 msflags
|= WS_BORDER
;
148 if ( style
& wxCAPTION
)
149 msflags
|= WS_CAPTION
;
153 // next translate the individual flags
154 if ( style
& wxMINIMIZE_BOX
)
155 msflags
|= WS_MINIMIZEBOX
;
156 if ( style
& wxMAXIMIZE_BOX
)
157 msflags
|= WS_MAXIMIZEBOX
;
158 if ( style
& wxSYSTEM_MENU
)
159 msflags
|= WS_SYSMENU
;
160 if ( style
& wxMINIMIZE
)
161 msflags
|= WS_MINIMIZE
;
162 if ( style
& wxMAXIMIZE
)
163 msflags
|= WS_MAXIMIZE
;
165 // Keep this here because it saves recoding this function in wxTinyFrame
166 #if wxUSE_ITSY_BITSY && !defined(__WIN32__)
167 if ( style
& wxTINY_CAPTION_VERT
)
168 msflags
|= IBS_VERTCAPTION
;
169 if ( style
& wxTINY_CAPTION_HORIZ
)
170 msflags
|= IBS_HORZCAPTION
;
172 if ( style
& (wxTINY_CAPTION_VERT
| wxTINY_CAPTION_HORIZ
) )
173 msflags
|= WS_CAPTION
;
178 #if !defined(__WIN16__) && !defined(__SC__)
179 if ( !(GetExtraStyle() & wxTOPLEVEL_EX_DIALOG
) )
181 if ( style
& wxFRAME_TOOL_WINDOW
)
183 // create the palette-like window
184 *exflags
|= WS_EX_TOOLWINDOW
;
187 // We have to solve 2 different problems here:
189 // 1. frames with wxFRAME_NO_TASKBAR flag shouldn't appear in the
190 // taskbar even if they don't have a parent
192 // 2. frames without this style should appear in the taskbar even
193 // if they're owned (Windows only puts non owned windows into
194 // the taskbar normally)
196 // The second one is solved here by using WS_EX_APPWINDOW flag, the
197 // first one is dealt with in our MSWGetParent() method
199 if ( !(style
& wxFRAME_NO_TASKBAR
) && GetParent() )
201 // need to force the frame to appear in the taskbar
202 *exflags
|= WS_EX_APPWINDOW
;
204 //else: nothing to do [here]
208 if ( style
& wxSTAY_ON_TOP
)
209 *exflags
|= WS_EX_TOPMOST
;
212 if ( GetExtraStyle() & wxFRAME_EX_CONTEXTHELP
)
213 *exflags
|= WS_EX_CONTEXTHELP
;
220 WXHWND
wxTopLevelWindowMSW::MSWGetParent() const
222 // for the frames without wxFRAME_FLOAT_ON_PARENT style we should use NULL
223 // parent HWND or it would be always on top of its parent which is not what
224 // we usually want (in fact, we only want it for frames with the
225 // wxFRAME_FLOAT_ON_PARENT flag)
227 if ( HasFlag(wxFRAME_FLOAT_ON_PARENT
) )
229 parent
= GetParent();
231 // this flag doesn't make sense then and will be ignored
232 wxASSERT_MSG( parent
,
233 _T("wxFRAME_FLOAT_ON_PARENT but no parent?") );
235 else // don't float on parent, must not be owned
240 // now deal with the 2nd taskbar-related problem (see comments above in
242 if ( HasFlag(wxFRAME_NO_TASKBAR
) && !parent
)
244 if ( !ms_hiddenParent
)
246 ms_hiddenParent
= new wxTopLevelWindowMSW(NULL
, -1, _T(""));
248 // we shouldn't leave it in wxTopLevelWindows or we wouldn't
249 // terminate the app when the last user-created frame is deleted --
250 // see ~wxTopLevelWindowMSW
251 wxTopLevelWindows
.DeleteObject(ms_hiddenParent
);
254 parent
= ms_hiddenParent
;
257 return parent
? parent
->GetHWND() : NULL
;
260 bool wxTopLevelWindowMSW::CreateDialog(const void *dlgTemplate
,
261 const wxString
& title
,
265 #ifdef __WXMICROWIN__
266 // no dialogs support under MicroWin yet
267 return CreateFrame(title
, pos
, size
);
268 #else // !__WXMICROWIN__
269 wxWindow
*parent
= GetParent();
271 // for the dialogs without wxDIALOG_NO_PARENT style, use the top level
272 // app window as parent - this avoids creating modal dialogs without
274 if ( !parent
&& !(GetWindowStyleFlag() & wxDIALOG_NO_PARENT
) )
276 parent
= wxTheApp
->GetTopWindow();
280 // don't use transient windows as parents, this is dangerous as it
281 // can lead to a crash if the parent is destroyed before the child
283 // also don't use the window which is currently hidden as then the
284 // dialog would be hidden as well
285 if ( (parent
->GetExtraStyle() & wxWS_EX_TRANSIENT
) ||
293 m_hWnd
= (WXHWND
)::CreateDialogIndirect
296 (DLGTEMPLATE
*)dlgTemplate
,
297 parent
? GetHwndOf(parent
) : NULL
,
303 wxFAIL_MSG(_("Failed to create dialog. Incorrect DLGTEMPLATE?"));
305 wxLogSysError(_("Can't create dialog using memory template"));
311 (void)MSWGetCreateWindowFlags(&exflags
);
315 ::SetWindowLong(GetHwnd(), GWL_EXSTYLE
, exflags
);
316 ::SetWindowPos(GetHwnd(), NULL
, 0, 0, 0, 0,
323 #if defined(__WIN95__)
324 // For some reason, the system menu is activated when we use the
325 // WS_EX_CONTEXTHELP style, so let's set a reasonable icon
326 if ( exflags
& WS_EX_CONTEXTHELP
)
328 wxFrame
*winTop
= wxDynamicCast(wxTheApp
->GetTopWindow(), wxFrame
);
331 wxIcon icon
= winTop
->GetIcon();
334 ::SendMessage(GetHwnd(), WM_SETICON
,
336 (LPARAM
)GetHiconOf(icon
));
342 // move the dialog to its initial position without forcing repainting
344 if ( !MSWGetCreateWindowCoords(pos
, size
, x
, y
, w
, h
) )
347 w
= (int)CW_USEDEFAULT
;
350 // we can't use CW_USEDEFAULT here as we're not calling CreateWindow()
351 // and passing CW_USEDEFAULT to MoveWindow() results in resizing the
352 // window to (0, 0) size which breaks quite a lot of things, e.g. the
353 // sizer calculation in wxSizer::Fit()
354 if ( w
== (int)CW_USEDEFAULT
)
356 // the exact number doesn't matter, the dialog will be resized
357 // again soon anyhow but it should be big enough to allow
358 // calculation relying on "totalSize - clientSize > 0" work, i.e.
359 // at least greater than the title bar height
364 if ( x
== (int)CW_USEDEFAULT
)
366 // centre it on the screen - what else can we do?
367 wxSize sizeDpy
= wxGetDisplaySize();
369 x
= (sizeDpy
.x
- w
) / 2;
370 y
= (sizeDpy
.y
- h
) / 2;
373 if ( !::MoveWindow(GetHwnd(), x
, y
, w
, h
, FALSE
) )
375 wxLogLastError(wxT("MoveWindow"));
378 if ( !title
.empty() )
380 ::SetWindowText(GetHwnd(), title
);
386 #endif // __WXMICROWIN__/!__WXMICROWIN__
389 bool wxTopLevelWindowMSW::CreateFrame(const wxString
& title
,
394 WXDWORD flags
= MSWGetCreateWindowFlags(&exflags
);
396 return MSWCreate(wxCanvasClassName
, title
, pos
, size
, flags
, exflags
);
399 bool wxTopLevelWindowMSW::Create(wxWindow
*parent
,
401 const wxString
& title
,
405 const wxString
& name
)
410 m_windowStyle
= style
;
414 m_windowId
= id
== -1 ? NewControlId() : id
;
416 wxTopLevelWindows
.Append(this);
419 parent
->AddChild(this);
421 if ( GetExtraStyle() & wxTOPLEVEL_EX_DIALOG
)
423 // we have different dialog templates to allows creation of dialogs
424 // with & without captions under MSWindows, resizeable or not (but a
425 // resizeable dialog always has caption - otherwise it would look too
428 // we need 3 additional WORDs for dialog menu, class and title (as we
429 // don't use DS_SETFONT we don't need the fourth WORD for the font)
430 static const int dlgsize
= sizeof(DLGTEMPLATE
) + (sizeof(WORD
) * 3);
431 DLGTEMPLATE
*dlgTemplate
= (DLGTEMPLATE
*)malloc(dlgsize
);
432 memset(dlgTemplate
, 0, dlgsize
);
434 // these values are arbitrary, they won't be used normally anyhow
437 dlgTemplate
->cx
= 144;
438 dlgTemplate
->cy
= 75;
440 // reuse the code in MSWGetStyle() but correct the results slightly for
442 dlgTemplate
->style
= MSWGetStyle(style
, NULL
);
444 // all dialogs are popups
445 dlgTemplate
->style
|= WS_POPUP
;
447 // force 3D-look if necessary, it looks impossibly ugly otherwise
448 if ( style
& (wxRESIZE_BORDER
| wxCAPTION
) )
449 dlgTemplate
->style
|= DS_MODALFRAME
;
451 bool ret
= CreateDialog(dlgTemplate
, title
, pos
, size
);
458 return CreateFrame(title
, pos
, size
);
462 wxTopLevelWindowMSW::~wxTopLevelWindowMSW()
464 if ( this == ms_hiddenParent
)
466 // stop [infinite] recursion which would otherwise happen when we do
467 // "delete ms_hiddenParent" below
471 wxTopLevelWindows
.DeleteObject(this);
473 if ( wxModelessWindows
.Find(this) )
474 wxModelessWindows
.DeleteObject(this);
476 // after destroying an owned window, Windows activates the next top level
477 // window in Z order but it may be different from our owner (to reproduce
478 // this simply Alt-TAB to another application and back before closing the
479 // owned frame) whereas we always want to yield activation to our parent
480 if ( HasFlag(wxFRAME_FLOAT_ON_PARENT
) )
482 wxWindow
*parent
= GetParent();
485 ::BringWindowToTop(GetHwndOf(parent
));
489 // If this is the last top-level window, exit.
490 if ( wxTheApp
&& (wxTopLevelWindows
.Number() == 0) )
492 if ( ms_hiddenParent
)
494 delete ms_hiddenParent
;
495 ms_hiddenParent
= NULL
;
498 wxTheApp
->SetTopWindow(NULL
);
500 if ( wxTheApp
->GetExitOnFrameDelete() )
502 ::PostQuitMessage(0);
507 // ----------------------------------------------------------------------------
508 // wxTopLevelWindowMSW showing
509 // ----------------------------------------------------------------------------
511 void wxTopLevelWindowMSW::DoShowWindow(int nShowCmd
)
513 ::ShowWindow(GetHwnd(), nShowCmd
);
515 m_iconized
= nShowCmd
== SW_MINIMIZE
;
518 bool wxTopLevelWindowMSW::Show(bool show
)
520 // don't use wxWindow version as we want to call DoShowWindow() ourselves
521 if ( !wxWindowBase::Show(show
) )
527 if ( m_maximizeOnShow
)
530 nShowCmd
= SW_MAXIMIZE
;
532 m_maximizeOnShow
= FALSE
;
544 DoShowWindow(nShowCmd
);
548 ::BringWindowToTop(GetHwnd());
550 wxActivateEvent
event(wxEVT_ACTIVATE
, TRUE
, m_windowId
);
551 event
.SetEventObject( this );
552 GetEventHandler()->ProcessEvent(event
);
556 // Try to highlight the correct window (the parent)
559 HWND hWndParent
= GetHwndOf(GetParent());
561 ::BringWindowToTop(hWndParent
);
568 // ----------------------------------------------------------------------------
569 // wxTopLevelWindowMSW maximize/minimize
570 // ----------------------------------------------------------------------------
572 void wxTopLevelWindowMSW::Maximize(bool maximize
)
576 // just maximize it directly
577 DoShowWindow(maximize
? SW_MAXIMIZE
: SW_RESTORE
);
581 // we can't maximize the hidden frame because it shows it as well, so
582 // just remember that we should do it later in this case
583 m_maximizeOnShow
= TRUE
;
587 bool wxTopLevelWindowMSW::IsMaximized() const
589 return ::IsZoomed(GetHwnd()) != 0;
592 void wxTopLevelWindowMSW::Iconize(bool iconize
)
594 DoShowWindow(iconize
? SW_MINIMIZE
: SW_RESTORE
);
597 bool wxTopLevelWindowMSW::IsIconized() const
599 // also update the current state
600 ((wxTopLevelWindowMSW
*)this)->m_iconized
= ::IsIconic(GetHwnd()) != 0;
605 void wxTopLevelWindowMSW::Restore()
607 DoShowWindow(SW_RESTORE
);
610 // ----------------------------------------------------------------------------
611 // wxTopLevelWindowMSW fullscreen
612 // ----------------------------------------------------------------------------
614 bool wxTopLevelWindowMSW::ShowFullScreen(bool show
, long style
)
621 m_fsIsShowing
= TRUE
;
624 // zap the frame borders
626 // save the 'normal' window style
627 m_fsOldWindowStyle
= GetWindowLong((HWND
)GetHWND(), GWL_STYLE
);
629 // save the old position, width & height, maximize state
630 m_fsOldSize
= GetRect();
631 m_fsIsMaximized
= IsMaximized();
633 // decide which window style flags to turn off
634 LONG newStyle
= m_fsOldWindowStyle
;
637 if (style
& wxFULLSCREEN_NOBORDER
)
638 offFlags
|= WS_BORDER
| WS_THICKFRAME
;
639 if (style
& wxFULLSCREEN_NOCAPTION
)
640 offFlags
|= (WS_CAPTION
| WS_SYSMENU
);
642 newStyle
&= (~offFlags
);
644 // change our window style to be compatible with full-screen mode
645 ::SetWindowLong((HWND
)GetHWND(), GWL_STYLE
, newStyle
);
647 // resize to the size of the desktop
650 RECT rect
= wxGetWindowRect(::GetDesktopWindow());
651 width
= rect
.right
- rect
.left
;
652 height
= rect
.bottom
- rect
.top
;
654 SetSize(width
, height
);
656 // now flush the window style cache and actually go full-screen
657 SetWindowPos((HWND
)GetHWND(), HWND_TOP
, 0, 0, width
, height
, SWP_FRAMECHANGED
);
659 wxSizeEvent
event(wxSize(width
, height
), GetId());
660 GetEventHandler()->ProcessEvent(event
);
669 m_fsIsShowing
= FALSE
;
671 Maximize(m_fsIsMaximized
);
672 SetWindowLong((HWND
)GetHWND(),GWL_STYLE
, m_fsOldWindowStyle
);
673 SetWindowPos((HWND
)GetHWND(),HWND_TOP
,m_fsOldSize
.x
, m_fsOldSize
.y
,
674 m_fsOldSize
.width
, m_fsOldSize
.height
, SWP_FRAMECHANGED
);
680 // ----------------------------------------------------------------------------
681 // wxTopLevelWindowMSW misc
682 // ----------------------------------------------------------------------------
684 void wxTopLevelWindowMSW::SetIcon(const wxIcon
& icon
)
686 SetIcons( wxIconBundle( icon
) );
689 void wxTopLevelWindowMSW::SetIcons(const wxIconBundle
& icons
)
691 wxTopLevelWindowBase::SetIcons(icons
);
693 #if defined(__WIN95__) && !defined(__WXMICROWIN__)
694 const wxIcon
& sml
= icons
.GetIcon( wxSize( 16, 16 ) );
695 if( sml
.Ok() && sml
.GetWidth() == 16 && sml
.GetHeight() == 16 )
697 ::SendMessage( GetHwndOf( this ), WM_SETICON
, ICON_SMALL
,
698 (LPARAM
)GetHiconOf(sml
) );
701 const wxIcon
& big
= icons
.GetIcon( wxSize( 32, 32 ) );
702 if( big
.Ok() && big
.GetWidth() == 32 && big
.GetHeight() == 32 )
704 ::SendMessage( GetHwndOf( this ), WM_SETICON
, ICON_BIG
,
705 (LPARAM
)GetHiconOf(big
) );
710 bool wxTopLevelWindowMSW::EnableCloseButton(bool enable
)
712 #ifndef __WXMICROWIN__
713 // get system (a.k.a. window) menu
714 HMENU hmenu
= ::GetSystemMenu(GetHwnd(), FALSE
/* get it */);
717 wxLogLastError(_T("GetSystemMenu"));
722 // enabling/disabling the close item from it also automatically
723 // disables/enables the close title bar button
724 if ( ::EnableMenuItem(hmenu
, SC_CLOSE
,
726 (enable
? MF_ENABLED
: MF_GRAYED
)) == -1 )
728 wxLogLastError(_T("EnableMenuItem(SC_CLOSE)"));
733 // update appearance immediately
734 if ( !::DrawMenuBar(GetHwnd()) )
736 wxLogLastError(_T("DrawMenuBar"));
738 #endif // !__WXMICROWIN__
743 // ----------------------------------------------------------------------------
744 // wxTopLevelWindowMSW message processing
745 // ----------------------------------------------------------------------------
747 long wxTopLevelWindowMSW::HandleNcActivate(bool activate
)
751 Normally, when another top level (whether it is overlapped or popup)
752 window is shown, it is activated and the parent window (i.e. we) loses
753 the activation. This, however, looks very ugly when the child window is
754 a [custom] combobox which we implement using a popup window as surely
755 opening a combobox shouldn't result in deactivating the parent window.
757 So we don't redraw the title bar in this case, even if we still return
758 TRUE to let the change of activation to take place as otherwise the
759 controls inside the popup window wouldn't work properly.
761 if ( !activate
&& wxPopupWindow::FindPopupFor(this) )
765 #endif // wxUSE_POPUPWIN
771 wxTopLevelWindowMSW::MSWWindowProc(WXUINT msg
, WXWPARAM wParam
, WXLPARAM lParam
)
773 if ( msg
== WM_NCACTIVATE
)
775 if ( HandleNcActivate(wParam
!= 0) )
777 // we processed WM_NCACTIVATE ourselves
782 return wxTopLevelWindowBase::MSWWindowProc(msg
, wParam
, lParam
);