/////////////////////////////////////////////////////////////////////////////// // Name: src/msw/popupwin.cpp // Purpose: implements wxPopupWindow for MSW // Author: Vadim Zeitlin // Modified by: // Created: 08.05.02 // Copyright: (c) 2002 Vadim Zeitlin // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// // ============================================================================ // declarations // ============================================================================ // ---------------------------------------------------------------------------- // headers // ---------------------------------------------------------------------------- // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #if wxUSE_POPUPWIN #ifndef WX_PRECOMP #endif //WX_PRECOMP #include "wx/popupwin.h" #include "wx/msw/private.h" // for GetDesktopWindow() // Set to the popup window currently being shown, if any. // // Note that this global variable is used in src/msw/window.cpp and so must be // extern. wxPopupWindow* wxCurrentPopupWindow = NULL; // ============================================================================ // implementation // ============================================================================ // ---------------------------------------------------------------------------- // wxPopupWindow // ---------------------------------------------------------------------------- bool wxPopupWindow::Create(wxWindow *parent, int flags) { // popup windows are created hidden by default Hide(); m_owner = wxGetTopLevelParent(parent); return wxPopupWindowBase::Create(parent) && wxWindow::Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, flags); } wxPopupWindow::~wxPopupWindow() { // If the popup is destroyed without being hidden first, ensure that we are // not left with a dangling pointer. if ( wxCurrentPopupWindow == this ) wxCurrentPopupWindow = NULL; } WXDWORD wxPopupWindow::MSWGetStyle(long flags, WXDWORD *exstyle) const { // we only honour the border flags, the others don't make sense for us WXDWORD style = wxWindow::MSWGetStyle(flags & wxBORDER_MASK, exstyle); // wxMSW uses 2 rather different implementations of wxPopupWindow // internally, each one with its own limitations, so we allow specifying // wxPU_CONTAINS_CONTROLS flag to select which one is used. The default is // to use a child window of the desktop for the popup, which is compatible // with the previous wxWidgets versions and works well for simple popups, // but many standard controls can't work as children of such a window // because it doesn't accept focus. So an alternative implementation for // the popups that will contain such controls is available, but this one // has problems due to the fact that it does take focus and not only can // (and does) this break existing code, but it also prevents the parent // window from keeping focus while showing the popup, as must be done when // using auto-completion tooltips, for example. So neither implementation // can be used in all cases and you have to explicitly choose your poison. if ( HasFlag(wxPU_CONTAINS_CONTROLS) ) { style &= ~WS_CHILD; style |= WS_POPUP; } if ( exstyle ) { // a popup window floats on top of everything *exstyle |= WS_EX_TOPMOST | WS_EX_TOOLWINDOW; } return style; } WXHWND wxPopupWindow::MSWGetParent() const { if ( HasFlag(wxPU_CONTAINS_CONTROLS) ) { return wxPopupWindowBase::MSWGetParent(); } else { // we must be a child of the desktop to be able to extend beyond the // parent window client area (like the comboboxes drop downs do) return (WXHWND)::GetDesktopWindow(); } } void wxPopupWindow::SetFocus() { // Focusing on a popup window does not work on MSW unless WS_POPUP style // is set. Since this is only the case if the style wxPU_CONTAINS_CONTROLS // is used, we'll handle the focus in that case and otherwise do nothing. if ( HasFlag(wxPU_CONTAINS_CONTROLS) ) { wxPopupWindowBase::SetFocus(); } } bool wxPopupWindow::Show(bool show) { // Note that we're called from the ctor before the window is actually // created to hide the popup initially. This call doesn't really hide the // window, so don't do anything in this case, in particular don't change // wxCurrentPopupWindow value. if ( !GetHwnd() ) return wxPopupWindowBase::Show(show); // It's important to update wxCurrentPopupWindow before showing the window, // to ensure that it's already set by the time the owner gets WM_NCACTIVATE // from inside Show() so that it knows to remain [appearing] active, see // the WM_NCACTIVATE handler in wxWindow::MSWHandleMessage(). if ( show ) { // There could have been a previous popup window which hasn't been // hidden yet. This will happen now, when we show this one, as it will // result in activation loss for the other one, so it's ok to overwrite // the old pointer, even if it's non-NULL. wxCurrentPopupWindow = this; } else { // Only reset the pointer if it points to this window, otherwise we // would lose the correct value in the situation described above. if ( wxCurrentPopupWindow == this ) wxCurrentPopupWindow = NULL; } if ( HasFlag(wxPU_CONTAINS_CONTROLS) ) { return wxPopupWindowBase::Show(show); } else { if ( !wxWindowMSW::Show(show) ) return false; if ( show ) { // raise to top of z order if ( !::SetWindowPos(GetHwnd(), HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) ) { wxLogLastError(wxT("SetWindowPos")); } // and set it as the foreground window so the mouse can be captured ::SetForegroundWindow(GetHwnd()); } return true; } } // ---------------------------------------------------------------------------- // wxPopupTransientWindow // ---------------------------------------------------------------------------- void wxPopupTransientWindow::Popup(wxWindow* focus) { Show(); // We can only set focus when using wxPU_CONTAINS_CONTROLS and then only to // one of our children as setting it to another window would result in an // immediate loss of activation and popup disappearance. if ( HasFlag(wxPU_CONTAINS_CONTROLS) && focus && IsDescendant(focus) ) focus->SetFocus(); } void wxPopupTransientWindow::Dismiss() { Hide(); } void wxPopupTransientWindow::DismissOnDeactivate() { // Hide the window automatically when it loses activation. DismissAndNotify(); // Activation might have gone to a different window or maybe // even a different application, don't let our owner continue // to appear active in this case. wxWindow* const owner = MSWGetOwner(); if ( owner ) { if ( ::GetActiveWindow() != GetHwndOf(owner) ) { ::SendMessage(GetHwndOf(owner), WM_NCACTIVATE, FALSE, 0); } } } void wxPopupTransientWindow::MSWDismissUnfocusedPopup() { // When we use wxPU_CONTAINS_CONTROLS, we can react to the popup // deactivation in MSWHandleMessage(), but if we don't have focus, we don't // get any events ourselves, so we rely on wxWindow to forward them to us. if ( !HasFlag(wxPU_CONTAINS_CONTROLS) ) { // It doesn't seem necessary to use CallAfter() here, as dismissing // this window shouldn't affect the focus, as it never has it anyhow. DismissAndNotify(); } } bool wxPopupTransientWindow::MSWHandleMessage(WXLRESULT *result, WXUINT message, WXWPARAM wParam, WXLPARAM lParam) { switch ( message ) { case WM_ACTIVATE: if ( wParam == WA_INACTIVE ) { // We need to dismiss this window, however doing it directly // from here seems to confuse ::ShowWindow(), which ends up // calling this handler, and may result in losing activation // entirely, so postpone it slightly. // // Also note that the active window hasn't changed yet, so we // postpone calling it until DismissOnDeactivate() is executed. CallAfter(&wxPopupTransientWindow::DismissOnDeactivate); } break; } return wxPopupTransientWindowBase::MSWHandleMessage(result, message, wParam, lParam); } #endif // #if wxUSE_POPUPWIN