X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/7e25f59e48c3ee93fcb54aa7fca1fd51628c2fac..3fc6e5fa23909c701654001a895f2ba95751e0af:/src/msw/toplevel.cpp diff --git a/src/msw/toplevel.cpp b/src/msw/toplevel.cpp index 83c019347d..1e6fc3de12 100644 --- a/src/msw/toplevel.cpp +++ b/src/msw/toplevel.cpp @@ -35,6 +35,7 @@ #include "wx/log.h" #include "wx/intl.h" #include "wx/frame.h" + #include "wx/containr.h" // wxSetFocusToChild() #endif //WX_PRECOMP #include "wx/msw/private.h" @@ -60,6 +61,11 @@ static inline bool IsZoomed(HWND WXUNUSED(hwnd)) { return FALSE; } #endif // __WXMICROWIN__ +// NB: wxDlgProc must be defined here and not in dialog.cpp because the latter +// is not included by wxUniv build which does need wxDlgProc +LONG APIENTRY _EXPORT +wxDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + // ---------------------------------------------------------------------------- // globals // ---------------------------------------------------------------------------- @@ -70,31 +76,16 @@ wxWindowList wxModelessWindows; // the name of the default wxWindows class extern const wxChar *wxCanvasClassName; +// the hidden parent for wxFRAME_NO_TASKBAR unowned frames +wxWindow *wxTopLevelWindowMSW::ms_hiddenParent = NULL; + // ============================================================================ // wxTopLevelWindowMSW implementation // ============================================================================ -// ---------------------------------------------------------------------------- -// wxDialog helpers -// ---------------------------------------------------------------------------- - -// Dialog window proc -LONG APIENTRY _EXPORT -wxDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch ( message ) - { - case WM_INITDIALOG: - // for this message, returning TRUE tells system to set focus to the - // first control in the dialog box - return TRUE; - - default: - // for all the other ones, FALSE means that we didn't process the - // message - return FALSE; - } -} +BEGIN_EVENT_TABLE(wxTopLevelWindowMSW, wxTopLevelWindowBase) + EVT_ACTIVATE(wxTopLevelWindowMSW::OnActivate) +END_EVENT_TABLE() // ---------------------------------------------------------------------------- // wxTopLevelWindowMSW creation @@ -113,6 +104,8 @@ void wxTopLevelWindowMSW::Init() m_fsOldWindowStyle = 0; m_fsIsMaximized = FALSE; m_fsIsShowing = FALSE; + + m_winLastFocused = (wxWindow *)NULL; } WXDWORD wxTopLevelWindowMSW::MSWGetStyle(long style, WXDWORD *exflags) const @@ -175,14 +168,30 @@ WXDWORD wxTopLevelWindowMSW::MSWGetStyle(long style, WXDWORD *exflags) const #if !defined(__WIN16__) && !defined(__SC__) if ( !(GetExtraStyle() & wxTOPLEVEL_EX_DIALOG) ) { - // make all frames appear in the win9x shell taskbar unless - // wxFRAME_TOOL_WINDOW or wxFRAME_NO_TASKBAR is given - without - // giving them WS_EX_APPWINDOW style, the child (i.e. owned) frames - // wouldn't appear in it - if ( (style & wxFRAME_TOOL_WINDOW) || (style & wxFRAME_NO_TASKBAR) ) + if ( style & wxFRAME_TOOL_WINDOW ) + { + // create the palette-like window *exflags |= WS_EX_TOOLWINDOW; - else if ( !(style & wxFRAME_NO_TASKBAR) ) + } + + // We have to solve 2 different problems here: + // + // 1. frames with wxFRAME_NO_TASKBAR flag shouldn't appear in the + // taskbar even if they don't have a parent + // + // 2. frames without this style should appear in the taskbar even + // if they're owned (Windows only puts non owned windows into + // the taskbar normally) + // + // The second one is solved here by using WS_EX_APPWINDOW flag, the + // first one is dealt with in our MSWGetParent() method + // implementation + if ( !(style & wxFRAME_NO_TASKBAR) && GetParent() ) + { + // need to force the frame to appear in the taskbar *exflags |= WS_EX_APPWINDOW; + } + //else: nothing to do [here] } #endif // !Win16 @@ -198,6 +207,46 @@ WXDWORD wxTopLevelWindowMSW::MSWGetStyle(long style, WXDWORD *exflags) const return msflags; } +WXHWND wxTopLevelWindowMSW::MSWGetParent() const +{ + // for the frames without wxFRAME_FLOAT_ON_PARENT style we should use NULL + // parent HWND or it would be always on top of its parent which is not what + // we usually want (in fact, we only want it for frames with the + // wxFRAME_FLOAT_ON_PARENT flag) + wxWindow *parent; + if ( HasFlag(wxFRAME_FLOAT_ON_PARENT) ) + { + parent = GetParent(); + + // this flag doesn't make sense then and will be ignored + wxASSERT_MSG( parent, + _T("wxFRAME_FLOAT_ON_PARENT but no parent?") ); + } + else // don't float on parent, must not be owned + { + parent = NULL; + } + + // now deal with the 2nd taskbar-related problem (see comments above in + // MSWGetStyle()) + if ( HasFlag(wxFRAME_NO_TASKBAR) && !parent ) + { + if ( !ms_hiddenParent ) + { + ms_hiddenParent = new wxTopLevelWindowMSW(NULL, -1, _T("")); + + // we shouldn't leave it in wxTopLevelWindows or we wouldn't + // terminate the app when the last user-created frame is deleted -- + // see ~wxTopLevelWindowMSW + wxTopLevelWindows.DeleteObject(ms_hiddenParent); + } + + parent = ms_hiddenParent; + } + + return parent ? parent->GetHWND() : WXHWND(NULL); +} + bool wxTopLevelWindowMSW::CreateDialog(const void *dlgTemplate, const wxString& title, const wxPoint& pos, @@ -402,19 +451,39 @@ bool wxTopLevelWindowMSW::Create(wxWindow *parent, wxTopLevelWindowMSW::~wxTopLevelWindowMSW() { - wxTopLevelWindows.DeleteObject(this); + if ( this == ms_hiddenParent ) + { + // stop [infinite] recursion which would otherwise happen when we do + // "delete ms_hiddenParent" below -- and we're not interested in doing + // anything of the rest below for that window because the rest of + // wxWindows doesn't even know about it + return; + } if ( wxModelessWindows.Find(this) ) wxModelessWindows.DeleteObject(this); - // If this is the last top-level window, exit. - if ( wxTheApp && (wxTopLevelWindows.Number() == 0) ) + // after destroying an owned window, Windows activates the next top level + // window in Z order but it may be different from our owner (to reproduce + // this simply Alt-TAB to another application and back before closing the + // owned frame) whereas we always want to yield activation to our parent + if ( HasFlag(wxFRAME_FLOAT_ON_PARENT) ) { - wxTheApp->SetTopWindow(NULL); + wxWindow *parent = GetParent(); + if ( parent ) + { + ::BringWindowToTop(GetHwndOf(parent)); + } + } - if ( wxTheApp->GetExitOnFrameDelete() ) + // if this is the last top-level window, we're going to exit and we should + // delete ms_hiddenParent now to avoid leaking it + if ( IsLastBeforeExit() ) + { + if ( ms_hiddenParent ) { - ::PostQuitMessage(0); + delete ms_hiddenParent; + ms_hiddenParent = NULL; } } } @@ -656,40 +725,74 @@ bool wxTopLevelWindowMSW::EnableCloseButton(bool enable) } // ---------------------------------------------------------------------------- -// wxTopLevelWindowMSW message processing +// wxTopLevelWindow event handling // ---------------------------------------------------------------------------- -long wxTopLevelWindowMSW::HandleNcActivate(bool activate) +// Default activation behaviour - set the focus for the first child +// subwindow found. +void wxTopLevelWindowMSW::OnActivate(wxActivateEvent& event) { -#if wxUSE_POPUPWIN - /* - Normally, when another top level (whether it is overlapped or popup) - window is shown, it is activated and the parent window (i.e. we) loses - the activation. This, however, looks very ugly when the child window is - a [custom] combobox which we implement using a popup window as surely - opening a combobox shouldn't result in deactivating the parent window. - - So we don't redraw the title bar in this case, even if we still return - TRUE to let the change of activation to take place as otherwise the - controls inside the popup window wouldn't work properly. - */ - if ( !activate && wxPopupWindow::FindPopupFor(this) ) + if ( event.GetActive() ) { - return TRUE; + // restore focus to the child which was last focused + wxLogTrace(_T("focus"), _T("wxTLW %08x activated."), m_hWnd); + + wxWindow *parent = m_winLastFocused ? m_winLastFocused->GetParent() + : NULL; + if ( !parent ) + { + parent = this; + } + + wxSetFocusToChild(parent, &m_winLastFocused); } -#endif // wxUSE_POPUPWIN + else // deactivating + { + // remember the last focused child if it is our child + m_winLastFocused = FindFocus(); - return FALSE; + // so we NULL it out if it's a child from some other frame + wxWindow *win = m_winLastFocused; + while ( win ) + { + if ( win->IsTopLevel() ) + { + if ( win != this ) + { + m_winLastFocused = NULL; + } + + break; + } + + win = win->GetParent(); + } + + wxLogTrace(_T("focus"), + _T("wxTLW %08x deactivated, last focused: %08x."), + m_hWnd, + m_winLastFocused ? GetHwndOf(m_winLastFocused) + : NULL); + + event.Skip(); + } } -long -wxTopLevelWindowMSW::MSWWindowProc(WXUINT msg, WXWPARAM wParam, WXLPARAM lParam) +// the DialogProc for all wxWindows dialogs +LONG APIENTRY _EXPORT +wxDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { - if ( msg == WM_NCACTIVATE && HandleNcActivate(wParam != 0) ) + switch ( message ) { - // we processed WM_NCACTIVATE ourselves - return TRUE; - } + case WM_INITDIALOG: + // for this message, returning TRUE tells system to set focus to + // the first control in the dialog box, but as we set the focus + // ourselves, we return FALSE from here as well, so fall through - return wxTopLevelWindowBase::MSWWindowProc(msg, wParam, lParam); + default: + // for all the other ones, FALSE means that we didn't process the + // message + return FALSE; + } } +