mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-28 08:45:32 +01:00
eff7b1aa14
git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@2686 8ced0084-cf51-0410-be5f-012b33b47a6e
605 lines
16 KiB
C++
605 lines
16 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/common/popupcmn.cpp
|
|
// Purpose: implementation of wxPopupTransientWindow
|
|
// Author: Vadim Zeitlin
|
|
// Modified by:
|
|
// Created: 06.01.01
|
|
// RCS-ID: $Id: popupcmn.cpp 56979 2008-11-26 15:31:18Z JMS $
|
|
// Copyright: (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
|
|
// License: wxWindows licence
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_POPUPWIN
|
|
|
|
#include "wx/popupwin.h"
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/combobox.h" // wxComboCtrl
|
|
#include "wx/app.h" // wxPostEvent
|
|
#include "wx/log.h"
|
|
#endif //WX_PRECOMP
|
|
|
|
#include "wx/recguard.h"
|
|
|
|
#ifdef __WXUNIVERSAL__
|
|
#include "wx/univ/renderer.h"
|
|
#include "wx/scrolbar.h"
|
|
#endif // __WXUNIVERSAL__
|
|
|
|
#ifdef __WXGTK__
|
|
#include <gtk/gtk.h>
|
|
#elif defined(__WXMSW__)
|
|
#include "wx/msw/private.h"
|
|
#elif defined(__WXX11__)
|
|
#include "wx/x11/private.h"
|
|
#endif
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxPopupWindow, wxWindow)
|
|
IMPLEMENT_DYNAMIC_CLASS(wxPopupTransientWindow, wxPopupWindow)
|
|
|
|
#if wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
|
|
IMPLEMENT_DYNAMIC_CLASS(wxPopupComboWindow, wxPopupTransientWindow)
|
|
#endif
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// private classes
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// event handlers which we use to intercept events which cause the popup to
|
|
// disappear
|
|
class wxPopupWindowHandler : public wxEvtHandler
|
|
{
|
|
public:
|
|
wxPopupWindowHandler(wxPopupTransientWindow *popup) : m_popup(popup) {}
|
|
|
|
protected:
|
|
// event handlers
|
|
void OnLeftDown(wxMouseEvent& event);
|
|
|
|
private:
|
|
wxPopupTransientWindow *m_popup;
|
|
|
|
DECLARE_EVENT_TABLE()
|
|
DECLARE_NO_COPY_CLASS(wxPopupWindowHandler)
|
|
};
|
|
|
|
class wxPopupFocusHandler : public wxEvtHandler
|
|
{
|
|
public:
|
|
wxPopupFocusHandler(wxPopupTransientWindow *popup) : m_popup(popup) {}
|
|
|
|
protected:
|
|
void OnKillFocus(wxFocusEvent& event);
|
|
void OnKeyDown(wxKeyEvent& event);
|
|
|
|
private:
|
|
wxPopupTransientWindow *m_popup;
|
|
|
|
DECLARE_EVENT_TABLE()
|
|
DECLARE_NO_COPY_CLASS(wxPopupFocusHandler)
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// event tables
|
|
// ----------------------------------------------------------------------------
|
|
|
|
BEGIN_EVENT_TABLE(wxPopupWindowHandler, wxEvtHandler)
|
|
EVT_LEFT_DOWN(wxPopupWindowHandler::OnLeftDown)
|
|
END_EVENT_TABLE()
|
|
|
|
BEGIN_EVENT_TABLE(wxPopupFocusHandler, wxEvtHandler)
|
|
EVT_KILL_FOCUS(wxPopupFocusHandler::OnKillFocus)
|
|
EVT_KEY_DOWN(wxPopupFocusHandler::OnKeyDown)
|
|
END_EVENT_TABLE()
|
|
|
|
BEGIN_EVENT_TABLE(wxPopupTransientWindow, wxPopupWindow)
|
|
#if defined( __WXMSW__ ) || defined( __WXMAC__ )
|
|
EVT_IDLE(wxPopupTransientWindow::OnIdle)
|
|
#endif
|
|
END_EVENT_TABLE()
|
|
|
|
// ============================================================================
|
|
// implementation
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPopupWindowBase
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxPopupWindowBase::~wxPopupWindowBase()
|
|
{
|
|
// this destructor is required for Darwin
|
|
}
|
|
|
|
bool wxPopupWindowBase::Create(wxWindow* WXUNUSED(parent), int WXUNUSED(flags))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void wxPopupWindowBase::Position(const wxPoint& ptOrigin,
|
|
const wxSize& size)
|
|
{
|
|
wxSize sizeScreen = wxGetDisplaySize(),
|
|
sizeSelf = GetSize();
|
|
|
|
// is there enough space to put the popup below the window (where we put it
|
|
// by default)?
|
|
wxCoord y = ptOrigin.y + size.y;
|
|
if ( y + sizeSelf.y > sizeScreen.y )
|
|
{
|
|
// check if there is enough space above
|
|
if ( ptOrigin.y > sizeSelf.y )
|
|
{
|
|
// do position the control above the window
|
|
y -= size.y + sizeSelf.y;
|
|
}
|
|
//else: not enough space below nor above, leave below
|
|
}
|
|
|
|
// now check left/right too
|
|
wxCoord x = ptOrigin.x;
|
|
|
|
if ( wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft )
|
|
{
|
|
// shift the window to the left instead of the right.
|
|
x -= size.x;
|
|
x -= sizeSelf.x; // also shift it by window width.
|
|
}
|
|
else
|
|
x += size.x;
|
|
|
|
|
|
if ( x + sizeSelf.x > sizeScreen.x )
|
|
{
|
|
// check if there is enough space to the left
|
|
if ( ptOrigin.x > sizeSelf.x )
|
|
{
|
|
// do position the control to the left
|
|
x -= size.x + sizeSelf.x;
|
|
}
|
|
//else: not enough space there neither, leave in default position
|
|
}
|
|
|
|
Move(x, y, wxSIZE_NO_ADJUSTMENTS);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPopupTransientWindow
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxPopupTransientWindow::Init()
|
|
{
|
|
m_child =
|
|
m_focus = (wxWindow *)NULL;
|
|
|
|
m_handlerFocus = NULL;
|
|
m_handlerPopup = NULL;
|
|
}
|
|
|
|
wxPopupTransientWindow::wxPopupTransientWindow(wxWindow *parent, int style)
|
|
{
|
|
Init();
|
|
|
|
(void)Create(parent, style);
|
|
}
|
|
|
|
wxPopupTransientWindow::~wxPopupTransientWindow()
|
|
{
|
|
if (m_handlerPopup && m_handlerPopup->GetNextHandler())
|
|
PopHandlers();
|
|
|
|
wxASSERT(!m_handlerFocus || !m_handlerFocus->GetNextHandler());
|
|
wxASSERT(!m_handlerPopup || !m_handlerPopup->GetNextHandler());
|
|
|
|
delete m_handlerFocus;
|
|
delete m_handlerPopup;
|
|
}
|
|
|
|
void wxPopupTransientWindow::PopHandlers()
|
|
{
|
|
if ( m_child )
|
|
{
|
|
if ( !m_child->RemoveEventHandler(m_handlerPopup) )
|
|
{
|
|
// something is very wrong and someone else probably deleted our
|
|
// handler - so don't risk deleting it second time
|
|
m_handlerPopup = NULL;
|
|
}
|
|
if (m_child->HasCapture())
|
|
{
|
|
m_child->ReleaseMouse();
|
|
}
|
|
m_child = NULL;
|
|
}
|
|
|
|
if ( m_focus )
|
|
{
|
|
if ( !m_focus->RemoveEventHandler(m_handlerFocus) )
|
|
{
|
|
// see above
|
|
m_handlerFocus = NULL;
|
|
}
|
|
}
|
|
m_focus = NULL;
|
|
}
|
|
|
|
void wxPopupTransientWindow::Popup(wxWindow *winFocus)
|
|
{
|
|
const wxWindowList& children = GetChildren();
|
|
if ( children.GetCount() )
|
|
{
|
|
m_child = children.GetFirst()->GetData();
|
|
}
|
|
else
|
|
{
|
|
m_child = this;
|
|
}
|
|
|
|
Show();
|
|
|
|
// There is is a problem if these are still in use
|
|
wxASSERT(!m_handlerFocus || !m_handlerFocus->GetNextHandler());
|
|
wxASSERT(!m_handlerPopup || !m_handlerPopup->GetNextHandler());
|
|
|
|
if (!m_handlerPopup)
|
|
m_handlerPopup = new wxPopupWindowHandler(this);
|
|
|
|
m_child->PushEventHandler(m_handlerPopup);
|
|
|
|
#if defined(__WXMSW__)
|
|
// Focusing on child of popup window does not work on MSW unless WS_POPUP
|
|
// style is set. We do not even want to try to set the focus, as it may
|
|
// provoke errors on some Windows versions (Vista and later).
|
|
if ( ::GetWindowLong(GetHwnd(), GWL_STYLE) & WS_POPUP )
|
|
#endif
|
|
{
|
|
m_focus = winFocus ? winFocus : this;
|
|
m_focus->SetFocus();
|
|
}
|
|
|
|
#if defined( __WXMSW__ ) || defined( __WXMAC__ )
|
|
// MSW doesn't allow to set focus to the popup window, but we need to
|
|
// subclass the window which has the focus, and not winFocus passed in or
|
|
// otherwise everything else breaks down
|
|
m_focus = FindFocus();
|
|
#elif defined(__WXGTK__)
|
|
// GTK+ catches the activate events from the popup
|
|
// window, not the focus events from the child window
|
|
m_focus = this;
|
|
#endif
|
|
|
|
if ( m_focus )
|
|
{
|
|
if (!m_handlerFocus)
|
|
m_handlerFocus = new wxPopupFocusHandler(this);
|
|
|
|
m_focus->PushEventHandler(m_handlerFocus);
|
|
}
|
|
}
|
|
|
|
bool wxPopupTransientWindow::Show( bool show )
|
|
{
|
|
#ifdef __WXGTK__
|
|
if (!show)
|
|
{
|
|
gdk_pointer_ungrab( (guint32)GDK_CURRENT_TIME );
|
|
|
|
gtk_grab_remove( m_widget );
|
|
}
|
|
#endif
|
|
|
|
#ifdef __WXX11__
|
|
if (!show)
|
|
{
|
|
XUngrabPointer( wxGlobalDisplay(), CurrentTime );
|
|
}
|
|
#endif
|
|
|
|
#if defined( __WXMSW__ ) || defined( __WMAC__ )
|
|
if (!show && m_child && m_child->HasCapture())
|
|
{
|
|
m_child->ReleaseMouse();
|
|
}
|
|
#endif
|
|
|
|
bool ret = wxPopupWindow::Show( show );
|
|
|
|
#ifdef __WXGTK__
|
|
if (show)
|
|
{
|
|
gtk_grab_add( m_widget );
|
|
|
|
gdk_pointer_grab( m_widget->window, TRUE,
|
|
(GdkEventMask)
|
|
(GDK_BUTTON_PRESS_MASK |
|
|
GDK_BUTTON_RELEASE_MASK |
|
|
GDK_POINTER_MOTION_HINT_MASK |
|
|
GDK_POINTER_MOTION_MASK),
|
|
(GdkWindow *) NULL,
|
|
(GdkCursor *) NULL,
|
|
(guint32)GDK_CURRENT_TIME );
|
|
}
|
|
#endif
|
|
|
|
#ifdef __WXX11__
|
|
if (show)
|
|
{
|
|
Window xwindow = (Window) m_clientWindow;
|
|
|
|
/* int res =*/ XGrabPointer(wxGlobalDisplay(), xwindow,
|
|
True,
|
|
ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask,
|
|
GrabModeAsync,
|
|
GrabModeAsync,
|
|
None,
|
|
None,
|
|
CurrentTime );
|
|
}
|
|
#endif
|
|
|
|
#if defined( __WXMSW__ ) || defined( __WMAC__ )
|
|
if (show && m_child)
|
|
{
|
|
// Assume that the mouse is outside the popup to begin with
|
|
m_child->CaptureMouse();
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
void wxPopupTransientWindow::Dismiss()
|
|
{
|
|
Hide();
|
|
PopHandlers();
|
|
}
|
|
|
|
void wxPopupTransientWindow::DismissAndNotify()
|
|
{
|
|
Dismiss();
|
|
OnDismiss();
|
|
}
|
|
|
|
void wxPopupTransientWindow::OnDismiss()
|
|
{
|
|
// nothing to do here - but it may be interesting for derived class
|
|
}
|
|
|
|
bool wxPopupTransientWindow::ProcessLeftDown(wxMouseEvent& WXUNUSED(event))
|
|
{
|
|
// no special processing here
|
|
return false;
|
|
}
|
|
|
|
#if defined( __WXMSW__ ) || defined( __WXMAC__ )
|
|
void wxPopupTransientWindow::OnIdle(wxIdleEvent& event)
|
|
{
|
|
event.Skip();
|
|
|
|
if (IsShown() && m_child)
|
|
{
|
|
wxPoint pos = ScreenToClient(wxGetMousePosition());
|
|
wxRect rect(GetSize());
|
|
|
|
if ( rect.Contains(pos) )
|
|
{
|
|
if ( m_child->HasCapture() )
|
|
{
|
|
m_child->ReleaseMouse();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !m_child->HasCapture() )
|
|
{
|
|
m_child->CaptureMouse();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // __WXMSW__
|
|
|
|
|
|
#if wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPopupComboWindow
|
|
// ----------------------------------------------------------------------------
|
|
|
|
BEGIN_EVENT_TABLE(wxPopupComboWindow, wxPopupTransientWindow)
|
|
EVT_KEY_DOWN(wxPopupComboWindow::OnKeyDown)
|
|
END_EVENT_TABLE()
|
|
|
|
wxPopupComboWindow::wxPopupComboWindow(wxComboCtrl *parent)
|
|
: wxPopupTransientWindow(parent)
|
|
{
|
|
m_combo = parent;
|
|
}
|
|
|
|
bool wxPopupComboWindow::Create(wxComboCtrl *parent)
|
|
{
|
|
m_combo = parent;
|
|
|
|
return wxPopupWindow::Create(parent);
|
|
}
|
|
|
|
void wxPopupComboWindow::PositionNearCombo()
|
|
{
|
|
// the origin point must be in screen coords
|
|
wxPoint ptOrigin = m_combo->ClientToScreen(wxPoint(0,0));
|
|
|
|
#if 0 //def __WXUNIVERSAL__
|
|
// account for the fact that (0, 0) is not the top left corner of the
|
|
// window: there is also the border
|
|
wxRect rectBorders = m_combo->GetRenderer()->
|
|
GetBorderDimensions(m_combo->GetBorder());
|
|
ptOrigin.x -= rectBorders.x;
|
|
ptOrigin.y -= rectBorders.y;
|
|
#endif // __WXUNIVERSAL__
|
|
|
|
// position below or above the combobox: the width is 0 to put it exactly
|
|
// below us, not to the left or to the right
|
|
Position(ptOrigin, wxSize(0, m_combo->GetSize().y));
|
|
}
|
|
|
|
void wxPopupComboWindow::OnDismiss()
|
|
{
|
|
m_combo->OnPopupDismiss();
|
|
}
|
|
|
|
void wxPopupComboWindow::OnKeyDown(wxKeyEvent& event)
|
|
{
|
|
m_combo->ProcessEvent(event);
|
|
}
|
|
|
|
#endif // wxUSE_COMBOBOX && defined(__WXUNIVERSAL__)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPopupWindowHandler
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxPopupWindowHandler::OnLeftDown(wxMouseEvent& event)
|
|
{
|
|
// let the window have it first (we're the first event handler in the chain
|
|
// of handlers for this window)
|
|
if ( m_popup->ProcessLeftDown(event) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
wxPoint pos = event.GetPosition();
|
|
|
|
// in non-Univ ports the system manages scrollbars for us
|
|
#if defined(__WXUNIVERSAL__) && wxUSE_SCROLLBAR
|
|
// scrollbar on which the click occurred
|
|
wxWindow *sbar = NULL;
|
|
#endif // __WXUNIVERSAL__ && wxUSE_SCROLLBAR
|
|
|
|
wxWindow *win = (wxWindow *)event.GetEventObject();
|
|
|
|
switch ( win->HitTest(pos.x, pos.y) )
|
|
{
|
|
case wxHT_WINDOW_OUTSIDE:
|
|
{
|
|
// do the coords translation now as after DismissAndNotify()
|
|
// m_popup may be destroyed
|
|
wxMouseEvent event2(event);
|
|
|
|
m_popup->ClientToScreen(&event2.m_x, &event2.m_y);
|
|
|
|
// clicking outside a popup dismisses it
|
|
m_popup->DismissAndNotify();
|
|
|
|
// dismissing a tooltip shouldn't waste a click, i.e. you
|
|
// should be able to dismiss it and press the button with the
|
|
// same click, so repost this event to the window beneath us
|
|
wxWindow *winUnder = wxFindWindowAtPoint(event2.GetPosition());
|
|
if ( winUnder )
|
|
{
|
|
// translate the event coords to the ones of the window
|
|
// which is going to get the event
|
|
winUnder->ScreenToClient(&event2.m_x, &event2.m_y);
|
|
|
|
event2.SetEventObject(winUnder);
|
|
wxPostEvent(winUnder, event2);
|
|
}
|
|
}
|
|
break;
|
|
|
|
#if defined(__WXUNIVERSAL__) && wxUSE_SCROLLBAR
|
|
case wxHT_WINDOW_HORZ_SCROLLBAR:
|
|
sbar = win->GetScrollbar(wxHORIZONTAL);
|
|
break;
|
|
|
|
case wxHT_WINDOW_VERT_SCROLLBAR:
|
|
sbar = win->GetScrollbar(wxVERTICAL);
|
|
break;
|
|
#endif // __WXUNIVERSAL__ && wxUSE_SCROLLBAR
|
|
|
|
default:
|
|
// forgot to update the switch after adding a new hit test code?
|
|
wxFAIL_MSG( _T("unexpected HitTest() return value") );
|
|
// fall through
|
|
|
|
case wxHT_WINDOW_CORNER:
|
|
// don't actually know if this one is good for anything, but let it
|
|
// pass just in case
|
|
|
|
case wxHT_WINDOW_INSIDE:
|
|
// let the normal processing take place
|
|
event.Skip();
|
|
break;
|
|
}
|
|
|
|
#if defined(__WXUNIVERSAL__) && wxUSE_SCROLLBAR
|
|
if ( sbar )
|
|
{
|
|
// translate the event coordinates to the scrollbar ones
|
|
pos = sbar->ScreenToClient(win->ClientToScreen(pos));
|
|
|
|
// and give the event to it
|
|
wxMouseEvent event2 = event;
|
|
event2.m_x = pos.x;
|
|
event2.m_y = pos.y;
|
|
|
|
(void)sbar->GetEventHandler()->ProcessEvent(event2);
|
|
}
|
|
#endif // __WXUNIVERSAL__ && wxUSE_SCROLLBAR
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPopupFocusHandler
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxPopupFocusHandler::OnKillFocus(wxFocusEvent& event)
|
|
{
|
|
// when we lose focus we always disappear - unless it goes to the popup (in
|
|
// which case we don't really lose it)
|
|
wxWindow *win = event.GetWindow();
|
|
while ( win )
|
|
{
|
|
if ( win == m_popup )
|
|
return;
|
|
win = win->GetParent();
|
|
}
|
|
|
|
m_popup->DismissAndNotify();
|
|
}
|
|
|
|
void wxPopupFocusHandler::OnKeyDown(wxKeyEvent& event)
|
|
{
|
|
// we can be associated with the popup itself in which case we should avoid
|
|
// infinite recursion
|
|
static int s_inside;
|
|
wxRecursionGuard guard(s_inside);
|
|
if ( guard.IsInside() )
|
|
{
|
|
event.Skip();
|
|
return;
|
|
}
|
|
|
|
// let the window have it first, it might process the keys
|
|
if ( !m_popup->GetEventHandler()->ProcessEvent(event) )
|
|
{
|
|
// by default, dismiss the popup
|
|
m_popup->DismissAndNotify();
|
|
}
|
|
}
|
|
|
|
#endif // wxUSE_POPUPWIN
|