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 // ============================================================================
74 // wxTopLevelWindowMSW implementation
75 // ============================================================================
77 // ----------------------------------------------------------------------------
79 // ----------------------------------------------------------------------------
83 wxDlgProc(HWND hDlg
, UINT message
, WPARAM wParam
, LPARAM lParam
)
88 // for this message, returning TRUE tells system to set focus to the
89 // first control in the dialog box
93 // for all the other ones, FALSE means that we didn't process the
99 // ----------------------------------------------------------------------------
100 // wxTopLevelWindowMSW creation
101 // ----------------------------------------------------------------------------
103 void wxTopLevelWindowMSW::Init()
106 m_maximizeOnShow
= FALSE
;
108 // unlike (almost?) all other windows, frames are created hidden
111 // Data to save/restore when calling ShowFullScreen
113 m_fsOldWindowStyle
= 0;
114 m_fsIsMaximized
= FALSE
;
115 m_fsIsShowing
= FALSE
;
118 WXDWORD
wxTopLevelWindowMSW::MSWGetStyle(long style
, WXDWORD
*exflags
) const
120 // let the base class deal with the common styles but fix the ones which
121 // don't make sense for us (we also deal with the borders ourselves)
122 WXDWORD msflags
= wxWindow::MSWGetStyle
124 (style
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exflags
127 // first select the kind of window being created
129 // note that if we don't set WS_POPUP, Windows assumes WS_OVERLAPPED and
130 // creates a window with both caption and border, hence we also test it
131 // below in some other cases
132 if ( style
& wxFRAME_TOOL_WINDOW
)
135 msflags
|= WS_OVERLAPPED
;
137 // border and caption styles
138 if ( style
& wxRESIZE_BORDER
)
139 msflags
|= WS_THICKFRAME
;
140 else if ( !(style
& wxBORDER_NONE
) )
141 msflags
|= WS_BORDER
;
145 if ( style
& wxCAPTION
)
146 msflags
|= WS_CAPTION
;
150 // next translate the individual flags
151 if ( style
& wxMINIMIZE_BOX
)
152 msflags
|= WS_MINIMIZEBOX
;
153 if ( style
& wxMAXIMIZE_BOX
)
154 msflags
|= WS_MAXIMIZEBOX
;
155 if ( style
& wxSYSTEM_MENU
)
156 msflags
|= WS_SYSMENU
;
157 if ( style
& wxMINIMIZE
)
158 msflags
|= WS_MINIMIZE
;
159 if ( style
& wxMAXIMIZE
)
160 msflags
|= WS_MAXIMIZE
;
162 // Keep this here because it saves recoding this function in wxTinyFrame
163 #if wxUSE_ITSY_BITSY && !defined(__WIN32__)
164 if ( style
& wxTINY_CAPTION_VERT
)
165 msflags
|= IBS_VERTCAPTION
;
166 if ( style
& wxTINY_CAPTION_HORIZ
)
167 msflags
|= IBS_HORZCAPTION
;
169 if ( style
& (wxTINY_CAPTION_VERT
| wxTINY_CAPTION_HORIZ
) )
170 msflags
|= WS_CAPTION
;
175 #if !defined(__WIN16__) && !defined(__SC__)
176 if ( !(GetExtraStyle() & wxTOPLEVEL_EX_DIALOG
) )
178 // make all frames appear in the win9x shell taskbar unless
179 // wxFRAME_TOOL_WINDOW or wxFRAME_NO_TASKBAR is given - without
180 // giving them WS_EX_APPWINDOW style, the child (i.e. owned) frames
181 // wouldn't appear in it
182 if ( (style
& wxFRAME_TOOL_WINDOW
) || (style
& wxFRAME_NO_TASKBAR
) )
183 *exflags
|= WS_EX_TOOLWINDOW
;
184 else if ( !(style
& wxFRAME_NO_TASKBAR
) )
185 *exflags
|= WS_EX_APPWINDOW
;
189 if ( style
& wxSTAY_ON_TOP
)
190 *exflags
|= WS_EX_TOPMOST
;
193 if ( GetExtraStyle() & wxFRAME_EX_CONTEXTHELP
)
194 *exflags
|= WS_EX_CONTEXTHELP
;
201 bool wxTopLevelWindowMSW::CreateDialog(const void *dlgTemplate
,
202 const wxString
& title
,
206 #ifdef __WXMICROWIN__
207 // no dialogs support under MicroWin yet
208 return CreateFrame(title
, pos
, size
);
209 #else // !__WXMICROWIN__
210 wxWindow
*parent
= GetParent();
212 // for the dialogs without wxDIALOG_NO_PARENT style, use the top level
213 // app window as parent - this avoids creating modal dialogs without
215 if ( !parent
&& !(GetWindowStyleFlag() & wxDIALOG_NO_PARENT
) )
217 parent
= wxTheApp
->GetTopWindow();
221 // don't use transient windows as parents, this is dangerous as it
222 // can lead to a crash if the parent is destroyed before the child
224 // also don't use the window which is currently hidden as then the
225 // dialog would be hidden as well
226 if ( (parent
->GetExtraStyle() & wxWS_EX_TRANSIENT
) ||
234 m_hWnd
= (WXHWND
)::CreateDialogIndirect
237 (DLGTEMPLATE
*)dlgTemplate
,
238 parent
? GetHwndOf(parent
) : NULL
,
244 wxFAIL_MSG(_("Failed to create dialog. Incorrect DLGTEMPLATE?"));
246 wxLogSysError(_("Can't create dialog using memory template"));
252 (void)MSWGetCreateWindowFlags(&exflags
);
256 ::SetWindowLong(GetHwnd(), GWL_EXSTYLE
, exflags
);
257 ::SetWindowPos(GetHwnd(), NULL
, 0, 0, 0, 0,
264 #if defined(__WIN95__)
265 // For some reason, the system menu is activated when we use the
266 // WS_EX_CONTEXTHELP style, so let's set a reasonable icon
267 if ( exflags
& WS_EX_CONTEXTHELP
)
269 wxFrame
*winTop
= wxDynamicCast(wxTheApp
->GetTopWindow(), wxFrame
);
272 wxIcon icon
= winTop
->GetIcon();
275 ::SendMessage(GetHwnd(), WM_SETICON
,
277 (LPARAM
)GetHiconOf(icon
));
283 // move the dialog to its initial position without forcing repainting
285 if ( !MSWGetCreateWindowCoords(pos
, size
, x
, y
, w
, h
) )
288 w
= (int)CW_USEDEFAULT
;
291 // we can't use CW_USEDEFAULT here as we're not calling CreateWindow()
292 // and passing CW_USEDEFAULT to MoveWindow() results in resizing the
293 // window to (0, 0) size which breaks quite a lot of things, e.g. the
294 // sizer calculation in wxSizer::Fit()
295 if ( w
== (int)CW_USEDEFAULT
)
297 // the exact number doesn't matter, the dialog will be resized
298 // again soon anyhow but it should be big enough to allow
299 // calculation relying on "totalSize - clientSize > 0" work, i.e.
300 // at least greater than the title bar height
305 if ( x
== (int)CW_USEDEFAULT
)
307 // centre it on the screen - what else can we do?
308 wxSize sizeDpy
= wxGetDisplaySize();
310 x
= (sizeDpy
.x
- w
) / 2;
311 y
= (sizeDpy
.y
- h
) / 2;
314 if ( !::MoveWindow(GetHwnd(), x
, y
, w
, h
, FALSE
) )
316 wxLogLastError(wxT("MoveWindow"));
319 if ( !title
.empty() )
321 ::SetWindowText(GetHwnd(), title
);
327 #endif // __WXMICROWIN__/!__WXMICROWIN__
330 bool wxTopLevelWindowMSW::CreateFrame(const wxString
& title
,
335 WXDWORD flags
= MSWGetCreateWindowFlags(&exflags
);
337 return MSWCreate(wxCanvasClassName
, title
, pos
, size
, flags
, exflags
);
340 bool wxTopLevelWindowMSW::Create(wxWindow
*parent
,
342 const wxString
& title
,
346 const wxString
& name
)
351 m_windowStyle
= style
;
355 m_windowId
= id
== -1 ? NewControlId() : id
;
357 wxTopLevelWindows
.Append(this);
360 parent
->AddChild(this);
362 if ( GetExtraStyle() & wxTOPLEVEL_EX_DIALOG
)
364 // we have different dialog templates to allows creation of dialogs
365 // with & without captions under MSWindows, resizeable or not (but a
366 // resizeable dialog always has caption - otherwise it would look too
369 // we need 3 additional WORDs for dialog menu, class and title (as we
370 // don't use DS_SETFONT we don't need the fourth WORD for the font)
371 static const int dlgsize
= sizeof(DLGTEMPLATE
) + (sizeof(WORD
) * 3);
372 DLGTEMPLATE
*dlgTemplate
= (DLGTEMPLATE
*)malloc(dlgsize
);
373 memset(dlgTemplate
, 0, dlgsize
);
375 // these values are arbitrary, they won't be used normally anyhow
378 dlgTemplate
->cx
= 144;
379 dlgTemplate
->cy
= 75;
381 // reuse the code in MSWGetStyle() but correct the results slightly for
383 dlgTemplate
->style
= MSWGetStyle(style
, NULL
);
385 // all dialogs are popups
386 dlgTemplate
->style
|= WS_POPUP
;
388 // force 3D-look if necessary, it looks impossibly ugly otherwise
389 if ( style
& (wxRESIZE_BORDER
| wxCAPTION
) )
390 dlgTemplate
->style
|= DS_MODALFRAME
;
392 bool ret
= CreateDialog(dlgTemplate
, title
, pos
, size
);
399 return CreateFrame(title
, pos
, size
);
403 wxTopLevelWindowMSW::~wxTopLevelWindowMSW()
405 wxTopLevelWindows
.DeleteObject(this);
407 if ( wxModelessWindows
.Find(this) )
408 wxModelessWindows
.DeleteObject(this);
410 // If this is the last top-level window, exit.
411 if ( wxTheApp
&& (wxTopLevelWindows
.Number() == 0) )
413 wxTheApp
->SetTopWindow(NULL
);
415 if ( wxTheApp
->GetExitOnFrameDelete() )
417 ::PostQuitMessage(0);
422 // ----------------------------------------------------------------------------
423 // wxTopLevelWindowMSW showing
424 // ----------------------------------------------------------------------------
426 void wxTopLevelWindowMSW::DoShowWindow(int nShowCmd
)
428 ::ShowWindow(GetHwnd(), nShowCmd
);
430 m_iconized
= nShowCmd
== SW_MINIMIZE
;
433 bool wxTopLevelWindowMSW::Show(bool show
)
435 // don't use wxWindow version as we want to call DoShowWindow() ourselves
436 if ( !wxWindowBase::Show(show
) )
442 if ( m_maximizeOnShow
)
445 nShowCmd
= SW_MAXIMIZE
;
447 m_maximizeOnShow
= FALSE
;
459 DoShowWindow(nShowCmd
);
463 ::BringWindowToTop(GetHwnd());
465 wxActivateEvent
event(wxEVT_ACTIVATE
, TRUE
, m_windowId
);
466 event
.SetEventObject( this );
467 GetEventHandler()->ProcessEvent(event
);
471 // Try to highlight the correct window (the parent)
474 HWND hWndParent
= GetHwndOf(GetParent());
476 ::BringWindowToTop(hWndParent
);
483 // ----------------------------------------------------------------------------
484 // wxTopLevelWindowMSW maximize/minimize
485 // ----------------------------------------------------------------------------
487 void wxTopLevelWindowMSW::Maximize(bool maximize
)
491 // just maximize it directly
492 DoShowWindow(maximize
? SW_MAXIMIZE
: SW_RESTORE
);
496 // we can't maximize the hidden frame because it shows it as well, so
497 // just remember that we should do it later in this case
498 m_maximizeOnShow
= TRUE
;
502 bool wxTopLevelWindowMSW::IsMaximized() const
504 return ::IsZoomed(GetHwnd()) != 0;
507 void wxTopLevelWindowMSW::Iconize(bool iconize
)
509 DoShowWindow(iconize
? SW_MINIMIZE
: SW_RESTORE
);
512 bool wxTopLevelWindowMSW::IsIconized() const
514 // also update the current state
515 ((wxTopLevelWindowMSW
*)this)->m_iconized
= ::IsIconic(GetHwnd()) != 0;
520 void wxTopLevelWindowMSW::Restore()
522 DoShowWindow(SW_RESTORE
);
525 // ----------------------------------------------------------------------------
526 // wxTopLevelWindowMSW fullscreen
527 // ----------------------------------------------------------------------------
529 bool wxTopLevelWindowMSW::ShowFullScreen(bool show
, long style
)
536 m_fsIsShowing
= TRUE
;
539 // zap the frame borders
541 // save the 'normal' window style
542 m_fsOldWindowStyle
= GetWindowLong((HWND
)GetHWND(), GWL_STYLE
);
544 // save the old position, width & height, maximize state
545 m_fsOldSize
= GetRect();
546 m_fsIsMaximized
= IsMaximized();
548 // decide which window style flags to turn off
549 LONG newStyle
= m_fsOldWindowStyle
;
552 if (style
& wxFULLSCREEN_NOBORDER
)
553 offFlags
|= WS_BORDER
| WS_THICKFRAME
;
554 if (style
& wxFULLSCREEN_NOCAPTION
)
555 offFlags
|= (WS_CAPTION
| WS_SYSMENU
);
557 newStyle
&= (~offFlags
);
559 // change our window style to be compatible with full-screen mode
560 ::SetWindowLong((HWND
)GetHWND(), GWL_STYLE
, newStyle
);
562 // resize to the size of the desktop
565 RECT rect
= wxGetWindowRect(::GetDesktopWindow());
566 width
= rect
.right
- rect
.left
;
567 height
= rect
.bottom
- rect
.top
;
569 SetSize(width
, height
);
571 // now flush the window style cache and actually go full-screen
572 SetWindowPos((HWND
)GetHWND(), HWND_TOP
, 0, 0, width
, height
, SWP_FRAMECHANGED
);
574 wxSizeEvent
event(wxSize(width
, height
), GetId());
575 GetEventHandler()->ProcessEvent(event
);
584 m_fsIsShowing
= FALSE
;
586 Maximize(m_fsIsMaximized
);
587 SetWindowLong((HWND
)GetHWND(),GWL_STYLE
, m_fsOldWindowStyle
);
588 SetWindowPos((HWND
)GetHWND(),HWND_TOP
,m_fsOldSize
.x
, m_fsOldSize
.y
,
589 m_fsOldSize
.width
, m_fsOldSize
.height
, SWP_FRAMECHANGED
);
595 // ----------------------------------------------------------------------------
596 // wxTopLevelWindowMSW misc
597 // ----------------------------------------------------------------------------
599 void wxTopLevelWindowMSW::SetIcon(const wxIcon
& icon
)
601 SetIcons( wxIconBundle( icon
) );
604 void wxTopLevelWindowMSW::SetIcons(const wxIconBundle
& icons
)
606 wxTopLevelWindowBase::SetIcons(icons
);
608 #if defined(__WIN95__) && !defined(__WXMICROWIN__)
609 const wxIcon
& sml
= icons
.GetIcon( wxSize( 16, 16 ) );
610 if( sml
.Ok() && sml
.GetWidth() == 16 && sml
.GetHeight() == 16 )
612 ::SendMessage( GetHwndOf( this ), WM_SETICON
, ICON_SMALL
,
613 (LPARAM
)GetHiconOf(sml
) );
616 const wxIcon
& big
= icons
.GetIcon( wxSize( 32, 32 ) );
617 if( big
.Ok() && big
.GetWidth() == 32 && big
.GetHeight() == 32 )
619 ::SendMessage( GetHwndOf( this ), WM_SETICON
, ICON_BIG
,
620 (LPARAM
)GetHiconOf(big
) );
625 bool wxTopLevelWindowMSW::EnableCloseButton(bool enable
)
627 #ifndef __WXMICROWIN__
628 // get system (a.k.a. window) menu
629 HMENU hmenu
= ::GetSystemMenu(GetHwnd(), FALSE
/* get it */);
632 wxLogLastError(_T("GetSystemMenu"));
637 // enabling/disabling the close item from it also automatically
638 // disables/enables the close title bar button
639 if ( ::EnableMenuItem(hmenu
, SC_CLOSE
,
641 (enable
? MF_ENABLED
: MF_GRAYED
)) == -1 )
643 wxLogLastError(_T("EnableMenuItem(SC_CLOSE)"));
648 // update appearance immediately
649 if ( !::DrawMenuBar(GetHwnd()) )
651 wxLogLastError(_T("DrawMenuBar"));
653 #endif // !__WXMICROWIN__
658 // ----------------------------------------------------------------------------
659 // wxTopLevelWindowMSW message processing
660 // ----------------------------------------------------------------------------
662 long wxTopLevelWindowMSW::HandleNcActivate(bool activate
)
666 Normally, when another top level (whether it is overlapped or popup)
667 window is shown, it is activated and the parent window (i.e. we) loses
668 the activation. This, however, looks very ugly when the child window is
669 a [custom] combobox which we implement using a popup window as surely
670 opening a combobox shouldn't result in deactivating the parent window.
672 So we don't redraw the title bar in this case, even if we still return
673 TRUE to let the change of activation to take place as otherwise the
674 controls inside the popup window wouldn't work properly.
676 if ( !activate
&& wxPopupWindow::FindPopupFor(this) )
680 #endif // wxUSE_POPUPWIN
686 wxTopLevelWindowMSW::MSWWindowProc(WXUINT msg
, WXWPARAM wParam
, WXLPARAM lParam
)
688 if ( msg
== WM_NCACTIVATE
&& HandleNcActivate(wParam
!= 0) )
690 // we processed WM_NCACTIVATE ourselves
694 return wxTopLevelWindowBase::MSWWindowProc(msg
, wParam
, lParam
);