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 // If this is the last top-level window, exit.
477 if ( wxTheApp
&& (wxTopLevelWindows
.Number() == 0) )
479 if ( ms_hiddenParent
)
481 delete ms_hiddenParent
;
482 ms_hiddenParent
= NULL
;
485 wxTheApp
->SetTopWindow(NULL
);
487 if ( wxTheApp
->GetExitOnFrameDelete() )
489 ::PostQuitMessage(0);
494 // ----------------------------------------------------------------------------
495 // wxTopLevelWindowMSW showing
496 // ----------------------------------------------------------------------------
498 void wxTopLevelWindowMSW::DoShowWindow(int nShowCmd
)
500 ::ShowWindow(GetHwnd(), nShowCmd
);
502 m_iconized
= nShowCmd
== SW_MINIMIZE
;
505 bool wxTopLevelWindowMSW::Show(bool show
)
507 // don't use wxWindow version as we want to call DoShowWindow() ourselves
508 if ( !wxWindowBase::Show(show
) )
514 if ( m_maximizeOnShow
)
517 nShowCmd
= SW_MAXIMIZE
;
519 m_maximizeOnShow
= FALSE
;
531 DoShowWindow(nShowCmd
);
535 ::BringWindowToTop(GetHwnd());
537 wxActivateEvent
event(wxEVT_ACTIVATE
, TRUE
, m_windowId
);
538 event
.SetEventObject( this );
539 GetEventHandler()->ProcessEvent(event
);
543 // Try to highlight the correct window (the parent)
546 HWND hWndParent
= GetHwndOf(GetParent());
548 ::BringWindowToTop(hWndParent
);
555 // ----------------------------------------------------------------------------
556 // wxTopLevelWindowMSW maximize/minimize
557 // ----------------------------------------------------------------------------
559 void wxTopLevelWindowMSW::Maximize(bool maximize
)
563 // just maximize it directly
564 DoShowWindow(maximize
? SW_MAXIMIZE
: SW_RESTORE
);
568 // we can't maximize the hidden frame because it shows it as well, so
569 // just remember that we should do it later in this case
570 m_maximizeOnShow
= TRUE
;
574 bool wxTopLevelWindowMSW::IsMaximized() const
576 return ::IsZoomed(GetHwnd()) != 0;
579 void wxTopLevelWindowMSW::Iconize(bool iconize
)
581 DoShowWindow(iconize
? SW_MINIMIZE
: SW_RESTORE
);
584 bool wxTopLevelWindowMSW::IsIconized() const
586 // also update the current state
587 ((wxTopLevelWindowMSW
*)this)->m_iconized
= ::IsIconic(GetHwnd()) != 0;
592 void wxTopLevelWindowMSW::Restore()
594 DoShowWindow(SW_RESTORE
);
597 // ----------------------------------------------------------------------------
598 // wxTopLevelWindowMSW fullscreen
599 // ----------------------------------------------------------------------------
601 bool wxTopLevelWindowMSW::ShowFullScreen(bool show
, long style
)
608 m_fsIsShowing
= TRUE
;
611 // zap the frame borders
613 // save the 'normal' window style
614 m_fsOldWindowStyle
= GetWindowLong((HWND
)GetHWND(), GWL_STYLE
);
616 // save the old position, width & height, maximize state
617 m_fsOldSize
= GetRect();
618 m_fsIsMaximized
= IsMaximized();
620 // decide which window style flags to turn off
621 LONG newStyle
= m_fsOldWindowStyle
;
624 if (style
& wxFULLSCREEN_NOBORDER
)
625 offFlags
|= WS_BORDER
| WS_THICKFRAME
;
626 if (style
& wxFULLSCREEN_NOCAPTION
)
627 offFlags
|= (WS_CAPTION
| WS_SYSMENU
);
629 newStyle
&= (~offFlags
);
631 // change our window style to be compatible with full-screen mode
632 ::SetWindowLong((HWND
)GetHWND(), GWL_STYLE
, newStyle
);
634 // resize to the size of the desktop
637 RECT rect
= wxGetWindowRect(::GetDesktopWindow());
638 width
= rect
.right
- rect
.left
;
639 height
= rect
.bottom
- rect
.top
;
641 SetSize(width
, height
);
643 // now flush the window style cache and actually go full-screen
644 SetWindowPos((HWND
)GetHWND(), HWND_TOP
, 0, 0, width
, height
, SWP_FRAMECHANGED
);
646 wxSizeEvent
event(wxSize(width
, height
), GetId());
647 GetEventHandler()->ProcessEvent(event
);
656 m_fsIsShowing
= FALSE
;
658 Maximize(m_fsIsMaximized
);
659 SetWindowLong((HWND
)GetHWND(),GWL_STYLE
, m_fsOldWindowStyle
);
660 SetWindowPos((HWND
)GetHWND(),HWND_TOP
,m_fsOldSize
.x
, m_fsOldSize
.y
,
661 m_fsOldSize
.width
, m_fsOldSize
.height
, SWP_FRAMECHANGED
);
667 // ----------------------------------------------------------------------------
668 // wxTopLevelWindowMSW misc
669 // ----------------------------------------------------------------------------
671 void wxTopLevelWindowMSW::SetIcon(const wxIcon
& icon
)
673 SetIcons( wxIconBundle( icon
) );
676 void wxTopLevelWindowMSW::SetIcons(const wxIconBundle
& icons
)
678 wxTopLevelWindowBase::SetIcons(icons
);
680 #if defined(__WIN95__) && !defined(__WXMICROWIN__)
681 const wxIcon
& sml
= icons
.GetIcon( wxSize( 16, 16 ) );
682 if( sml
.Ok() && sml
.GetWidth() == 16 && sml
.GetHeight() == 16 )
684 ::SendMessage( GetHwndOf( this ), WM_SETICON
, ICON_SMALL
,
685 (LPARAM
)GetHiconOf(sml
) );
688 const wxIcon
& big
= icons
.GetIcon( wxSize( 32, 32 ) );
689 if( big
.Ok() && big
.GetWidth() == 32 && big
.GetHeight() == 32 )
691 ::SendMessage( GetHwndOf( this ), WM_SETICON
, ICON_BIG
,
692 (LPARAM
)GetHiconOf(big
) );
697 bool wxTopLevelWindowMSW::EnableCloseButton(bool enable
)
699 #ifndef __WXMICROWIN__
700 // get system (a.k.a. window) menu
701 HMENU hmenu
= ::GetSystemMenu(GetHwnd(), FALSE
/* get it */);
704 wxLogLastError(_T("GetSystemMenu"));
709 // enabling/disabling the close item from it also automatically
710 // disables/enables the close title bar button
711 if ( ::EnableMenuItem(hmenu
, SC_CLOSE
,
713 (enable
? MF_ENABLED
: MF_GRAYED
)) == -1 )
715 wxLogLastError(_T("EnableMenuItem(SC_CLOSE)"));
720 // update appearance immediately
721 if ( !::DrawMenuBar(GetHwnd()) )
723 wxLogLastError(_T("DrawMenuBar"));
725 #endif // !__WXMICROWIN__
730 // ----------------------------------------------------------------------------
731 // wxTopLevelWindowMSW message processing
732 // ----------------------------------------------------------------------------
734 long wxTopLevelWindowMSW::HandleNcActivate(bool activate
)
738 Normally, when another top level (whether it is overlapped or popup)
739 window is shown, it is activated and the parent window (i.e. we) loses
740 the activation. This, however, looks very ugly when the child window is
741 a [custom] combobox which we implement using a popup window as surely
742 opening a combobox shouldn't result in deactivating the parent window.
744 So we don't redraw the title bar in this case, even if we still return
745 TRUE to let the change of activation to take place as otherwise the
746 controls inside the popup window wouldn't work properly.
748 if ( !activate
&& wxPopupWindow::FindPopupFor(this) )
752 #endif // wxUSE_POPUPWIN
758 wxTopLevelWindowMSW::MSWWindowProc(WXUINT msg
, WXWPARAM wParam
, WXLPARAM lParam
)
760 if ( msg
== WM_NCACTIVATE
&& HandleNcActivate(wParam
!= 0) )
762 // we processed WM_NCACTIVATE ourselves
766 return wxTopLevelWindowBase::MSWWindowProc(msg
, wParam
, lParam
);