X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/7e25f59e48c3ee93fcb54aa7fca1fd51628c2fac..9045ad9dfc9d205ad645e6d496bec9ccddd91ced:/src/msw/toplevel.cpp?ds=inline diff --git a/src/msw/toplevel.cpp b/src/msw/toplevel.cpp index 83c019347d..4758fd9a52 100644 --- a/src/msw/toplevel.cpp +++ b/src/msw/toplevel.cpp @@ -35,8 +35,11 @@ #include "wx/log.h" #include "wx/intl.h" #include "wx/frame.h" + #include "wx/containr.h" // wxSetFocusToChild() #endif //WX_PRECOMP +#include "wx/module.h" + #include "wx/msw/private.h" #include "wx/popupwin.h" @@ -60,6 +63,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 +78,40 @@ wxWindowList wxModelessWindows; // the name of the default wxWindows class extern const wxChar *wxCanvasClassName; -// ============================================================================ -// wxTopLevelWindowMSW implementation -// ============================================================================ - // ---------------------------------------------------------------------------- -// wxDialog helpers +// wxTLWHiddenParentModule: used to manage the hidden parent window (we need a +// module to ensure that the window is always deleted) // ---------------------------------------------------------------------------- -// Dialog window proc -LONG APIENTRY _EXPORT -wxDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +class wxTLWHiddenParentModule : public wxModule { - 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; +public: + // module init/finalize + virtual bool OnInit(); + virtual void OnExit(); - default: - // for all the other ones, FALSE means that we didn't process the - // message - return FALSE; - } -} + // get the hidden window (creates on demand) + static HWND GetHWND(); + +private: + // the HWND of the hidden parent + static HWND ms_hwnd; + + // the class used to create it + static const wxChar *ms_className; + + DECLARE_DYNAMIC_CLASS(wxTLWHiddenParentModule) +}; + +IMPLEMENT_DYNAMIC_CLASS(wxTLWHiddenParentModule, wxModule) + +// ============================================================================ +// wxTopLevelWindowMSW implementation +// ============================================================================ + +BEGIN_EVENT_TABLE(wxTopLevelWindowMSW, wxTopLevelWindowBase) + EVT_ACTIVATE(wxTopLevelWindowMSW::OnActivate) +END_EVENT_TABLE() // ---------------------------------------------------------------------------- // wxTopLevelWindowMSW creation @@ -113,6 +130,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 +194,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 +233,40 @@ 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) + HWND hwndParent = NULL; + if ( HasFlag(wxFRAME_FLOAT_ON_PARENT) ) + { + const wxWindow *parent = GetParent(); + + if ( !parent ) + { + // this flag doesn't make sense then and will be ignored + wxFAIL_MSG( _T("wxFRAME_FLOAT_ON_PARENT but no parent?") ); + } + else + { + hwndParent = GetHwndOf(parent); + } + } + //else: don't float on parent, must not be owned + + // now deal with the 2nd taskbar-related problem (see comments above in + // MSWGetStyle()) + if ( HasFlag(wxFRAME_NO_TASKBAR) && !hwndParent ) + { + // use hidden parent + hwndParent = wxTLWHiddenParentModule::GetHWND(); + } + + return (WXHWND)hwndParent; +} + bool wxTopLevelWindowMSW::CreateDialog(const void *dlgTemplate, const wxString& title, const wxPoint& pos, @@ -241,9 +310,9 @@ bool wxTopLevelWindowMSW::CreateDialog(const void *dlgTemplate, if ( !m_hWnd ) { - wxFAIL_MSG(_("Failed to create dialog. Incorrect DLGTEMPLATE?")); + wxFAIL_MSG(wxT("Failed to create dialog. Incorrect DLGTEMPLATE?")); - wxLogSysError(_("Can't create dialog using memory template")); + wxLogSysError(wxT("Can't create dialog using memory template")); return FALSE; } @@ -402,19 +471,19 @@ bool wxTopLevelWindowMSW::Create(wxWindow *parent, wxTopLevelWindowMSW::~wxTopLevelWindowMSW() { - wxTopLevelWindows.DeleteObject(this); - 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); - - if ( wxTheApp->GetExitOnFrameDelete() ) + wxWindow *parent = GetParent(); + if ( parent ) { - ::PostQuitMessage(0); + ::BringWindowToTop(GetHwndOf(parent)); } } } @@ -448,7 +517,10 @@ bool wxTopLevelWindowMSW::Show(bool show) } else // just show { - nShowCmd = SW_SHOW; + if ( GetWindowStyle() & wxFRAME_TOOL_WINDOW ) + nShowCmd = SW_SHOWNA; + else + nShowCmd = SW_SHOW; } } else // hide @@ -495,7 +567,7 @@ void wxTopLevelWindowMSW::Maximize(bool maximize) { // we can't maximize the hidden frame because it shows it as well, so // just remember that we should do it later in this case - m_maximizeOnShow = TRUE; + m_maximizeOnShow = maximize; } } @@ -656,40 +728,156 @@ 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."), (int) 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(); + + if ( m_winLastFocused ) + { + // let it know that it doesn't have focus any more + m_winLastFocused->HandleKillFocus((WXHWND)NULL); + } - 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."), + (int) m_hWnd, + (int) (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 + + default: + // for all the other ones, FALSE means that we didn't process the + // message + return FALSE; } +} + +// ============================================================================ +// wxTLWHiddenParentModule implementation +// ============================================================================ + +HWND wxTLWHiddenParentModule::ms_hwnd = NULL; - return wxTopLevelWindowBase::MSWWindowProc(msg, wParam, lParam); +const wxChar *wxTLWHiddenParentModule::ms_className = NULL; + +bool wxTLWHiddenParentModule::OnInit() +{ + ms_hwnd = NULL; + ms_className = NULL; + + return TRUE; } + +void wxTLWHiddenParentModule::OnExit() +{ + if ( ms_hwnd ) + { + if ( !::DestroyWindow(ms_hwnd) ) + { + wxLogLastError(_T("DestroyWindow(hidden TLW parent)")); + } + + ms_hwnd = NULL; + } + + if ( ms_className ) + { + if ( !::UnregisterClass(ms_className, wxGetInstance()) ) + { + wxLogLastError(_T("UnregisterClass(\"wxTLWHiddenParent\")")); + } + + ms_className = NULL; + } +} + +/* static */ +HWND wxTLWHiddenParentModule::GetHWND() +{ + if ( !ms_hwnd ) + { + if ( !ms_className ) + { + static const wxChar *HIDDEN_PARENT_CLASS = _T("wxTLWHiddenParent"); + + WNDCLASS wndclass; + wxZeroMemory(wndclass); + + wndclass.lpfnWndProc = DefWindowProc; + wndclass.hInstance = wxGetInstance(); + wndclass.lpszClassName = HIDDEN_PARENT_CLASS; + + if ( !::RegisterClass(&wndclass) ) + { + wxLogLastError(_T("RegisterClass(\"wxTLWHiddenParent\")")); + } + else + { + ms_className = HIDDEN_PARENT_CLASS; + } + } + + ms_hwnd = ::CreateWindow(ms_className, _T(""), 0, 0, 0, 0, 0, NULL, + (HMENU)NULL, wxGetInstance(), NULL); + if ( !ms_hwnd ) + { + wxLogLastError(_T("CreateWindow(hidden TLW parent)")); + } + } + + return ms_hwnd; +} +