X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/7ce6cc9b8eda3c0195ae5233d8a4d35707a88190..4a2d030adfa836f6ada1830c9057170d053bcc64:/src/msw/toplevel.cpp diff --git a/src/msw/toplevel.cpp b/src/msw/toplevel.cpp index 0c5d936882..37b3a3d0a7 100644 --- a/src/msw/toplevel.cpp +++ b/src/msw/toplevel.cpp @@ -4,9 +4,8 @@ // Author: Vadim Zeitlin // Modified by: // Created: 24.09.01 -// RCS-ID: $Id$ // Copyright: (c) 2001 SciTech Software, Inc. (www.scitechsoft.com) -// License: wxWindows licence +// Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// // ============================================================================ @@ -33,25 +32,25 @@ #include "wx/log.h" #include "wx/intl.h" #include "wx/frame.h" + #include "wx/menu.h" #include "wx/containr.h" // wxSetFocusToChild() + #include "wx/module.h" #endif //WX_PRECOMP -#include "wx/module.h" #include "wx/dynlib.h" #include "wx/msw/private.h" #if defined(__WXWINCE__) && !defined(__HANDHELDPC__) - #include - #include - // Standard SDK doesn't have aygshell.dll: see include/wx/msw/wince/libraries.h - #if _WIN32_WCE < 400 || !defined(__WINCE_STANDARDSDK__) - #include - #endif -#include "wx/msw/wince/missing.h" + #include + #include + // Standard SDK doesn't have aygshell.dll: see include/wx/msw/wince/libraries.h + #if _WIN32_WCE < 400 || !defined(__WINCE_STANDARDSDK__) + #include + #endif #endif -#include "wx/msw/missing.h" #include "wx/msw/winundef.h" +#include "wx/msw/missing.h" #include "wx/display.h" @@ -63,6 +62,21 @@ #define ICON_SMALL 0 #endif +// FIXME-VC6: Only VC6 doesn't have this in its standard headers so this +// could be removed once support for it is dropped. +#ifndef WM_UNINITMENUPOPUP + #define WM_UNINITMENUPOPUP 0x0125 +#endif + +// ---------------------------------------------------------------------------- +// globals +// ---------------------------------------------------------------------------- + +#if wxUSE_MENUS || wxUSE_MENUS_NATIVE + extern wxMenu *wxCurrentPopupMenu; +#endif // wxUSE_MENUS || wxUSE_MENUS_NATIVE + + // ---------------------------------------------------------------------------- // stubs for missing functions under MicroWindows // ---------------------------------------------------------------------------- @@ -79,17 +93,6 @@ static inline bool IsZoomed(HWND WXUNUSED(hwnd)) { return false; } LONG APIENTRY _EXPORT wxDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); -// ---------------------------------------------------------------------------- -// globals -// ---------------------------------------------------------------------------- - -// the name of the default wxWidgets class -#ifdef __WXWINCE__ -extern wxChar *wxCanvasClassName; -#else -extern const wxChar *wxCanvasClassName; -#endif - // ---------------------------------------------------------------------------- // wxTLWHiddenParentModule: used to manage the hidden parent window (we need a // module to ensure that the window is always deleted) @@ -140,7 +143,7 @@ void wxTopLevelWindowMSW::Init() m_fsIsMaximized = false; m_fsIsShowing = false; - m_winLastFocused = (wxWindow *)NULL; + m_winLastFocused = NULL; #if defined(__SMARTPHONE__) && defined(__WXWINCE__) m_MenuBarHWND = 0; @@ -153,6 +156,9 @@ void wxTopLevelWindowMSW::Init() m_activateInfo = (void*) info; #endif + + m_menuSystem = NULL; + m_menuDepth = 0; } WXDWORD wxTopLevelWindowMSW::MSWGetStyle(long style, WXDWORD *exflags) const @@ -222,9 +228,11 @@ WXDWORD wxTopLevelWindowMSW::MSWGetStyle(long style, WXDWORD *exflags) const } #ifndef __WXWINCE__ - if ( style & wxSYSTEM_MENU ) + // notice that if wxCLOSE_BOX is specified we need to use WS_SYSMENU too as + // otherwise the close box doesn't appear + if ( style & (wxSYSTEM_MENU | wxCLOSE_BOX) ) msflags |= WS_SYSMENU; -#endif +#endif // !__WXWINCE__ // NB: under CE these 2 styles are not supported currently, we should // call Minimize()/Maximize() "manually" if we want to support them @@ -235,7 +243,7 @@ WXDWORD wxTopLevelWindowMSW::MSWGetStyle(long style, WXDWORD *exflags) const msflags |= WS_MAXIMIZE; // Keep this here because it saves recoding this function in wxTinyFrame - if ( style & (wxTINY_CAPTION_VERT | wxTINY_CAPTION_HORIZ) ) + if ( style & wxTINY_CAPTION ) msflags |= WS_CAPTION; if ( exflags ) @@ -298,7 +306,7 @@ WXHWND wxTopLevelWindowMSW::MSWGetParent() const if ( !parent ) { // this flag doesn't make sense then and will be ignored - wxFAIL_MSG( _T("wxFRAME_FLOAT_ON_PARENT but no parent?") ); + wxFAIL_MSG( wxT("wxFRAME_FLOAT_ON_PARENT but no parent?") ); } else { @@ -331,56 +339,14 @@ bool wxTopLevelWindowMSW::HandleSettingChange(WXWPARAM wParam, WXLPARAM lParam) } #endif -bool wxTopLevelWindowMSW::MSWProcessMessage(WXMSG* pMsg) -{ - // MSW specific feature: if the dialog has only one notebook-like child - // window (actually it could be any window that returns true from its - // HasMultiplePages()), then [Shift-]Ctrl-Tab and Ctrl-PageUp/Down keys - // should iterate over its pages even if the focus is outside of the - // control because this is how the standard MSW properties dialogs behave - if ( pMsg->message == WM_KEYDOWN && wxIsCtrlDown() && - (pMsg->wParam == VK_TAB || - pMsg->wParam == VK_PRIOR || - pMsg->wParam == VK_NEXT) ) - { - // check if we have a unique notebook-like child - wxWindow *bookctrl = NULL; - for ( wxWindowList::const_iterator i = GetChildren().begin(), - end = GetChildren().end(); - i != end; - ++i ) - { - wxWindow * const window = *i; - if ( window->HasMultiplePages() ) - { - if ( bookctrl ) - { - // this is the second book-like control already so don't do - // anything as we don't know which one should have its page - // changed - bookctrl = NULL; - break; - } - - bookctrl = window; - } - } - - if ( bookctrl && bookctrl->wxWindowMSW::MSWProcessMessage(pMsg) ) - return true; - } - - return wxTopLevelWindowBase::MSWProcessMessage(pMsg); -} - WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) { WXLRESULT rc = 0; bool processed = false; -#if defined(__SMARTPHONE__) || defined(__POCKETPC__) switch ( message ) { +#if defined(__SMARTPHONE__) || defined(__POCKETPC__) case WM_ACTIVATE: { SHACTIVATEINFO* info = (SHACTIVATEINFO*) m_activateInfo; @@ -407,8 +373,95 @@ WXLRESULT wxTopLevelWindowMSW::MSWWindowProc(WXUINT message, WXWPARAM wParam, WX } break; } +#endif // __SMARTPHONE__ || __POCKETPC__ + + case WM_SYSCOMMAND: + { + // From MSDN: + // + // ... the four low-order bits of the wParam parameter are + // used internally by the system. To obtain the correct + // result when testing the value of wParam, an application + // must combine the value 0xFFF0 with the wParam value by + // using the bitwise AND operator. + unsigned id = wParam & 0xfff0; + + // Preserve the focus when minimizing/restoring the window: we + // need to do it manually as DefWindowProc() doesn't appear to + // do this for us for some reason (perhaps because we don't use + // WM_NEXTDLGCTL for setting focus?). Moreover, our code in + // OnActivate() doesn't work in this case as we receive the + // deactivation event too late when the window is being + // minimized and the focus is already NULL by then. Similarly, + // we receive the activation event too early and restoring + // focus in it fails because the window is still minimized. So + // we need to do it here. + if ( id == SC_MINIMIZE ) + { + // For minimization, it's simple enough: just save the + // focus as usual. The important thing is that we're not + // minimized yet, so this works correctly. + DoSaveLastFocus(); + } + else if ( id == SC_RESTORE ) + { + // For restoring, it's trickier as DefWindowProc() sets + // focus to the window itself. So run it first and restore + // our saved focus only afterwards. + processed = true; + rc = wxTopLevelWindowBase::MSWWindowProc(message, + wParam, lParam); + + DoRestoreLastFocus(); + } + +#ifndef __WXUNIVERSAL__ + // We need to generate events for the custom items added to the + // system menu if it had been created (and presumably modified). + // As SC_SIZE is the first of the system-defined commands, we + // only do this for the custom commands before it and leave + // SC_SIZE and everything after it to DefWindowProc(). + if ( m_menuSystem && id < SC_SIZE ) + { + if ( m_menuSystem->MSWCommand(0 /* unused anyhow */, id) ) + processed = true; + } +#endif // #ifndef __WXUNIVERSAL__ + } + break; + +#if !defined(__WXMICROWIN__) && !defined(__WXWINCE__) +#if wxUSE_MENUS + case WM_INITMENUPOPUP: + processed = HandleMenuPopup(wxEVT_MENU_OPEN, (WXHMENU)wParam); + break; + + case WM_MENUSELECT: + { + WXWORD item, flags; + WXHMENU hmenu; + UnpackMenuSelect(wParam, lParam, &item, &flags, &hmenu); + + processed = HandleMenuSelect(item, flags, hmenu); + } + break; + + case WM_EXITMENULOOP: + // Under Windows 98 and 2000 and later we're going to get + // WM_UNINITMENUPOPUP which will be used to generate this event + // with more information (notably the menu that was closed) so we + // only need this one under old Windows systems where the newer + // event is never sent. + if ( wxGetWinVersion() < wxWinVersion_98 ) + processed = HandleExitMenuLoop(wParam); + break; + + case WM_UNINITMENUPOPUP: + processed = HandleMenuPopup(wxEVT_MENU_CLOSE, (WXHMENU)wParam); + break; +#endif // wxUSE_MENUS +#endif // !__WXMICROWIN__ } -#endif if ( !processed ) rc = wxTopLevelWindowBase::MSWWindowProc(message, wParam, lParam); @@ -425,29 +478,9 @@ bool wxTopLevelWindowMSW::CreateDialog(const void *dlgTemplate, // no dialogs support under MicroWin yet return CreateFrame(title, pos, size); #else // !__WXMICROWIN__ - wxWindow *parent = GetParent(); - - // for the dialogs without wxDIALOG_NO_PARENT style, use the top level - // app window as parent - this avoids creating modal dialogs without - // parent - if ( !parent && !(GetWindowStyleFlag() & wxDIALOG_NO_PARENT) ) - { - parent = wxTheApp->GetTopWindow(); - - if ( parent ) - { - // don't use transient windows as parents, this is dangerous as it - // can lead to a crash if the parent is destroyed before the child - // - // also don't use the window which is currently hidden as then the - // dialog would be hidden as well - if ( (parent->GetExtraStyle() & wxWS_EX_TRANSIENT) || - !parent->IsShown() ) - { - parent = NULL; - } - } - } + // static cast is valid as we're only ever called for dialogs + wxWindow * const + parent = static_cast(this)->GetParentForModalDialog(); m_hWnd = (WXHWND)::CreateDialogIndirect ( @@ -466,31 +499,16 @@ bool wxTopLevelWindowMSW::CreateDialog(const void *dlgTemplate, return false; } - WXDWORD exflags; - (void)MSWGetCreateWindowFlags(&exflags); - - if ( exflags ) - { - ::SetWindowLong(GetHwnd(), GWL_EXSTYLE, exflags); - ::SetWindowPos(GetHwnd(), - exflags & WS_EX_TOPMOST ? HWND_TOPMOST : 0, - 0, 0, 0, 0, - SWP_NOSIZE | - SWP_NOMOVE | - (exflags & WS_EX_TOPMOST ? 0 : SWP_NOZORDER) | - SWP_NOACTIVATE); - } - #if !defined(__WXWINCE__) // For some reason, the system menu is activated when we use the // WS_EX_CONTEXTHELP style, so let's set a reasonable icon - if ( exflags & WS_EX_CONTEXTHELP ) + if ( HasExtraStyle(wxWS_EX_CONTEXTHELP) ) { wxFrame *winTop = wxDynamicCast(wxTheApp->GetTopWindow(), wxFrame); if ( winTop ) { wxIcon icon = winTop->GetIcon(); - if ( icon.Ok() ) + if ( icon.IsOk() ) { ::SendMessage(GetHwnd(), WM_SETICON, (WPARAM)TRUE, @@ -498,34 +516,35 @@ bool wxTopLevelWindowMSW::CreateDialog(const void *dlgTemplate, } } } -#endif +#endif // !__WXWINCE__ + + if ( !title.empty() ) + { + ::SetWindowText(GetHwnd(), title.t_str()); + } + + SubclassWin(m_hWnd); +#if !defined(__WXWINCE__) || defined(__WINCE_STANDARDSDK__) // move the dialog to its initial position without forcing repainting int x, y, w, h; (void)MSWGetCreateWindowCoords(pos, size, x, y, w, h); if ( x == (int)CW_USEDEFAULT ) { - // centre it on the screen - what else can we do? - wxSize sizeDpy = wxGetDisplaySize(); - - x = (sizeDpy.x - w) / 2; - y = (sizeDpy.y - h) / 2; + // Let the system position the window, just set its size. + ::SetWindowPos(GetHwnd(), 0, + 0, 0, w, h, + SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } - -#if !defined(__WXWINCE__) || defined(__WINCE_STANDARDSDK__) - if ( !::MoveWindow(GetHwnd(), x, y, w, h, FALSE) ) + else // Move the window to the desired location and set its size too. { - wxLogLastError(wxT("MoveWindow")); - } -#endif - - if ( !title.empty() ) - { - ::SetWindowText(GetHwnd(), title); + if ( !::MoveWindow(GetHwnd(), x, y, w, h, FALSE) ) + { + wxLogLastError(wxT("MoveWindow")); + } } - - SubclassWin(m_hWnd); +#endif // !__WXWINCE__ #ifdef __SMARTPHONE__ // Work around title non-display glitch @@ -545,7 +564,13 @@ bool wxTopLevelWindowMSW::CreateFrame(const wxString& title, const wxSize sz = IsAlwaysMaximized() ? wxDefaultSize : size; - return MSWCreate(wxCanvasClassName, title, pos, sz, flags, exflags); +#ifndef __WXWINCE__ + if ( wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft ) + exflags |= WS_EX_LAYOUTRTL; +#endif + + return MSWCreate(MSWGetRegisteredClassName(), + title.t_str(), pos, sz, flags, exflags); } bool wxTopLevelWindowMSW::Create(wxWindow *parent, @@ -556,33 +581,29 @@ bool wxTopLevelWindowMSW::Create(wxWindow *parent, long style, const wxString& name) { - bool ret wxDUMMY_INITIALIZE(false); - - // init our fields - Init(); - wxSize sizeReal = size; if ( !sizeReal.IsFullySpecified() ) { sizeReal.SetDefaults(GetDefaultSize()); } - m_windowStyle = style; - - SetName(name); - - m_windowId = id == wxID_ANY ? NewControlId() : id; - + // notice that we should append this window to wxTopLevelWindows list + // before calling CreateBase() as it behaves differently for TLW and + // non-TLW windows wxTopLevelWindows.Append(this); + bool ret = CreateBase(parent, id, pos, sizeReal, style, name); + if ( !ret ) + return false; + if ( parent ) parent->AddChild(this); if ( GetExtraStyle() & wxTOPLEVEL_EX_DIALOG ) { // we have different dialog templates to allows creation of dialogs - // with & without captions under MSWindows, resizeable or not (but a - // resizeable dialog always has caption - otherwise it would look too + // with & without captions under MSWindows, resizable or not (but a + // resizable dialog always has caption - otherwise it would look too // strange) // we need 3 additional WORDs for dialog menu, class and title (as we @@ -599,12 +620,23 @@ bool wxTopLevelWindowMSW::Create(wxWindow *parent, // reuse the code in MSWGetStyle() but correct the results slightly for // the dialog - dlgTemplate->style = MSWGetStyle(style, NULL); + // + // NB: we need a temporary variable as we can't pass pointer to + // dwExtendedStyle directly, it's not aligned correctly for 64 bit + // architectures + WXDWORD dwExtendedStyle; + dlgTemplate->style = MSWGetStyle(style, &dwExtendedStyle); + dlgTemplate->dwExtendedStyle = dwExtendedStyle; // all dialogs are popups dlgTemplate->style |= WS_POPUP; #ifndef __WXWINCE__ + if ( wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft ) + { + dlgTemplate->dwExtendedStyle |= WS_EX_LAYOUTRTL; + } + // force 3D-look if necessary, it looks impossibly ugly otherwise if ( style & (wxRESIZE_BORDER | wxCAPTION) ) dlgTemplate->style |= DS_MODALFRAME; @@ -652,6 +684,10 @@ bool wxTopLevelWindowMSW::Create(wxWindow *parent, wxTopLevelWindowMSW::~wxTopLevelWindowMSW() { + delete m_menuSystem; + + SendDestroyEvent(); + #if defined(__SMARTPHONE__) || defined(__POCKETPC__) SHACTIVATEINFO* info = (SHACTIVATEINFO*) m_activateInfo; delete info; @@ -680,7 +716,21 @@ void wxTopLevelWindowMSW::DoShowWindow(int nShowCmd) { ::ShowWindow(GetHwnd(), nShowCmd); - m_iconized = nShowCmd == SW_MINIMIZE; + // Hiding the window doesn't change its iconized state. + if ( nShowCmd != SW_HIDE ) + { + // Otherwise restoring, maximizing or showing the window normally also + // makes it not iconized and only minimizing it does make it iconized. + m_iconized = nShowCmd == SW_MINIMIZE; + } +} + +void wxTopLevelWindowMSW::ShowWithoutActivating() +{ + if ( !wxWindowBase::Show(true) ) + return; + + DoShowWindow(SW_SHOWNA); } bool wxTopLevelWindowMSW::Show(bool show) @@ -704,12 +754,33 @@ bool wxTopLevelWindowMSW::Show(bool show) m_maximizeOnShow = false; } + else if ( m_iconized ) + { + // We were iconized while we were hidden, so now we need to show + // the window in iconized state. + nShowCmd = SW_MINIMIZE; + } + else if ( ::IsIconic(GetHwnd()) ) + { + // We were restored while we were hidden, so now we need to show + // the window in its normal state. + // + // As below, don't activate some kinds of windows. + if ( HasFlag(wxFRAME_TOOL_WINDOW) || !IsEnabled() ) + nShowCmd = SW_SHOWNOACTIVATE; + else + nShowCmd = SW_RESTORE; + } else // just show { - if ( GetWindowStyle() & wxFRAME_TOOL_WINDOW ) - nShowCmd = SW_SHOWNA; - else - nShowCmd = SW_SHOW; + // we shouldn't use SW_SHOW which also activates the window for + // tool frames (as they shouldn't steal focus from the main window) + // nor for the currently disabled windows as they would be enabled + // as a side effect + if ( HasFlag(wxFRAME_TOOL_WINDOW) || !IsEnabled() ) + nShowCmd = SW_SHOWNA; + else + nShowCmd = SW_SHOW; } } else // hide @@ -717,6 +788,18 @@ bool wxTopLevelWindowMSW::Show(bool show) nShowCmd = SW_HIDE; } +#if wxUSE_DEFERRED_SIZING + // we only set pending size if we're maximized before being shown, now that + // we're shown we don't need it any more (it is reset in size event handler + // for child windows but we have to do it ourselves for this parent window) + // + // make sure to reset it before actually showing the window as this will + // generate WM_SIZE events and we want to use the correct client size from + // them, not the size returned by WM_NCCALCSIZE in DoGetClientSize() which + // turns out to be wrong for maximized windows (see #11762) + m_pendingSize = wxDefaultSize; +#endif // wxUSE_DEFERRED_SIZING + DoShowWindow(nShowCmd); #if defined(__WXWINCE__) && (_WIN32_WCE >= 400 && !defined(__POCKETPC__) && !defined(__SMARTPHONE__)) @@ -726,14 +809,14 @@ bool wxTopLevelWindowMSW::Show(bool show) frame->GetMenuBar()->AddAdornments(GetWindowStyleFlag()); #endif - // we only set pending size if we're maximized before being shown, now that - // we're shown we don't need it any more (it is reset in size event handler - // for child windows but we have to do it ourselves for this parent window) - m_pendingSize = wxDefaultSize; - return true; } +void wxTopLevelWindowMSW::Raise() +{ + ::SetForegroundWindow(GetHwnd()); +} + // ---------------------------------------------------------------------------- // wxTopLevelWindowMSW maximize/minimize // ---------------------------------------------------------------------------- @@ -751,6 +834,7 @@ void wxTopLevelWindowMSW::Maximize(bool maximize) // so just remember that we should do it later in this case m_maximizeOnShow = maximize; +#if wxUSE_DEFERRED_SIZING // after calling Maximize() the client code expects to get the frame // "real" size and doesn't want to know that, because of implementation // details, the frame isn't really maximized yet but will be only once @@ -768,13 +852,15 @@ void wxTopLevelWindowMSW::Maximize(bool maximize) m_pendingSize = wxGetClientDisplayRect().GetSize(); } //else: can't do anything in this case, we don't have the old size +#endif // wxUSE_DEFERRED_SIZING } } bool wxTopLevelWindowMSW::IsMaximized() const { return IsAlwaysMaximized() || -#if !defined(__SMARTPHONE__) && !defined(__POCKETPC__) +#if !defined(__SMARTPHONE__) && !defined(__POCKETPC__) && !defined(__WINCE_STANDARDSDK__) + (::IsZoomed(GetHwnd()) != 0) || #endif m_maximizeOnShow; @@ -782,7 +868,24 @@ bool wxTopLevelWindowMSW::IsMaximized() const void wxTopLevelWindowMSW::Iconize(bool iconize) { - DoShowWindow(iconize ? SW_MINIMIZE : SW_RESTORE); + if ( iconize == m_iconized ) + { + // Do nothing, in particular don't restore non-iconized windows when + // Iconize(false) is called as this would wrongly un-maximize them. + return; + } + + if ( IsShown() ) + { + // change the window state immediately + DoShowWindow(iconize ? SW_MINIMIZE : SW_RESTORE); + } + else // hidden + { + // iconizing the window shouldn't show it so just update the internal + // state (otherwise it's done by DoShowWindow() itself) + m_iconized = iconize; + } } bool wxTopLevelWindowMSW::IsIconized() const @@ -790,6 +893,9 @@ bool wxTopLevelWindowMSW::IsIconized() const #ifdef __WXWINCE__ return false; #else + if ( !IsShown() ) + return m_iconized; + // don't use m_iconized, it may be briefly out of sync with the real state // as it's only modified when we receive a WM_SIZE and we could be called // from an event handler from one of the messages we receive before it, @@ -803,6 +909,154 @@ void wxTopLevelWindowMSW::Restore() DoShowWindow(SW_RESTORE); } +void wxTopLevelWindowMSW::SetLayoutDirection(wxLayoutDirection dir) +{ + if ( dir == wxLayout_Default ) + dir = wxTheApp->GetLayoutDirection(); + + if ( dir != wxLayout_Default ) + wxTopLevelWindowBase::SetLayoutDirection(dir); +} + +// ---------------------------------------------------------------------------- +// wxTopLevelWindowMSW geometry +// ---------------------------------------------------------------------------- + +#ifndef __WXWINCE__ + +void wxTopLevelWindowMSW::DoGetPosition(int *x, int *y) const +{ + if ( IsIconized() ) + { + WINDOWPLACEMENT wp; + wp.length = sizeof(WINDOWPLACEMENT); + if ( ::GetWindowPlacement(GetHwnd(), &wp) ) + { + RECT& rc = wp.rcNormalPosition; + + // the position returned by GetWindowPlacement() is in workspace + // coordinates except for windows with WS_EX_TOOLWINDOW style + if ( !HasFlag(wxFRAME_TOOL_WINDOW) ) + { + // we must use the correct display for the translation as the + // task bar might be shown on one display but not the other one + int n = wxDisplay::GetFromWindow(this); + wxDisplay dpy(n == wxNOT_FOUND ? 0 : n); + const wxPoint ptOfs = dpy.GetClientArea().GetPosition() - + dpy.GetGeometry().GetPosition(); + + rc.left += ptOfs.x; + rc.top += ptOfs.y; + } + + if ( x ) + *x = rc.left; + if ( y ) + *y = rc.top; + + return; + } + + wxLogLastError(wxT("GetWindowPlacement")); + } + //else: normal case + + wxTopLevelWindowBase::DoGetPosition(x, y); +} + +void wxTopLevelWindowMSW::DoGetSize(int *width, int *height) const +{ + if ( IsIconized() ) + { + WINDOWPLACEMENT wp; + wp.length = sizeof(WINDOWPLACEMENT); + if ( ::GetWindowPlacement(GetHwnd(), &wp) ) + { + const RECT& rc = wp.rcNormalPosition; + + if ( width ) + *width = rc.right - rc.left; + if ( height ) + *height = rc.bottom - rc.top; + + return; + } + + wxLogLastError(wxT("GetWindowPlacement")); + } + //else: normal case + + wxTopLevelWindowBase::DoGetSize(width, height); +} + +#endif // __WXWINCE__ + +void +wxTopLevelWindowMSW::MSWGetCreateWindowCoords(const wxPoint& pos, + const wxSize& size, + int& x, int& y, + int& w, int& h) const +{ + // let the system position the window if no explicit position was specified + if ( pos.x == wxDefaultCoord ) + { + // if x is set to CW_USEDEFAULT, y parameter is ignored anyhow so we + // can just as well set it to CW_USEDEFAULT as well + x = + y = CW_USEDEFAULT; + } + else + { + // OTOH, if x is not set to CW_USEDEFAULT, y shouldn't be set to it + // neither because it is not handled as a special value by Windows then + // and so we have to choose some default value for it, even if a + // completely arbitrary one + static const int DEFAULT_Y = 200; + + x = pos.x; + y = pos.y == wxDefaultCoord ? DEFAULT_Y : pos.y; + } + + if ( size.x == wxDefaultCoord || size.y == wxDefaultCoord ) + { + // We don't use CW_USEDEFAULT here for several reasons: + // + // 1. It results in huge frames on modern screens (1000*800 is not + // uncommon on my 1280*1024 screen) which is way too big for a half + // empty frame of most of wxWidgets samples for example) + // + // 2. It is buggy for frames with wxFRAME_TOOL_WINDOW style for which + // the default is for whatever reason 8*8 which breaks client <-> + // window size calculations (it would be nice if it didn't, but it + // does and the simplest way to fix it seemed to change the broken + // default size anyhow) + // + // 3. There is just no advantage in doing it: with x and y it is + // possible that [future versions of] Windows position the new top + // level window in some smart way which we can't do, but we can + // guess a reasonably good size for a new window just as well + // ourselves + // + // The only exception is for the Windows CE platform where the system + // does know better than we how should the windows be sized +#ifdef _WIN32_WCE + w = + h = CW_USEDEFAULT; +#else // !_WIN32_WCE + wxSize sizeReal = size; + sizeReal.SetDefaults(GetDefaultSize()); + + w = sizeReal.x; + h = sizeReal.y; +#endif // _WIN32_WCE/!_WIN32_WCE + } + else + { + w = size.x; + h = size.y; + } +} + // ---------------------------------------------------------------------------- // wxTopLevelWindowMSW fullscreen // ---------------------------------------------------------------------------- @@ -846,6 +1100,12 @@ bool wxTopLevelWindowMSW::ShowFullScreen(bool show, long style) newStyle &= ~offFlags; + // Full screen windows should logically be popups as they don't have + // decorations (and are definitely not children) and while not using + // this style doesn't seem to make any difference for most windows, it + // breaks wxGLCanvas in some cases, see #15434, so just always use it. + newStyle |= WS_POPUP; + // change our window style to be compatible with full-screen mode ::SetWindowLong(GetHwnd(), GWL_STYLE, newStyle); @@ -898,7 +1158,8 @@ bool wxTopLevelWindowMSW::ShowFullScreen(bool show, long style) // finally send an event allowing the window to relayout itself &c wxSizeEvent event(rect.GetSize(), GetId()); - GetEventHandler()->ProcessEvent(event); + event.SetEventObject(this); + HandleWindowEvent(event); } else // stop showing full screen { @@ -928,30 +1189,36 @@ wxString wxTopLevelWindowMSW::GetTitle() const return GetLabel(); } -void wxTopLevelWindowMSW::SetIcon(const wxIcon& icon) +bool wxTopLevelWindowMSW::DoSelectAndSetIcon(const wxIconBundle& icons, + int smX, + int smY, + int i) { - SetIcons( wxIconBundle( icon ) ); + const wxSize size(::GetSystemMetrics(smX), ::GetSystemMetrics(smY)); + + wxIcon icon = icons.GetIcon(size, wxIconBundle::FALLBACK_NEAREST_LARGER); + + if ( !icon.IsOk() ) + return false; + + ::SendMessage(GetHwnd(), WM_SETICON, i, (LPARAM)GetHiconOf(icon)); + return true; } void wxTopLevelWindowMSW::SetIcons(const wxIconBundle& icons) { wxTopLevelWindowBase::SetIcons(icons); -#if !defined(__WXMICROWIN__) - const wxIcon& sml = icons.GetIcon( wxSize( 16, 16 ) ); - if( sml.Ok() && sml.GetWidth() == 16 && sml.GetHeight() == 16 ) + if ( icons.IsEmpty() ) { - ::SendMessage( GetHwndOf( this ), WM_SETICON, ICON_SMALL, - (LPARAM)GetHiconOf(sml) ); + // FIXME: SetIcons(wxNullIconBundle) should unset existing icons, + // but we currently don't do that + wxASSERT_MSG( m_icons.IsEmpty(), "unsetting icons doesn't work" ); + return; } - const wxIcon& big = icons.GetIcon( wxSize( 32, 32 ) ); - if( big.Ok() && big.GetWidth() == 32 && big.GetHeight() == 32 ) - { - ::SendMessage( GetHwndOf( this ), WM_SETICON, ICON_BIG, - (LPARAM)GetHiconOf(big) ); - } -#endif // !__WXMICROWIN__ + DoSelectAndSetIcon(icons, SM_CXSMICON, SM_CYSMICON, ICON_SMALL); + DoSelectAndSetIcon(icons, SM_CXICON, SM_CYICON, ICON_BIG); } bool wxTopLevelWindowMSW::EnableCloseButton(bool enable) @@ -972,7 +1239,7 @@ bool wxTopLevelWindowMSW::EnableCloseButton(bool enable) MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED)) == -1 ) { - wxLogLastError(_T("EnableMenuItem(SC_CLOSE)")); + wxLogLastError(wxT("EnableMenuItem(SC_CLOSE)")); return false; } @@ -980,7 +1247,7 @@ bool wxTopLevelWindowMSW::EnableCloseButton(bool enable) // update appearance immediately if ( !::DrawMenuBar(GetHwnd()) ) { - wxLogLastError(_T("DrawMenuBar")); + wxLogLastError(wxT("DrawMenuBar")); } #endif #endif // !__WXMICROWIN__ @@ -988,54 +1255,6 @@ bool wxTopLevelWindowMSW::EnableCloseButton(bool enable) return true; } -#ifndef __WXWINCE__ - -bool wxTopLevelWindowMSW::SetShape(const wxRegion& region) -{ - wxCHECK_MSG( HasFlag(wxFRAME_SHAPED), false, - _T("Shaped windows must be created with the wxFRAME_SHAPED style.")); - - // The empty region signifies that the shape should be removed from the - // window. - if ( region.IsEmpty() ) - { - if (::SetWindowRgn(GetHwnd(), NULL, TRUE) == 0) - { - wxLogLastError(_T("SetWindowRgn")); - return false; - } - return true; - } - - // Windows takes ownership of the region, so - // we'll have to make a copy of the region to give to it. - DWORD noBytes = ::GetRegionData(GetHrgnOf(region), 0, NULL); - RGNDATA *rgnData = (RGNDATA*) new char[noBytes]; - ::GetRegionData(GetHrgnOf(region), noBytes, rgnData); - HRGN hrgn = ::ExtCreateRegion(NULL, noBytes, rgnData); - delete[] (char*) rgnData; - - // SetWindowRgn expects the region to be in coordinants - // relative to the window, not the client area. Figure - // out the offset, if any. - RECT rect; - DWORD dwStyle = ::GetWindowLong(GetHwnd(), GWL_STYLE); - DWORD dwExStyle = ::GetWindowLong(GetHwnd(), GWL_EXSTYLE); - ::GetClientRect(GetHwnd(), &rect); - ::AdjustWindowRectEx(&rect, dwStyle, FALSE, dwExStyle); - ::OffsetRgn(hrgn, -rect.left, -rect.top); - - // Now call the shape API with the new region. - if (::SetWindowRgn(GetHwnd(), hrgn, TRUE) == 0) - { - wxLogLastError(_T("SetWindowRgn")); - return false; - } - return true; -} - -#endif // !__WXWINCE__ - void wxTopLevelWindowMSW::RequestUserAttention(int flags) { // check if we can use FlashWindowEx(): unfortunately a simple test for @@ -1043,15 +1262,15 @@ void wxTopLevelWindowMSW::RequestUserAttention(int flags) // provide FlashWindowEx() declaration, so try to detect whether we have // real headers for WINVER 0x0500 by checking for existence of a symbol not // declated in MSVC6 header -#if defined(FLASHW_STOP) && defined(VK_XBUTTON1) +#if defined(FLASHW_STOP) && defined(VK_XBUTTON1) && wxUSE_DYNLIB_CLASS // available in the headers, check if it is supported by the system typedef BOOL (WINAPI *FlashWindowEx_t)(FLASHWINFO *pfwi); - FlashWindowEx_t s_pfnFlashWindowEx = NULL; + static FlashWindowEx_t s_pfnFlashWindowEx = NULL; if ( !s_pfnFlashWindowEx ) { - wxDynamicLibrary dllUser32(_T("user32.dll")); + wxDynamicLibrary dllUser32(wxT("user32.dll")); s_pfnFlashWindowEx = (FlashWindowEx_t) - dllUser32.GetSymbol(_T("FlashWindowEx")); + dllUser32.GetSymbol(wxT("FlashWindowEx")); // we can safely unload user32.dll here, it's going to remain loaded as // long as the program is running anyhow @@ -1085,21 +1304,66 @@ void wxTopLevelWindowMSW::RequestUserAttention(int flags) } } +wxMenu *wxTopLevelWindowMSW::MSWGetSystemMenu() const +{ +#ifndef __WXUNIVERSAL__ + if ( !m_menuSystem ) + { + HMENU hmenu = ::GetSystemMenu(GetHwnd(), FALSE); + if ( !hmenu ) + { + wxLogLastError(wxT("GetSystemMenu()")); + return NULL; + } + + wxTopLevelWindowMSW * const + self = const_cast(this); + + self->m_menuSystem = wxMenu::MSWNewFromHMENU(hmenu); + + // We need to somehow associate this menu with this window to ensure + // that we get events from it. A natural idea would be to pretend that + // it's attached to our menu bar but this wouldn't work if we don't + // have any menu bar which is a common case for applications using + // custom items in the system menu (they mostly do it exactly because + // they don't have any other menus). + // + // So reuse the invoking window pointer instead, this is not exactly + // correct but doesn't seem to have any serious drawbacks. + m_menuSystem->SetInvokingWindow(self); + } +#endif // #ifndef __WXUNIVERSAL__ + + return m_menuSystem; +} + +// ---------------------------------------------------------------------------- +// Transparency support // --------------------------------------------------------------------------- bool wxTopLevelWindowMSW::SetTransparent(wxByte alpha) { +#if wxUSE_DYNLIB_CLASS typedef DWORD (WINAPI *PSETLAYEREDWINDOWATTR)(HWND, DWORD, BYTE, DWORD); - static PSETLAYEREDWINDOWATTR pSetLayeredWindowAttributes = NULL; + static PSETLAYEREDWINDOWATTR + pSetLayeredWindowAttributes = (PSETLAYEREDWINDOWATTR)-1; - if ( pSetLayeredWindowAttributes == NULL ) + if ( pSetLayeredWindowAttributes == (PSETLAYEREDWINDOWATTR)-1 ) { - wxDynamicLibrary dllUser32(_T("user32.dll")); + wxDynamicLibrary dllUser32(wxT("user32.dll")); + + // use RawGetSymbol() and not GetSymbol() to avoid error messages under + // Windows 95: there is nothing the user can do about this anyhow pSetLayeredWindowAttributes = (PSETLAYEREDWINDOWATTR) - dllUser32.GetSymbol(wxT("SetLayeredWindowAttributes")); + dllUser32.RawGetSymbol(wxT("SetLayeredWindowAttributes")); + + // it's ok to destroy dllUser32 here, we link statically to user32.dll + // anyhow so it won't be unloaded } - if ( pSetLayeredWindowAttributes == NULL ) + + if ( !pSetLayeredWindowAttributes ) return false; +#endif // wxUSE_DYNLIB_CLASS LONG exstyle = GetWindowLong(GetHwnd(), GWL_EXSTYLE); @@ -1111,80 +1375,223 @@ bool wxTopLevelWindowMSW::SetTransparent(wxByte alpha) return true; } +#if wxUSE_DYNLIB_CLASS // Otherwise, set the layered style if needed and set the alpha value if ((exstyle & WS_EX_LAYERED) == 0 ) SetWindowLong(GetHwnd(), GWL_EXSTYLE, exstyle | WS_EX_LAYERED); - return pSetLayeredWindowAttributes(GetHwnd(), 0, (BYTE)alpha, LWA_ALPHA) != 0; + if ( pSetLayeredWindowAttributes(GetHwnd(), 0, (BYTE)alpha, LWA_ALPHA) ) + return true; +#endif // wxUSE_DYNLIB_CLASS + + return false; } bool wxTopLevelWindowMSW::CanSetTransparent() { // The API is available on win2k and above - + static int os_type = -1; static int ver_major = -1; if (os_type == -1) os_type = ::wxGetOsVersion(&ver_major); - return (os_type == wxWINDOWS_NT && ver_major >= 5); + return (os_type == wxOS_WINDOWS_NT && ver_major >= 5); +} + +void wxTopLevelWindowMSW::DoFreeze() +{ + // do nothing: freezing toplevel window causes paint and mouse events + // to go through it any TLWs under it, so the best we can do is to freeze + // all children -- and wxWindowBase::Freeze() does that } +void wxTopLevelWindowMSW::DoThaw() +{ + // intentionally empty -- see DoFreeze() +} + + // ---------------------------------------------------------------------------- // wxTopLevelWindow event handling // ---------------------------------------------------------------------------- -// Default activation behaviour - set the focus for the first child -// subwindow found. +void wxTopLevelWindowMSW::DoSaveLastFocus() +{ + if ( m_iconized ) + return; + + // remember the last focused child if it is our child + m_winLastFocused = FindFocus(); + + if ( m_winLastFocused ) + { + // and don't remember it if it's a child from some other frame + if ( wxGetTopLevelParent(m_winLastFocused) != this ) + { + m_winLastFocused = NULL; + } + } +} + +void wxTopLevelWindowMSW::DoRestoreLastFocus() +{ + wxWindow *parent = m_winLastFocused ? m_winLastFocused->GetParent() + : NULL; + if ( !parent ) + { + parent = this; + } + + wxSetFocusToChild(parent, &m_winLastFocused); +} + void wxTopLevelWindowMSW::OnActivate(wxActivateEvent& event) { if ( event.GetActive() ) { + // We get WM_ACTIVATE before being restored from iconized state, so we + // can be still iconized here. In this case, avoid restoring the focus + // as it doesn't work anyhow and we will do when we're really restored. + if ( m_iconized ) + { + event.Skip(); + return; + } + // restore focus to the child which was last focused unless we already // have it - wxLogTrace(_T("focus"), _T("wxTLW %08x activated."), (int) m_hWnd); + wxLogTrace(wxT("focus"), wxT("wxTLW %p activated."), m_hWnd); wxWindow *winFocus = FindFocus(); if ( !winFocus || wxGetTopLevelParent(winFocus) != this ) - { - wxWindow *parent = m_winLastFocused ? m_winLastFocused->GetParent() - : NULL; - if ( !parent ) - { - parent = this; - } - - wxSetFocusToChild(parent, &m_winLastFocused); - } + DoRestoreLastFocus(); } else // deactivating { - // remember the last focused child if it is our child - m_winLastFocused = FindFocus(); + DoSaveLastFocus(); + + wxLogTrace(wxT("focus"), + wxT("wxTLW %p deactivated, last focused: %p."), + m_hWnd, + m_winLastFocused ? GetHwndOf(m_winLastFocused) : NULL); + + event.Skip(); + } +} + +#if wxUSE_MENUS + +bool +wxTopLevelWindowMSW::HandleMenuSelect(WXWORD nItem, WXWORD flags, WXHMENU hMenu) +{ + // Ignore the special messages generated when the menu is closed (this is + // the only case when the flags are set to -1), in particular don't clear + // the help string in the status bar when this happens as it had just been + // restored by the base class code. + if ( !hMenu && flags == 0xffff ) + return false; + + // Unfortunately we also need to ignore another message which is sent after + // closing the currently active submenu of the menu bar by pressing Escape: + // in this case we get WM_UNINITMENUPOPUP, from which we generate + // wxEVT_MENU_CLOSE, and _then_ we get WM_MENUSELECT for the top level menu + // from which we overwrite the help string just restored by OnMenuClose() + // handler in wxFrameBase. To prevent this from happening we discard these + // messages but only in the case it's really the top level menu as we still + // need to clear the help string when a submenu is selected in a menu. + if ( flags == (MF_POPUP | MF_HILITE) && !m_menuDepth ) + return false; + + // sign extend to int from unsigned short we get from Windows + int item = (signed short)nItem; + + // WM_MENUSELECT is generated for both normal items and menus, including + // the top level menus of the menu bar, which can't be represented using + // any valid identifier in wxMenuEvent so use an otherwise unused value for + // them + if ( flags & (MF_POPUP | MF_SEPARATOR) ) + item = wxID_NONE; + + wxMenuEvent event(wxEVT_MENU_HIGHLIGHT, item); + event.SetEventObject(this); - if ( m_winLastFocused ) + if ( HandleWindowEvent(event) ) + return true; + + // by default, i.e. if the event wasn't handled above, clear the status bar + // text when an item which can't have any associated help string in wx API + // is selected + if ( item == wxID_NONE ) + DoGiveHelp(wxEmptyString, true); + + return false; +} + +bool +wxTopLevelWindowMSW::DoSendMenuOpenCloseEvent(wxEventType evtType, wxMenu* menu, bool popup) +{ + // Update the menu depth when dealing with the top level menus. + if ( !popup ) + { + if ( evtType == wxEVT_MENU_OPEN ) + { + m_menuDepth++; + } + else if ( evtType == wxEVT_MENU_CLOSE ) { - // let it know that it doesn't have focus any more - m_winLastFocused->HandleKillFocus((WXHWND)NULL); + wxASSERT_MSG( m_menuDepth > 0, wxS("No open menus?") ); - // and don't remember it if it's a child from some other frame - if ( wxGetTopLevelParent(m_winLastFocused) != this ) - { - m_winLastFocused = NULL; - } + m_menuDepth--; + } + else + { + wxFAIL_MSG( wxS("Unexpected menu event type") ); } + } - wxLogTrace(_T("focus"), - _T("wxTLW %08x deactivated, last focused: %08x."), - (int) m_hWnd, - (int) (m_winLastFocused ? GetHwndOf(m_winLastFocused) - : NULL)); + wxMenuEvent event(evtType, popup ? wxID_ANY : 0, menu); + event.SetEventObject(menu); - event.Skip(); + return HandleWindowEvent(event); +} + +bool wxTopLevelWindowMSW::HandleExitMenuLoop(WXWORD isPopup) +{ + return DoSendMenuOpenCloseEvent(wxEVT_MENU_CLOSE, + isPopup ? wxCurrentPopupMenu : NULL, + isPopup != 0); +} + +bool wxTopLevelWindowMSW::HandleMenuPopup(wxEventType evtType, WXHMENU hMenu) +{ + bool isPopup = false; + wxMenu* menu = NULL; + if ( wxCurrentPopupMenu && wxCurrentPopupMenu->GetHMenu() == hMenu ) + { + menu = wxCurrentPopupMenu; + isPopup = true; + } + else + { + menu = MSWFindMenuFromHMENU(hMenu); } + + + return DoSendMenuOpenCloseEvent(evtType, menu, isPopup); +} + +wxMenu* wxTopLevelWindowMSW::MSWFindMenuFromHMENU(WXHMENU WXUNUSED(hMenu)) +{ + // We don't have any menus at this level. + return NULL; } +#endif // wxUSE_MENUS + + + // the DialogProc for all wxWidgets dialogs LONG APIENTRY _EXPORT wxDlgProc(HWND hDlg, @@ -1251,7 +1658,7 @@ void wxTLWHiddenParentModule::OnExit() { if ( !::DestroyWindow(ms_hwnd) ) { - wxLogLastError(_T("DestroyWindow(hidden TLW parent)")); + wxLogLastError(wxT("DestroyWindow(hidden TLW parent)")); } ms_hwnd = NULL; @@ -1261,7 +1668,7 @@ void wxTLWHiddenParentModule::OnExit() { if ( !::UnregisterClass(ms_className, wxGetInstance()) ) { - wxLogLastError(_T("UnregisterClass(\"wxTLWHiddenParent\")")); + wxLogLastError(wxT("UnregisterClass(\"wxTLWHiddenParent\")")); } ms_className = NULL; @@ -1275,7 +1682,7 @@ HWND wxTLWHiddenParentModule::GetHWND() { if ( !ms_className ) { - static const wxChar *HIDDEN_PARENT_CLASS = _T("wxTLWHiddenParent"); + static const wxChar *HIDDEN_PARENT_CLASS = wxT("wxTLWHiddenParent"); WNDCLASS wndclass; wxZeroMemory(wndclass); @@ -1286,7 +1693,7 @@ HWND wxTLWHiddenParentModule::GetHWND() if ( !::RegisterClass(&wndclass) ) { - wxLogLastError(_T("RegisterClass(\"wxTLWHiddenParent\")")); + wxLogLastError(wxT("RegisterClass(\"wxTLWHiddenParent\")")); } else { @@ -1298,7 +1705,7 @@ HWND wxTLWHiddenParentModule::GetHWND() (HMENU)NULL, wxGetInstance(), NULL); if ( !ms_hwnd ) { - wxLogLastError(_T("CreateWindow(hidden TLW parent)")); + wxLogLastError(wxT("CreateWindow(hidden TLW parent)")); } }