initially on Mac git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@44424 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2310 lines
62 KiB
C++
2310 lines
62 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/common/combocmn.cpp
|
|
// Purpose: wxComboCtrlBase
|
|
// Author: Jaakko Salli
|
|
// Modified by:
|
|
// Created: Apr-30-2006
|
|
// RCS-ID: $Id$
|
|
// Copyright: (c) 2005 Jaakko Salli
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_COMBOCTRL
|
|
|
|
#include "wx/combobox.h"
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/app.h"
|
|
#include "wx/log.h"
|
|
#include "wx/dcclient.h"
|
|
#include "wx/settings.h"
|
|
#include "wx/dialog.h"
|
|
#include "wx/timer.h"
|
|
#endif
|
|
|
|
#include "wx/tooltip.h"
|
|
|
|
#include "wx/combo.h"
|
|
|
|
|
|
|
|
// constants
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#define DEFAULT_DROPBUTTON_WIDTH 19
|
|
|
|
#define BMP_BUTTON_MARGIN 4
|
|
|
|
#define DEFAULT_POPUP_HEIGHT 400
|
|
|
|
#define DEFAULT_TEXT_INDENT 3
|
|
|
|
#define COMBO_MARGIN 2 // spacing right of wxTextCtrl
|
|
|
|
|
|
#if defined(__WXMSW__)
|
|
|
|
#define USE_TRANSIENT_POPUP 1 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
|
|
#define TRANSIENT_POPUPWIN_IS_PERFECT 0 // wxPopupTransientWindow works, its child can have focus, and common
|
|
// native controls work on it like normal.
|
|
#define POPUPWIN_IS_PERFECT 0 // Same, but for non-transient popup window.
|
|
#define TEXTCTRL_TEXT_CENTERED 0 // 1 if text in textctrl is vertically centered
|
|
#define FOCUS_RING 0 // No focus ring on wxMSW
|
|
|
|
//#undef wxUSE_POPUPWIN
|
|
//#define wxUSE_POPUPWIN 0
|
|
|
|
#elif defined(__WXGTK__)
|
|
|
|
// NB: It is not recommended to use wxDialog as popup on wxGTK, because of
|
|
// this bug: If wxDialog is hidden, its position becomes corrupt
|
|
// between hide and next show, but without internal coordinates being
|
|
// reflected (or something like that - atleast commenting out ->Hide()
|
|
// seemed to eliminate the position change).
|
|
|
|
#define USE_TRANSIENT_POPUP 1 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
|
|
#define TRANSIENT_POPUPWIN_IS_PERFECT 0 // wxPopupTransientWindow works, its child can have focus, and common
|
|
// native controls work on it like normal.
|
|
#define POPUPWIN_IS_PERFECT 1 // Same, but for non-transient popup window.
|
|
#define TEXTCTRL_TEXT_CENTERED 1 // 1 if text in textctrl is vertically centered
|
|
#define FOCUS_RING 0 // No focus ring on wxGTK
|
|
|
|
#elif defined(__WXMAC__)
|
|
|
|
#define USE_TRANSIENT_POPUP 0 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
|
|
#define TRANSIENT_POPUPWIN_IS_PERFECT 0 // wxPopupTransientWindow works, its child can have focus, and common
|
|
// native controls work on it like normal.
|
|
#define POPUPWIN_IS_PERFECT 0 // Same, but for non-transient popup window.
|
|
#define TEXTCTRL_TEXT_CENTERED 1 // 1 if text in textctrl is vertically centered
|
|
#define FOCUS_RING 3 // Reserve room for the textctrl's focus ring to display
|
|
|
|
#undef DEFAULT_DROPBUTTON_WIDTH
|
|
#define DEFAULT_DROPBUTTON_WIDTH 22
|
|
#undef COMBO_MARGIN
|
|
#define COMBO_MARGIN FOCUS_RING
|
|
|
|
#else
|
|
|
|
#define USE_TRANSIENT_POPUP 0 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
|
|
#define TRANSIENT_POPUPWIN_IS_PERFECT 0 // wxPopupTransientWindow works, its child can have focus, and common
|
|
// native controls work on it like normal.
|
|
#define POPUPWIN_IS_PERFECT 0 // Same, but for non-transient popup window.
|
|
#define TEXTCTRL_TEXT_CENTERED 1 // 1 if text in textctrl is vertically centered
|
|
#define FOCUS_RING 0
|
|
|
|
#endif
|
|
|
|
|
|
// Popupwin is really only supported on wxMSW (not WINCE) and wxGTK, regardless
|
|
// what the wxUSE_POPUPWIN says.
|
|
// FIXME: Why isn't wxUSE_POPUPWIN reliable any longer? (it was in wxW2.6.2)
|
|
#if (!defined(__WXMSW__) && !defined(__WXGTK__)) || defined(__WXWINCE__)
|
|
#undef wxUSE_POPUPWIN
|
|
#define wxUSE_POPUPWIN 0
|
|
#endif
|
|
|
|
|
|
#if wxUSE_POPUPWIN
|
|
#include "wx/popupwin.h"
|
|
#else
|
|
#undef USE_TRANSIENT_POPUP
|
|
#define USE_TRANSIENT_POPUP 0
|
|
#endif
|
|
|
|
|
|
// Define different types of popup windows
|
|
enum
|
|
{
|
|
POPUPWIN_NONE = 0,
|
|
POPUPWIN_WXPOPUPTRANSIENTWINDOW = 1,
|
|
POPUPWIN_WXPOPUPWINDOW = 2,
|
|
POPUPWIN_WXDIALOG = 3
|
|
};
|
|
|
|
|
|
#if USE_TRANSIENT_POPUP
|
|
// wxPopupTransientWindow is implemented
|
|
|
|
#define wxComboPopupWindowBase wxPopupTransientWindow
|
|
#define PRIMARY_POPUP_TYPE POPUPWIN_WXPOPUPTRANSIENTWINDOW
|
|
#define USES_WXPOPUPTRANSIENTWINDOW 1
|
|
|
|
#if TRANSIENT_POPUPWIN_IS_PERFECT
|
|
//
|
|
#elif POPUPWIN_IS_PERFECT
|
|
#define wxComboPopupWindowBase2 wxPopupWindow
|
|
#define SECONDARY_POPUP_TYPE POPUPWIN_WXPOPUPWINDOW
|
|
#define USES_WXPOPUPWINDOW 1
|
|
#else
|
|
#define wxComboPopupWindowBase2 wxDialog
|
|
#define SECONDARY_POPUP_TYPE POPUPWIN_WXDIALOG
|
|
#define USES_WXDIALOG 1
|
|
#endif
|
|
|
|
#elif wxUSE_POPUPWIN
|
|
// wxPopupWindow (but not wxPopupTransientWindow) is properly implemented
|
|
|
|
#define wxComboPopupWindowBase wxPopupWindow
|
|
#define PRIMARY_POPUP_TYPE POPUPWIN_WXPOPUPWINDOW
|
|
#define USES_WXPOPUPWINDOW 1
|
|
|
|
#if !POPUPWIN_IS_PERFECT
|
|
#define wxComboPopupWindowBase2 wxDialog
|
|
#define SECONDARY_POPUP_TYPE POPUPWIN_WXDIALOG
|
|
#define USES_WXDIALOG 1
|
|
#endif
|
|
|
|
#else
|
|
// wxPopupWindow is not implemented
|
|
|
|
#define wxComboPopupWindowBase wxDialog
|
|
#define PRIMARY_POPUP_TYPE POPUPWIN_WXDIALOG
|
|
#define USES_WXDIALOG 1
|
|
|
|
#endif
|
|
|
|
|
|
#ifndef USES_WXPOPUPTRANSIENTWINDOW
|
|
#define USES_WXPOPUPTRANSIENTWINDOW 0
|
|
#endif
|
|
|
|
#ifndef USES_WXPOPUPWINDOW
|
|
#define USES_WXPOPUPWINDOW 0
|
|
#endif
|
|
|
|
#ifndef USES_WXDIALOG
|
|
#define USES_WXDIALOG 0
|
|
#endif
|
|
|
|
|
|
#if USES_WXPOPUPWINDOW
|
|
#define INSTALL_TOPLEV_HANDLER 1
|
|
#else
|
|
#define INSTALL_TOPLEV_HANDLER 0
|
|
#endif
|
|
|
|
|
|
//
|
|
// ** TODO **
|
|
// * wxComboPopupWindow for external use (ie. replace old wxUniv wxPopupComboWindow)
|
|
//
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxComboFrameEventHandler takes care of hiding the popup when events happen
|
|
// in its top level parent.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if INSTALL_TOPLEV_HANDLER
|
|
|
|
//
|
|
// This will no longer be necessary after wxTransientPopupWindow
|
|
// works well on all platforms.
|
|
//
|
|
|
|
class wxComboFrameEventHandler : public wxEvtHandler
|
|
{
|
|
public:
|
|
wxComboFrameEventHandler( wxComboCtrlBase* pCb );
|
|
virtual ~wxComboFrameEventHandler();
|
|
|
|
void OnPopup();
|
|
|
|
void OnIdle( wxIdleEvent& event );
|
|
void OnMouseEvent( wxMouseEvent& event );
|
|
void OnActivate( wxActivateEvent& event );
|
|
void OnResize( wxSizeEvent& event );
|
|
void OnMove( wxMoveEvent& event );
|
|
void OnMenuEvent( wxMenuEvent& event );
|
|
void OnClose( wxCloseEvent& event );
|
|
|
|
protected:
|
|
wxWindow* m_focusStart;
|
|
wxComboCtrlBase* m_combo;
|
|
|
|
private:
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
BEGIN_EVENT_TABLE(wxComboFrameEventHandler, wxEvtHandler)
|
|
EVT_IDLE(wxComboFrameEventHandler::OnIdle)
|
|
EVT_LEFT_DOWN(wxComboFrameEventHandler::OnMouseEvent)
|
|
EVT_RIGHT_DOWN(wxComboFrameEventHandler::OnMouseEvent)
|
|
EVT_SIZE(wxComboFrameEventHandler::OnResize)
|
|
EVT_MOVE(wxComboFrameEventHandler::OnMove)
|
|
EVT_MENU_HIGHLIGHT(wxID_ANY,wxComboFrameEventHandler::OnMenuEvent)
|
|
EVT_MENU_OPEN(wxComboFrameEventHandler::OnMenuEvent)
|
|
EVT_ACTIVATE(wxComboFrameEventHandler::OnActivate)
|
|
EVT_CLOSE(wxComboFrameEventHandler::OnClose)
|
|
END_EVENT_TABLE()
|
|
|
|
wxComboFrameEventHandler::wxComboFrameEventHandler( wxComboCtrlBase* combo )
|
|
: wxEvtHandler()
|
|
{
|
|
m_combo = combo;
|
|
}
|
|
|
|
wxComboFrameEventHandler::~wxComboFrameEventHandler()
|
|
{
|
|
}
|
|
|
|
void wxComboFrameEventHandler::OnPopup()
|
|
{
|
|
m_focusStart = ::wxWindow::FindFocus();
|
|
}
|
|
|
|
void wxComboFrameEventHandler::OnIdle( wxIdleEvent& event )
|
|
{
|
|
wxWindow* winFocused = ::wxWindow::FindFocus();
|
|
|
|
wxWindow* popup = m_combo->GetPopupControl()->GetControl();
|
|
wxWindow* winpopup = m_combo->GetPopupWindow();
|
|
|
|
if (
|
|
winFocused != m_focusStart &&
|
|
winFocused != popup &&
|
|
winFocused->GetParent() != popup &&
|
|
winFocused != winpopup &&
|
|
winFocused->GetParent() != winpopup &&
|
|
winFocused != m_combo &&
|
|
winFocused != m_combo->GetButton() // GTK (atleast) requires this
|
|
)
|
|
{
|
|
m_combo->HidePopup();
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void wxComboFrameEventHandler::OnMenuEvent( wxMenuEvent& event )
|
|
{
|
|
m_combo->HidePopup();
|
|
event.Skip();
|
|
}
|
|
|
|
void wxComboFrameEventHandler::OnMouseEvent( wxMouseEvent& event )
|
|
{
|
|
m_combo->HidePopup();
|
|
event.Skip();
|
|
}
|
|
|
|
void wxComboFrameEventHandler::OnClose( wxCloseEvent& event )
|
|
{
|
|
m_combo->HidePopup();
|
|
event.Skip();
|
|
}
|
|
|
|
void wxComboFrameEventHandler::OnActivate( wxActivateEvent& event )
|
|
{
|
|
m_combo->HidePopup();
|
|
event.Skip();
|
|
}
|
|
|
|
void wxComboFrameEventHandler::OnResize( wxSizeEvent& event )
|
|
{
|
|
m_combo->HidePopup();
|
|
event.Skip();
|
|
}
|
|
|
|
void wxComboFrameEventHandler::OnMove( wxMoveEvent& event )
|
|
{
|
|
m_combo->HidePopup();
|
|
event.Skip();
|
|
}
|
|
|
|
#endif // INSTALL_TOPLEV_HANDLER
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxComboPopupWindow is, in essence, wxPopupWindow customized for
|
|
// wxComboCtrl.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class wxComboPopupWindow : public wxComboPopupWindowBase
|
|
{
|
|
public:
|
|
|
|
wxComboPopupWindow( wxComboCtrlBase *parent,
|
|
int style )
|
|
#if USES_WXPOPUPWINDOW || USES_WXPOPUPTRANSIENTWINDOW
|
|
: wxComboPopupWindowBase(parent,style)
|
|
#else
|
|
: wxComboPopupWindowBase(parent,
|
|
wxID_ANY,
|
|
wxEmptyString,
|
|
wxPoint(-21,-21),
|
|
wxSize(20,20),
|
|
style)
|
|
#endif
|
|
{
|
|
m_inShow = 0;
|
|
}
|
|
|
|
#if USES_WXPOPUPTRANSIENTWINDOW
|
|
virtual bool Show( bool show );
|
|
virtual bool ProcessLeftDown(wxMouseEvent& event);
|
|
protected:
|
|
virtual void OnDismiss();
|
|
#endif
|
|
|
|
private:
|
|
wxByte m_inShow;
|
|
};
|
|
|
|
|
|
#if USES_WXPOPUPTRANSIENTWINDOW
|
|
bool wxComboPopupWindow::Show( bool show )
|
|
{
|
|
// Guard against recursion
|
|
if ( m_inShow )
|
|
return wxComboPopupWindowBase::Show(show);
|
|
|
|
m_inShow++;
|
|
|
|
wxASSERT( IsKindOf(CLASSINFO(wxPopupTransientWindow)) );
|
|
|
|
wxPopupTransientWindow* ptw = (wxPopupTransientWindow*) this;
|
|
wxComboCtrlBase* combo = (wxComboCtrlBase*) GetParent();
|
|
|
|
if ( show != ptw->IsShown() )
|
|
{
|
|
if ( show )
|
|
ptw->Popup(combo->GetPopupControl()->GetControl());
|
|
else
|
|
ptw->Dismiss();
|
|
}
|
|
|
|
m_inShow--;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxComboPopupWindow::ProcessLeftDown(wxMouseEvent& event)
|
|
{
|
|
return wxPopupTransientWindow::ProcessLeftDown(event);
|
|
}
|
|
|
|
// First thing that happens when a transient popup closes is that this method gets called.
|
|
void wxComboPopupWindow::OnDismiss()
|
|
{
|
|
wxComboCtrlBase* combo = (wxComboCtrlBase*) GetParent();
|
|
wxASSERT_MSG( combo->IsKindOf(CLASSINFO(wxComboCtrlBase)),
|
|
wxT("parent might not be wxComboCtrl, but check IMPLEMENT_DYNAMIC_CLASS(2) macro for correctness") );
|
|
|
|
combo->OnPopupDismiss();
|
|
}
|
|
#endif // USES_WXPOPUPTRANSIENTWINDOW
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxComboPopupWindowEvtHandler does bulk of the custom event handling
|
|
// of a popup window. It is separate so we can have different types
|
|
// of popup windows.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class wxComboPopupWindowEvtHandler : public wxEvtHandler
|
|
{
|
|
public:
|
|
|
|
wxComboPopupWindowEvtHandler( wxComboCtrlBase *parent )
|
|
{
|
|
m_combo = parent;
|
|
}
|
|
|
|
void OnSizeEvent( wxSizeEvent& event );
|
|
void OnKeyEvent(wxKeyEvent& event);
|
|
#if USES_WXDIALOG
|
|
void OnActivate( wxActivateEvent& event );
|
|
#endif
|
|
|
|
private:
|
|
wxComboCtrlBase* m_combo;
|
|
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
|
|
BEGIN_EVENT_TABLE(wxComboPopupWindowEvtHandler, wxEvtHandler)
|
|
EVT_KEY_DOWN(wxComboPopupWindowEvtHandler::OnKeyEvent)
|
|
EVT_KEY_UP(wxComboPopupWindowEvtHandler::OnKeyEvent)
|
|
#if USES_WXDIALOG
|
|
EVT_ACTIVATE(wxComboPopupWindowEvtHandler::OnActivate)
|
|
#endif
|
|
EVT_SIZE(wxComboPopupWindowEvtHandler::OnSizeEvent)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
void wxComboPopupWindowEvtHandler::OnSizeEvent( wxSizeEvent& WXUNUSED(event) )
|
|
{
|
|
// Block the event so that the popup control does not get auto-resized.
|
|
}
|
|
|
|
void wxComboPopupWindowEvtHandler::OnKeyEvent( wxKeyEvent& event )
|
|
{
|
|
// Relay keyboard event to the main child controls
|
|
wxWindowList children = m_combo->GetPopupWindow()->GetChildren();
|
|
wxWindowList::iterator node = children.begin();
|
|
wxWindow* child = (wxWindow*)*node;
|
|
child->AddPendingEvent(event);
|
|
}
|
|
|
|
#if USES_WXDIALOG
|
|
void wxComboPopupWindowEvtHandler::OnActivate( wxActivateEvent& event )
|
|
{
|
|
if ( !event.GetActive() )
|
|
{
|
|
// Tell combo control that we are dismissed.
|
|
m_combo->HidePopup();
|
|
|
|
event.Skip();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxComboPopup
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxComboPopup::~wxComboPopup()
|
|
{
|
|
}
|
|
|
|
void wxComboPopup::OnPopup()
|
|
{
|
|
}
|
|
|
|
void wxComboPopup::OnDismiss()
|
|
{
|
|
}
|
|
|
|
wxSize wxComboPopup::GetAdjustedSize( int minWidth,
|
|
int prefHeight,
|
|
int WXUNUSED(maxHeight) )
|
|
{
|
|
return wxSize(minWidth,prefHeight);
|
|
}
|
|
|
|
void wxComboPopup::DefaultPaintComboControl( wxComboCtrlBase* combo,
|
|
wxDC& dc, const wxRect& rect )
|
|
{
|
|
if ( combo->GetWindowStyle() & wxCB_READONLY ) // ie. no textctrl
|
|
{
|
|
combo->PrepareBackground(dc,rect,0);
|
|
|
|
dc.DrawText( combo->GetValue(),
|
|
rect.x + combo->GetTextIndent(),
|
|
(rect.height-dc.GetCharHeight())/2 + rect.y );
|
|
}
|
|
}
|
|
|
|
void wxComboPopup::PaintComboControl( wxDC& dc, const wxRect& rect )
|
|
{
|
|
DefaultPaintComboControl(m_combo,dc,rect);
|
|
}
|
|
|
|
void wxComboPopup::OnComboKeyEvent( wxKeyEvent& event )
|
|
{
|
|
event.Skip();
|
|
}
|
|
|
|
void wxComboPopup::OnComboDoubleClick()
|
|
{
|
|
}
|
|
|
|
void wxComboPopup::SetStringValue( const wxString& WXUNUSED(value) )
|
|
{
|
|
}
|
|
|
|
bool wxComboPopup::LazyCreate()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void wxComboPopup::Dismiss()
|
|
{
|
|
m_combo->HidePopup();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// input handling
|
|
// ----------------------------------------------------------------------------
|
|
|
|
//
|
|
// This is pushed to the event handler queue of the child textctrl.
|
|
//
|
|
class wxComboBoxExtraInputHandler : public wxEvtHandler
|
|
{
|
|
public:
|
|
|
|
wxComboBoxExtraInputHandler( wxComboCtrlBase* combo )
|
|
: wxEvtHandler()
|
|
{
|
|
m_combo = combo;
|
|
}
|
|
virtual ~wxComboBoxExtraInputHandler() { }
|
|
void OnKey(wxKeyEvent& event);
|
|
void OnFocus(wxFocusEvent& event);
|
|
|
|
protected:
|
|
wxComboCtrlBase* m_combo;
|
|
|
|
private:
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
|
|
BEGIN_EVENT_TABLE(wxComboBoxExtraInputHandler, wxEvtHandler)
|
|
EVT_KEY_DOWN(wxComboBoxExtraInputHandler::OnKey)
|
|
EVT_SET_FOCUS(wxComboBoxExtraInputHandler::OnFocus)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
void wxComboBoxExtraInputHandler::OnKey(wxKeyEvent& event)
|
|
{
|
|
// Let the wxComboCtrl event handler have a go first.
|
|
wxComboCtrlBase* combo = m_combo;
|
|
wxObject* prevObj = event.GetEventObject();
|
|
|
|
event.SetId(combo->GetId());
|
|
event.SetEventObject(combo);
|
|
combo->GetEventHandler()->ProcessEvent(event);
|
|
|
|
event.SetId(((wxWindow*)prevObj)->GetId());
|
|
event.SetEventObject(prevObj);
|
|
}
|
|
|
|
void wxComboBoxExtraInputHandler::OnFocus(wxFocusEvent& event)
|
|
{
|
|
// FIXME: This code does run when control is clicked,
|
|
// yet on Windows it doesn't select all the text.
|
|
if ( !(m_combo->GetInternalFlags() & wxCC_NO_TEXT_AUTO_SELECT) )
|
|
{
|
|
if ( m_combo->GetTextCtrl() )
|
|
m_combo->GetTextCtrl()->SelectAll();
|
|
else
|
|
m_combo->SetSelection(-1,-1);
|
|
}
|
|
|
|
// Send focus indication to parent.
|
|
// NB: This is needed for cases where the textctrl gets focus
|
|
// instead of its parent. While this may trigger multiple
|
|
// wxEVT_SET_FOCUSes (since m_text->SetFocus is called
|
|
// from combo's focus event handler), they should be quite
|
|
// harmless.
|
|
wxFocusEvent evt2(wxEVT_SET_FOCUS,m_combo->GetId());
|
|
evt2.SetEventObject(m_combo);
|
|
m_combo->GetEventHandler()->ProcessEvent(evt2);
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
|
|
//
|
|
// This is pushed to the event handler queue of the control in popup.
|
|
//
|
|
|
|
class wxComboPopupExtraEventHandler : public wxEvtHandler
|
|
{
|
|
public:
|
|
|
|
wxComboPopupExtraEventHandler( wxComboCtrlBase* combo )
|
|
: wxEvtHandler()
|
|
{
|
|
m_combo = combo;
|
|
m_beenInside = false;
|
|
}
|
|
virtual ~wxComboPopupExtraEventHandler() { }
|
|
|
|
void OnMouseEvent( wxMouseEvent& event );
|
|
|
|
// Called from wxComboCtrlBase::OnPopupDismiss
|
|
void OnPopupDismiss()
|
|
{
|
|
m_beenInside = false;
|
|
}
|
|
|
|
protected:
|
|
wxComboCtrlBase* m_combo;
|
|
|
|
bool m_beenInside;
|
|
|
|
private:
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
|
|
BEGIN_EVENT_TABLE(wxComboPopupExtraEventHandler, wxEvtHandler)
|
|
EVT_MOUSE_EVENTS(wxComboPopupExtraEventHandler::OnMouseEvent)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
void wxComboPopupExtraEventHandler::OnMouseEvent( wxMouseEvent& event )
|
|
{
|
|
wxPoint pt = event.GetPosition();
|
|
wxSize sz = m_combo->GetPopupControl()->GetControl()->GetClientSize();
|
|
int evtType = event.GetEventType();
|
|
bool isInside = pt.x >= 0 && pt.y >= 0 && pt.x < sz.x && pt.y < sz.y;
|
|
|
|
if ( evtType == wxEVT_MOTION ||
|
|
evtType == wxEVT_LEFT_DOWN ||
|
|
evtType == wxEVT_RIGHT_DOWN )
|
|
{
|
|
// Block motion and click events outside the popup
|
|
if ( !isInside || !m_combo->IsPopupShown() )
|
|
{
|
|
event.Skip(false);
|
|
return;
|
|
}
|
|
}
|
|
else if ( evtType == wxEVT_LEFT_UP )
|
|
{
|
|
if ( !m_combo->IsPopupShown() )
|
|
{
|
|
event.Skip(false);
|
|
return;
|
|
}
|
|
|
|
if ( !m_beenInside )
|
|
{
|
|
if ( isInside )
|
|
{
|
|
m_beenInside = true;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Some mouse events to popup that happen outside it, before cursor
|
|
// has been inside the popu, need to be ignored by it but relayed to
|
|
// the dropbutton.
|
|
//
|
|
wxWindow* btn = m_combo->GetButton();
|
|
if ( btn )
|
|
btn->GetEventHandler()->AddPendingEvent(event);
|
|
else
|
|
m_combo->GetEventHandler()->AddPendingEvent(event);
|
|
|
|
return;
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxComboCtrlBase
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
BEGIN_EVENT_TABLE(wxComboCtrlBase, wxControl)
|
|
EVT_TEXT(wxID_ANY,wxComboCtrlBase::OnTextCtrlEvent)
|
|
EVT_SIZE(wxComboCtrlBase::OnSizeEvent)
|
|
EVT_SET_FOCUS(wxComboCtrlBase::OnFocusEvent)
|
|
EVT_KILL_FOCUS(wxComboCtrlBase::OnFocusEvent)
|
|
EVT_IDLE(wxComboCtrlBase::OnIdleEvent)
|
|
//EVT_BUTTON(wxID_ANY,wxComboCtrlBase::OnButtonClickEvent)
|
|
EVT_KEY_DOWN(wxComboCtrlBase::OnKeyEvent)
|
|
EVT_TEXT_ENTER(wxID_ANY,wxComboCtrlBase::OnTextCtrlEvent)
|
|
EVT_SYS_COLOUR_CHANGED(wxComboCtrlBase::OnSysColourChanged)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
IMPLEMENT_ABSTRACT_CLASS(wxComboCtrlBase, wxControl)
|
|
|
|
void wxComboCtrlBase::Init()
|
|
{
|
|
m_winPopup = (wxWindow *)NULL;
|
|
m_popup = (wxWindow *)NULL;
|
|
m_popupWinState = Hidden;
|
|
m_btn = (wxWindow*) NULL;
|
|
m_text = (wxTextCtrl*) NULL;
|
|
m_popupInterface = (wxComboPopup*) NULL;
|
|
|
|
m_popupExtraHandler = (wxEvtHandler*) NULL;
|
|
m_textEvtHandler = (wxEvtHandler*) NULL;
|
|
|
|
#if INSTALL_TOPLEV_HANDLER
|
|
m_toplevEvtHandler = (wxEvtHandler*) NULL;
|
|
#endif
|
|
|
|
m_mainCtrlWnd = this;
|
|
|
|
m_heightPopup = -1;
|
|
m_widthMinPopup = -1;
|
|
m_anchorSide = 0;
|
|
m_widthCustomPaint = 0;
|
|
m_widthCustomBorder = 0;
|
|
|
|
m_btnState = 0;
|
|
m_btnWidDefault = 0;
|
|
m_blankButtonBg = false;
|
|
m_ignoreEvtText = 0;
|
|
m_popupWinType = POPUPWIN_NONE;
|
|
m_btnWid = m_btnHei = -1;
|
|
m_btnSide = wxRIGHT;
|
|
m_btnSpacingX = 0;
|
|
|
|
m_extLeft = 0;
|
|
m_extRight = 0;
|
|
m_absIndent = -1;
|
|
m_iFlags = 0;
|
|
m_timeCanAcceptClick = 0;
|
|
|
|
m_resetFocus = false;
|
|
}
|
|
|
|
bool wxComboCtrlBase::Create(wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxString& value,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxValidator& validator,
|
|
const wxString& name)
|
|
{
|
|
if ( !wxControl::Create(parent,
|
|
id,
|
|
pos,
|
|
size,
|
|
style | wxWANTS_CHARS,
|
|
validator,
|
|
name) )
|
|
return false;
|
|
|
|
m_valueString = value;
|
|
|
|
// Get colours
|
|
OnThemeChange();
|
|
m_absIndent = GetNativeTextIndent();
|
|
|
|
m_iFlags |= wxCC_IFLAG_CREATED;
|
|
|
|
// If x and y indicate valid size, wxSizeEvent won't be
|
|
// emitted automatically, so we need to add artifical one.
|
|
if ( size.x > 0 && size.y > 0 )
|
|
{
|
|
wxSizeEvent evt(size,GetId());
|
|
GetEventHandler()->AddPendingEvent(evt);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void wxComboCtrlBase::InstallInputHandlers()
|
|
{
|
|
if ( m_text )
|
|
{
|
|
m_textEvtHandler = new wxComboBoxExtraInputHandler(this);
|
|
m_text->PushEventHandler(m_textEvtHandler);
|
|
}
|
|
}
|
|
|
|
void
|
|
wxComboCtrlBase::CreateTextCtrl(int style, const wxValidator& validator)
|
|
{
|
|
if ( !(m_windowStyle & wxCB_READONLY) )
|
|
{
|
|
if ( m_text )
|
|
m_text->Destroy();
|
|
|
|
// wxTE_PROCESS_TAB is needed because on Windows, wxTAB_TRAVERSAL is
|
|
// not used by the wxPropertyGrid and therefore the tab is processed by
|
|
// looking at ancestors to see if they have wxTAB_TRAVERSAL. The
|
|
// navigation event is then sent to the wrong window.
|
|
style |= wxTE_PROCESS_TAB;
|
|
|
|
if ( HasFlag(wxTE_PROCESS_ENTER) )
|
|
style |= wxTE_PROCESS_ENTER;
|
|
|
|
// Ignore EVT_TEXT generated by the constructor (but only
|
|
// if the event redirector already exists)
|
|
// NB: This must be " = 1" instead of "++";
|
|
if ( m_textEvtHandler )
|
|
m_ignoreEvtText = 1;
|
|
else
|
|
m_ignoreEvtText = 0;
|
|
|
|
m_text = new wxTextCtrl(this, wxID_ANY, m_valueString,
|
|
wxDefaultPosition, wxSize(10,-1),
|
|
style, validator);
|
|
}
|
|
}
|
|
|
|
void wxComboCtrlBase::OnThemeChange()
|
|
{
|
|
// Leave the default bg on the Mac so the area used by the focus ring will
|
|
// be the correct colour and themed brush. Instead we'll use
|
|
// wxSYS_COLOUR_WINDOW in the EVT_PAINT handler as needed.
|
|
#ifndef __WXMAC__
|
|
SetOwnBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
|
#endif
|
|
}
|
|
|
|
wxComboCtrlBase::~wxComboCtrlBase()
|
|
{
|
|
if ( HasCapture() )
|
|
ReleaseMouse();
|
|
|
|
#if INSTALL_TOPLEV_HANDLER
|
|
delete ((wxComboFrameEventHandler*)m_toplevEvtHandler);
|
|
m_toplevEvtHandler = (wxEvtHandler*) NULL;
|
|
#endif
|
|
|
|
DestroyPopup();
|
|
|
|
if ( m_text )
|
|
m_text->RemoveEventHandler(m_textEvtHandler);
|
|
|
|
delete m_textEvtHandler;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// geometry stuff
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Recalculates button and textctrl areas
|
|
void wxComboCtrlBase::CalculateAreas( int btnWidth )
|
|
{
|
|
wxSize sz = GetClientSize();
|
|
int customBorder = m_widthCustomBorder;
|
|
int btnBorder; // border for button only
|
|
|
|
// check if button should really be outside the border: we'll do it it if
|
|
// its platform default or bitmap+pushbutton background is used, but not if
|
|
// there is vertical size adjustment or horizontal spacing.
|
|
if ( ( (m_iFlags & wxCC_BUTTON_OUTSIDE_BORDER) ||
|
|
(m_bmpNormal.Ok() && m_blankButtonBg) ) &&
|
|
m_btnSpacingX == 0 &&
|
|
m_btnHei <= 0 )
|
|
{
|
|
m_iFlags |= wxCC_IFLAG_BUTTON_OUTSIDE;
|
|
btnBorder = 0;
|
|
}
|
|
else
|
|
{
|
|
m_iFlags &= ~(wxCC_IFLAG_BUTTON_OUTSIDE);
|
|
btnBorder = customBorder;
|
|
}
|
|
|
|
// Defaul indentation
|
|
if ( m_absIndent < 0 )
|
|
m_absIndent = GetNativeTextIndent();
|
|
|
|
int butWidth = btnWidth;
|
|
|
|
if ( butWidth <= 0 )
|
|
butWidth = m_btnWidDefault;
|
|
else
|
|
m_btnWidDefault = butWidth;
|
|
|
|
if ( butWidth <= 0 )
|
|
return;
|
|
|
|
int butHeight = sz.y - btnBorder*2;
|
|
|
|
// Adjust button width
|
|
if ( m_btnWid > 0 )
|
|
butWidth = m_btnWid;
|
|
else
|
|
{
|
|
// Adjust button width to match aspect ratio
|
|
// (but only if control is smaller than best size).
|
|
int bestHeight = GetBestSize().y;
|
|
int height = GetSize().y;
|
|
|
|
if ( height < bestHeight )
|
|
{
|
|
// Make very small buttons square, as it makes
|
|
// them accommodate arrow image better and still
|
|
// looks decent.
|
|
if ( height > 18 )
|
|
butWidth = (height*butWidth)/bestHeight;
|
|
else
|
|
butWidth = butHeight;
|
|
}
|
|
}
|
|
|
|
// Adjust button height
|
|
if ( m_btnHei > 0 )
|
|
butHeight = m_btnHei;
|
|
|
|
// Use size of normal bitmap if...
|
|
// It is larger
|
|
// OR
|
|
// button width is set to default and blank button bg is not drawn
|
|
if ( m_bmpNormal.Ok() )
|
|
{
|
|
int bmpReqWidth = m_bmpNormal.GetWidth();
|
|
int bmpReqHeight = m_bmpNormal.GetHeight();
|
|
|
|
// If drawing blank button background, we need to add some margin.
|
|
if ( m_blankButtonBg )
|
|
{
|
|
bmpReqWidth += BMP_BUTTON_MARGIN*2;
|
|
bmpReqHeight += BMP_BUTTON_MARGIN*2;
|
|
}
|
|
|
|
if ( butWidth < bmpReqWidth || ( m_btnWid == 0 && !m_blankButtonBg ) )
|
|
butWidth = bmpReqWidth;
|
|
if ( butHeight < bmpReqHeight || ( m_btnHei == 0 && !m_blankButtonBg ) )
|
|
butHeight = bmpReqHeight;
|
|
|
|
// Need to fix height?
|
|
if ( (sz.y-(customBorder*2)) < butHeight && btnWidth == 0 )
|
|
{
|
|
int newY = butHeight+(customBorder*2);
|
|
SetClientSize(wxDefaultCoord,newY);
|
|
sz.y = newY;
|
|
}
|
|
}
|
|
|
|
int butAreaWid = butWidth + (m_btnSpacingX*2);
|
|
|
|
m_btnSize.x = butWidth;
|
|
m_btnSize.y = butHeight;
|
|
|
|
m_btnArea.x = ( m_btnSide==wxRIGHT ? sz.x - butAreaWid - btnBorder : btnBorder );
|
|
m_btnArea.y = btnBorder + FOCUS_RING;
|
|
m_btnArea.width = butAreaWid;
|
|
m_btnArea.height = sz.y - ((btnBorder+FOCUS_RING)*2);
|
|
|
|
m_tcArea.x = ( m_btnSide==wxRIGHT ? 0 : butAreaWid ) + customBorder + FOCUS_RING;
|
|
m_tcArea.y = customBorder + FOCUS_RING;
|
|
m_tcArea.width = sz.x - butAreaWid - (customBorder*2) - (FOCUS_RING*2);
|
|
m_tcArea.height = sz.y - ((customBorder+FOCUS_RING)*2);
|
|
|
|
/*
|
|
if ( m_text )
|
|
{
|
|
::wxMessageBox(wxString::Format(wxT("ButtonArea (%i,%i,%i,%i)\n"),m_btnArea.x,m_btnArea.y,m_btnArea.width,m_btnArea.height) +
|
|
wxString::Format(wxT("TextCtrlArea (%i,%i,%i,%i)"),m_tcArea.x,m_tcArea.y,m_tcArea.width,m_tcArea.height));
|
|
}
|
|
*/
|
|
}
|
|
|
|
void wxComboCtrlBase::PositionTextCtrl( int textCtrlXAdjust, int textCtrlYAdjust )
|
|
{
|
|
if ( !m_text )
|
|
return;
|
|
|
|
#if !TEXTCTRL_TEXT_CENTERED
|
|
|
|
wxSize sz = GetClientSize();
|
|
|
|
int customBorder = m_widthCustomBorder;
|
|
if ( (m_text->GetWindowStyleFlag() & wxBORDER_MASK) == wxNO_BORDER )
|
|
{
|
|
// Centre textctrl
|
|
int tcSizeY = m_text->GetBestSize().y;
|
|
int diff = sz.y - tcSizeY;
|
|
int y = textCtrlYAdjust + (diff/2);
|
|
|
|
if ( y < customBorder )
|
|
y = customBorder;
|
|
|
|
m_text->SetSize( m_tcArea.x + m_widthCustomPaint + m_absIndent + textCtrlXAdjust,
|
|
y,
|
|
m_tcArea.width - COMBO_MARGIN -
|
|
(textCtrlXAdjust + m_widthCustomPaint + m_absIndent),
|
|
-1 );
|
|
|
|
// Make sure textctrl doesn't exceed the bottom custom border
|
|
wxSize tsz = m_text->GetSize();
|
|
diff = (y + tsz.y) - (sz.y - customBorder);
|
|
if ( diff >= 0 )
|
|
{
|
|
tsz.y = tsz.y - diff - 1;
|
|
m_text->SetSize(tsz);
|
|
}
|
|
}
|
|
else
|
|
#else // TEXTCTRL_TEXT_CENTERED
|
|
wxUnusedVar(textCtrlXAdjust);
|
|
wxUnusedVar(textCtrlYAdjust);
|
|
#endif // !TEXTCTRL_TEXT_CENTERED/TEXTCTRL_TEXT_CENTERED
|
|
{
|
|
// If it has border, have textctrl will the entire text field.
|
|
m_text->SetSize( m_tcArea.x + m_widthCustomPaint,
|
|
m_tcArea.y,
|
|
m_tcArea.width - m_widthCustomPaint,
|
|
m_tcArea.height );
|
|
}
|
|
}
|
|
|
|
wxSize wxComboCtrlBase::DoGetBestSize() const
|
|
{
|
|
wxSize sizeText(150,0);
|
|
|
|
if ( m_text )
|
|
sizeText = m_text->GetBestSize();
|
|
|
|
// TODO: Better method to calculate close-to-native control height.
|
|
|
|
int fhei;
|
|
if ( m_font.Ok() )
|
|
fhei = (m_font.GetPointSize()*2) + 5;
|
|
else if ( wxNORMAL_FONT->Ok() )
|
|
fhei = (wxNORMAL_FONT->GetPointSize()*2) + 5;
|
|
else
|
|
fhei = sizeText.y + 4;
|
|
|
|
// Need to force height to accomodate bitmap?
|
|
int btnSizeY = m_btnSize.y;
|
|
if ( m_bmpNormal.Ok() && fhei < btnSizeY )
|
|
fhei = btnSizeY;
|
|
|
|
// Control height doesn't depend on border
|
|
/*
|
|
// Add border
|
|
int border = m_windowStyle & wxBORDER_MASK;
|
|
if ( border == wxSIMPLE_BORDER )
|
|
fhei += 2;
|
|
else if ( border == wxNO_BORDER )
|
|
fhei += (m_widthCustomBorder*2);
|
|
else
|
|
// Sunken etc.
|
|
fhei += 4;
|
|
*/
|
|
|
|
// Final adjustments
|
|
#ifdef __WXGTK__
|
|
fhei += 1;
|
|
#endif
|
|
|
|
#ifdef __WXMAC__
|
|
// these are the numbers from the HIG:
|
|
switch ( m_windowVariant )
|
|
{
|
|
case wxWINDOW_VARIANT_NORMAL:
|
|
default :
|
|
fhei = 22;
|
|
break;
|
|
case wxWINDOW_VARIANT_SMALL:
|
|
fhei = 19;
|
|
break;
|
|
case wxWINDOW_VARIANT_MINI:
|
|
fhei = 15;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
fhei += 2 * FOCUS_RING;
|
|
int width = sizeText.x + FOCUS_RING + COMBO_MARGIN + DEFAULT_DROPBUTTON_WIDTH;
|
|
|
|
wxSize ret(width, fhei);
|
|
CacheBestSize(ret);
|
|
return ret;
|
|
}
|
|
|
|
void wxComboCtrlBase::OnSizeEvent( wxSizeEvent& event )
|
|
{
|
|
if ( !IsCreated() )
|
|
return;
|
|
|
|
// defined by actual wxComboCtrls
|
|
OnResize();
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// standard operations
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxComboCtrlBase::Enable(bool enable)
|
|
{
|
|
if ( !wxControl::Enable(enable) )
|
|
return false;
|
|
|
|
if ( m_btn )
|
|
m_btn->Enable(enable);
|
|
if ( m_text )
|
|
m_text->Enable(enable);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxComboCtrlBase::Show(bool show)
|
|
{
|
|
if ( !wxControl::Show(show) )
|
|
return false;
|
|
|
|
if (m_btn)
|
|
m_btn->Show(show);
|
|
|
|
if (m_text)
|
|
m_text->Show(show);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxComboCtrlBase::SetFont ( const wxFont& font )
|
|
{
|
|
if ( !wxControl::SetFont(font) )
|
|
return false;
|
|
|
|
if (m_text)
|
|
m_text->SetFont(font);
|
|
|
|
return true;
|
|
}
|
|
|
|
#if wxUSE_TOOLTIPS
|
|
void wxComboCtrlBase::DoSetToolTip(wxToolTip *tooltip)
|
|
{
|
|
wxControl::DoSetToolTip(tooltip);
|
|
|
|
// Set tool tip for button and text box
|
|
if ( tooltip )
|
|
{
|
|
const wxString &tip = tooltip->GetTip();
|
|
if ( m_text ) m_text->SetToolTip(tip);
|
|
if ( m_btn ) m_btn->SetToolTip(tip);
|
|
}
|
|
else
|
|
{
|
|
if ( m_text ) m_text->SetToolTip( (wxToolTip*) NULL );
|
|
if ( m_btn ) m_btn->SetToolTip( (wxToolTip*) NULL );
|
|
}
|
|
}
|
|
#endif // wxUSE_TOOLTIPS
|
|
|
|
#if wxUSE_VALIDATORS
|
|
void wxComboCtrlBase::SetValidator(const wxValidator& validator)
|
|
{
|
|
wxTextCtrl* textCtrl = GetTextCtrl();
|
|
|
|
if ( textCtrl )
|
|
textCtrl->SetValidator( validator );
|
|
}
|
|
|
|
wxValidator* wxComboCtrlBase::GetValidator()
|
|
{
|
|
wxTextCtrl* textCtrl = GetTextCtrl();
|
|
|
|
if ( textCtrl )
|
|
return textCtrl->GetValidator();
|
|
|
|
return wxControl::GetValidator();
|
|
}
|
|
#endif // wxUSE_VALIDATORS
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// painting
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if (!defined(__WXMSW__)) || defined(__WXUNIVERSAL__)
|
|
// prepare combo box background on area in a way typical on platform
|
|
void wxComboCtrlBase::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
|
|
{
|
|
wxSize sz = GetClientSize();
|
|
bool isEnabled;
|
|
bool isFocused; // also selected
|
|
|
|
// For smaller size control (and for disabled background) use less spacing
|
|
int focusSpacingX;
|
|
int focusSpacingY;
|
|
|
|
if ( !(flags & wxCONTROL_ISSUBMENU) )
|
|
{
|
|
// Drawing control
|
|
isEnabled = IsEnabled();
|
|
isFocused = ShouldDrawFocus();
|
|
|
|
// Windows-style: for smaller size control (and for disabled background) use less spacing
|
|
focusSpacingX = isEnabled ? 2 : 1;
|
|
focusSpacingY = sz.y > (GetCharHeight()+2) && isEnabled ? 2 : 1;
|
|
}
|
|
else
|
|
{
|
|
// Drawing a list item
|
|
isEnabled = true; // they are never disabled
|
|
isFocused = flags & wxCONTROL_SELECTED ? true : false;
|
|
|
|
focusSpacingX = 0;
|
|
focusSpacingY = 0;
|
|
}
|
|
|
|
// Set the background sub-rectangle for selection, disabled etc
|
|
wxRect selRect(rect);
|
|
selRect.y += focusSpacingY;
|
|
selRect.height -= (focusSpacingY*2);
|
|
|
|
int wcp = 0;
|
|
|
|
if ( !(flags & wxCONTROL_ISSUBMENU) )
|
|
wcp += m_widthCustomPaint;
|
|
|
|
selRect.x += wcp + focusSpacingX;
|
|
selRect.width -= wcp + (focusSpacingX*2);
|
|
|
|
wxColour bgCol;
|
|
|
|
if ( isEnabled )
|
|
{
|
|
// If popup is hidden and this control is focused,
|
|
// then draw the focus-indicator (selbgcolor background etc.).
|
|
if ( isFocused )
|
|
{
|
|
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
|
|
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
|
|
}
|
|
else
|
|
{
|
|
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
|
|
#ifndef __WXMAC__ // see note in OnThemeChange
|
|
bgCol = GetBackgroundColour();
|
|
#else
|
|
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) );
|
|
#ifndef __WXMAC__ // see note in OnThemeChange
|
|
bgCol = GetBackgroundColour();
|
|
#else
|
|
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
|
#endif
|
|
}
|
|
|
|
dc.SetBrush( bgCol );
|
|
dc.SetPen( bgCol );
|
|
dc.DrawRectangle( selRect );
|
|
|
|
// Don't clip exactly to the selection rectangle so we can draw
|
|
// to the non-selected area in front of it.
|
|
wxRect clipRect(rect.x,rect.y,
|
|
(selRect.x+selRect.width)-rect.x,rect.height);
|
|
dc.SetClippingRegion(clipRect);
|
|
}
|
|
#else
|
|
// Save the library size a bit for platforms that re-implement this.
|
|
void wxComboCtrlBase::PrepareBackground( wxDC&, const wxRect&, int ) const
|
|
{
|
|
}
|
|
#endif
|
|
|
|
void wxComboCtrlBase::DrawButton( wxDC& dc, const wxRect& rect, int paintBg )
|
|
{
|
|
int drawState = m_btnState;
|
|
|
|
#ifdef __WXGTK__
|
|
if ( GetPopupWindowState() >= Animating )
|
|
drawState |= wxCONTROL_PRESSED;
|
|
#endif
|
|
|
|
wxRect drawRect(rect.x+m_btnSpacingX,
|
|
rect.y+((rect.height-m_btnSize.y)/2),
|
|
m_btnSize.x,
|
|
m_btnSize.y);
|
|
|
|
// Make sure area is not larger than the control
|
|
if ( drawRect.y < rect.y )
|
|
drawRect.y = rect.y;
|
|
if ( drawRect.height > rect.height )
|
|
drawRect.height = rect.height;
|
|
|
|
bool enabled = IsEnabled();
|
|
|
|
if ( !enabled )
|
|
drawState |= wxCONTROL_DISABLED;
|
|
|
|
if ( !m_bmpNormal.Ok() )
|
|
{
|
|
// Need to clear button background even if m_btn is present
|
|
if ( paintBg )
|
|
{
|
|
wxColour bgCol;
|
|
|
|
if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
|
|
bgCol = GetParent()->GetBackgroundColour();
|
|
else
|
|
bgCol = GetBackgroundColour();
|
|
|
|
dc.SetBrush(bgCol);
|
|
dc.SetPen(bgCol);
|
|
dc.DrawRectangle(rect);
|
|
}
|
|
|
|
// Draw standard button
|
|
if (GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL)
|
|
drawState |= wxCONTROL_SIZE_SMALL;
|
|
else if (GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI)
|
|
drawState |= wxCONTROL_SIZE_MINI;
|
|
|
|
wxRendererNative::Get().DrawComboBoxDropButton(this,
|
|
dc,
|
|
drawRect,
|
|
drawState);
|
|
}
|
|
else
|
|
{
|
|
// Draw bitmap
|
|
|
|
wxBitmap* pBmp;
|
|
|
|
if ( !enabled )
|
|
pBmp = &m_bmpDisabled;
|
|
else if ( m_btnState & wxCONTROL_PRESSED )
|
|
pBmp = &m_bmpPressed;
|
|
else if ( m_btnState & wxCONTROL_CURRENT )
|
|
pBmp = &m_bmpHover;
|
|
else
|
|
pBmp = &m_bmpNormal;
|
|
|
|
if ( m_blankButtonBg )
|
|
{
|
|
// If using blank button background, we need to clear its background
|
|
// with button face colour instead of colour for rest of the control.
|
|
if ( paintBg )
|
|
{
|
|
wxColour bgCol = GetParent()->GetBackgroundColour(); //wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
|
|
//wxColour bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
|
dc.SetPen(bgCol);
|
|
dc.SetBrush(bgCol);
|
|
dc.DrawRectangle(rect);
|
|
}
|
|
|
|
if (GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_SMALL)
|
|
drawState |= wxCONTROL_SIZE_SMALL;
|
|
else if (GetParent()->GetWindowVariant() == wxWINDOW_VARIANT_MINI)
|
|
drawState |= wxCONTROL_SIZE_MINI;
|
|
|
|
wxRendererNative::Get().DrawPushButton(this,
|
|
dc,
|
|
drawRect,
|
|
drawState);
|
|
|
|
}
|
|
else
|
|
|
|
{
|
|
// Need to clear button background even if m_btn is present
|
|
// (assume non-button background was cleared just before this call so brushes are good)
|
|
if ( paintBg )
|
|
dc.DrawRectangle(rect);
|
|
}
|
|
|
|
// Draw bitmap centered in drawRect
|
|
dc.DrawBitmap(*pBmp,
|
|
drawRect.x + (drawRect.width-pBmp->GetWidth())/2,
|
|
drawRect.y + (drawRect.height-pBmp->GetHeight())/2,
|
|
true);
|
|
}
|
|
}
|
|
|
|
void wxComboCtrlBase::RecalcAndRefresh()
|
|
{
|
|
if ( IsCreated() )
|
|
{
|
|
wxSizeEvent evt(GetSize(),GetId());
|
|
GetEventHandler()->ProcessEvent(evt);
|
|
Refresh();
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// miscellaneous event handlers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxComboCtrlBase::OnTextCtrlEvent(wxCommandEvent& event)
|
|
{
|
|
if ( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED )
|
|
{
|
|
if ( m_ignoreEvtText > 0 )
|
|
{
|
|
m_ignoreEvtText--;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Change event id, object and string before relaying it forward
|
|
event.SetId(GetId());
|
|
wxString s = event.GetString();
|
|
event.SetEventObject(this);
|
|
event.SetString(s);
|
|
event.Skip();
|
|
}
|
|
|
|
// call if cursor is on button area or mouse is captured for the button
|
|
bool wxComboCtrlBase::HandleButtonMouseEvent( wxMouseEvent& event,
|
|
int flags )
|
|
{
|
|
int type = event.GetEventType();
|
|
|
|
if ( type == wxEVT_MOTION )
|
|
{
|
|
if ( flags & wxCC_MF_ON_BUTTON )
|
|
{
|
|
if ( !(m_btnState & wxCONTROL_CURRENT) )
|
|
{
|
|
// Mouse hover begins
|
|
m_btnState |= wxCONTROL_CURRENT;
|
|
if ( HasCapture() ) // Retain pressed state.
|
|
m_btnState |= wxCONTROL_PRESSED;
|
|
Refresh();
|
|
}
|
|
}
|
|
else if ( (m_btnState & wxCONTROL_CURRENT) )
|
|
{
|
|
// Mouse hover ends
|
|
m_btnState &= ~(wxCONTROL_CURRENT|wxCONTROL_PRESSED);
|
|
Refresh();
|
|
}
|
|
}
|
|
else if ( type == wxEVT_LEFT_DOWN || type == wxEVT_LEFT_DCLICK )
|
|
{
|
|
if ( flags & (wxCC_MF_ON_CLICK_AREA|wxCC_MF_ON_BUTTON) )
|
|
{
|
|
m_btnState |= wxCONTROL_PRESSED;
|
|
Refresh();
|
|
|
|
if ( !(m_iFlags & wxCC_POPUP_ON_MOUSE_UP) )
|
|
OnButtonClick();
|
|
else
|
|
// If showing popup now, do not capture mouse or there will be interference
|
|
CaptureMouse();
|
|
}
|
|
}
|
|
else if ( type == wxEVT_LEFT_UP )
|
|
{
|
|
|
|
// Only accept event if mouse was left-press was previously accepted
|
|
if ( HasCapture() )
|
|
ReleaseMouse();
|
|
|
|
if ( m_btnState & wxCONTROL_PRESSED )
|
|
{
|
|
// If mouse was inside, fire the click event.
|
|
if ( m_iFlags & wxCC_POPUP_ON_MOUSE_UP )
|
|
{
|
|
if ( flags & (wxCC_MF_ON_CLICK_AREA|wxCC_MF_ON_BUTTON) )
|
|
OnButtonClick();
|
|
}
|
|
|
|
m_btnState &= ~(wxCONTROL_PRESSED);
|
|
Refresh();
|
|
}
|
|
}
|
|
else if ( type == wxEVT_LEAVE_WINDOW )
|
|
{
|
|
if ( m_btnState & (wxCONTROL_CURRENT|wxCONTROL_PRESSED) )
|
|
{
|
|
m_btnState &= ~(wxCONTROL_CURRENT);
|
|
|
|
// Mouse hover ends
|
|
if ( IsPopupWindowState(Hidden) )
|
|
{
|
|
m_btnState &= ~(wxCONTROL_PRESSED);
|
|
Refresh();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
// returns true if event was consumed or filtered
|
|
bool wxComboCtrlBase::PreprocessMouseEvent( wxMouseEvent& event,
|
|
int WXUNUSED(flags) )
|
|
{
|
|
wxLongLong t = ::wxGetLocalTimeMillis();
|
|
int evtType = event.GetEventType();
|
|
|
|
#if USES_WXPOPUPWINDOW || USES_WXDIALOG
|
|
if ( m_popupWinType != POPUPWIN_WXPOPUPTRANSIENTWINDOW )
|
|
{
|
|
if ( IsPopupWindowState(Visible) &&
|
|
( evtType == wxEVT_LEFT_DOWN || evtType == wxEVT_RIGHT_DOWN ) )
|
|
{
|
|
HidePopup();
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Filter out clicks on button immediately after popup dismiss (Windows like behaviour)
|
|
if ( evtType == wxEVT_LEFT_DOWN && t < m_timeCanAcceptClick )
|
|
{
|
|
event.SetEventType(0);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void wxComboCtrlBase::HandleNormalMouseEvent( wxMouseEvent& event )
|
|
{
|
|
int evtType = event.GetEventType();
|
|
|
|
if ( (evtType == wxEVT_LEFT_DOWN || evtType == wxEVT_LEFT_DCLICK) &&
|
|
(m_windowStyle & wxCB_READONLY) )
|
|
{
|
|
if ( GetPopupWindowState() >= Animating )
|
|
{
|
|
#if USES_WXPOPUPWINDOW
|
|
// Click here always hides the popup.
|
|
if ( m_popupWinType == POPUPWIN_WXPOPUPWINDOW )
|
|
HidePopup();
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if ( !(m_windowStyle & wxCC_SPECIAL_DCLICK) )
|
|
{
|
|
// In read-only mode, clicking the text is the
|
|
// same as clicking the button.
|
|
OnButtonClick();
|
|
}
|
|
else if ( /*evtType == wxEVT_LEFT_UP || */evtType == wxEVT_LEFT_DCLICK )
|
|
{
|
|
//if ( m_popupInterface->CycleValue() )
|
|
// Refresh();
|
|
if ( m_popupInterface )
|
|
m_popupInterface->OnComboDoubleClick();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if ( IsPopupShown() )
|
|
{
|
|
// relay (some) mouse events to the popup
|
|
if ( evtType == wxEVT_MOUSEWHEEL )
|
|
m_popup->AddPendingEvent(event);
|
|
}
|
|
else if ( evtType )
|
|
event.Skip();
|
|
}
|
|
|
|
void wxComboCtrlBase::OnKeyEvent(wxKeyEvent& event)
|
|
{
|
|
if ( IsPopupShown() )
|
|
{
|
|
// pass it to the popped up control
|
|
GetPopupControl()->GetControl()->AddPendingEvent(event);
|
|
}
|
|
else // no popup
|
|
{
|
|
int keycode = event.GetKeyCode();
|
|
|
|
if ( keycode == WXK_TAB )
|
|
{
|
|
wxNavigationKeyEvent evt;
|
|
|
|
wxWindow* mainCtrl = GetMainWindowOfCompositeControl();
|
|
|
|
evt.SetFlags(wxNavigationKeyEvent::FromTab|
|
|
(!event.ShiftDown() ? wxNavigationKeyEvent::IsForward
|
|
: wxNavigationKeyEvent::IsBackward));
|
|
evt.SetEventObject(mainCtrl);
|
|
evt.SetCurrentFocus(mainCtrl);
|
|
mainCtrl->GetParent()->GetEventHandler()->AddPendingEvent(evt);
|
|
return;
|
|
}
|
|
|
|
if ( IsKeyPopupToggle(event) )
|
|
{
|
|
OnButtonClick();
|
|
return;
|
|
}
|
|
|
|
int comboStyle = GetWindowStyle();
|
|
wxComboPopup* popupInterface = GetPopupControl();
|
|
|
|
if ( !popupInterface )
|
|
{
|
|
event.Skip();
|
|
return;
|
|
}
|
|
|
|
if ( (comboStyle & wxCB_READONLY) ||
|
|
(keycode != WXK_RIGHT && keycode != WXK_LEFT) )
|
|
{
|
|
popupInterface->OnComboKeyEvent(event);
|
|
}
|
|
else
|
|
event.Skip();
|
|
}
|
|
}
|
|
|
|
void wxComboCtrlBase::OnFocusEvent( wxFocusEvent& event )
|
|
{
|
|
if ( event.GetEventType() == wxEVT_SET_FOCUS )
|
|
{
|
|
wxWindow* tc = GetTextCtrl();
|
|
if ( tc && tc != DoFindFocus() )
|
|
#ifdef __WXMAC__
|
|
m_resetFocus = true;
|
|
#else
|
|
tc->SetFocus();
|
|
#endif
|
|
}
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void wxComboCtrlBase::OnIdleEvent( wxIdleEvent& WXUNUSED(event) )
|
|
{
|
|
if ( m_resetFocus )
|
|
{
|
|
m_resetFocus = false;
|
|
wxWindow* tc = GetTextCtrl();
|
|
if ( tc )
|
|
tc->SetFocus();
|
|
}
|
|
}
|
|
|
|
void wxComboCtrlBase::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event))
|
|
{
|
|
OnThemeChange();
|
|
// indentation may also have changed
|
|
if ( !(m_iFlags & wxCC_IFLAG_INDENT_SET) )
|
|
m_absIndent = GetNativeTextIndent();
|
|
RecalcAndRefresh();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// popup handling
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Create popup window and the child control
|
|
void wxComboCtrlBase::CreatePopup()
|
|
{
|
|
wxComboPopup* popupInterface = m_popupInterface;
|
|
wxWindow* popup;
|
|
|
|
if ( !m_winPopup )
|
|
{
|
|
#ifdef wxComboPopupWindowBase2
|
|
if ( m_iFlags & wxCC_IFLAG_USE_ALT_POPUP )
|
|
{
|
|
#if !USES_WXDIALOG
|
|
m_winPopup = new wxComboPopupWindowBase2( this, wxNO_BORDER );
|
|
#else
|
|
m_winPopup = new wxComboPopupWindowBase2( this, wxID_ANY, wxEmptyString,
|
|
wxPoint(-21,-21), wxSize(20, 20),
|
|
wxNO_BORDER );
|
|
#endif
|
|
m_popupWinType = SECONDARY_POPUP_TYPE;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
m_winPopup = new wxComboPopupWindow( this, wxNO_BORDER );
|
|
m_popupWinType = PRIMARY_POPUP_TYPE;
|
|
}
|
|
m_popupWinEvtHandler = new wxComboPopupWindowEvtHandler(this);
|
|
m_winPopup->PushEventHandler(m_popupWinEvtHandler);
|
|
}
|
|
|
|
popupInterface->Create(m_winPopup);
|
|
m_popup = popup = popupInterface->GetControl();
|
|
|
|
m_popupExtraHandler = new wxComboPopupExtraEventHandler(this);
|
|
popup->PushEventHandler( m_popupExtraHandler );
|
|
|
|
// This may be helpful on some platforms
|
|
// (eg. it bypasses a wxGTK popupwindow bug where
|
|
// window is not initially hidden when it should be)
|
|
m_winPopup->Hide();
|
|
|
|
popupInterface->m_iFlags |= wxCP_IFLAG_CREATED;
|
|
}
|
|
|
|
// Destroy popup window and the child control
|
|
void wxComboCtrlBase::DestroyPopup()
|
|
{
|
|
HidePopup();
|
|
|
|
if ( m_popup )
|
|
m_popup->RemoveEventHandler(m_popupExtraHandler);
|
|
|
|
delete m_popupExtraHandler;
|
|
|
|
delete m_popupInterface;
|
|
|
|
if ( m_winPopup )
|
|
{
|
|
m_winPopup->RemoveEventHandler(m_popupWinEvtHandler);
|
|
delete m_popupWinEvtHandler;
|
|
m_popupWinEvtHandler = NULL;
|
|
m_winPopup->Destroy();
|
|
}
|
|
|
|
m_popupExtraHandler = (wxEvtHandler*) NULL;
|
|
m_popupInterface = (wxComboPopup*) NULL;
|
|
m_winPopup = (wxWindow*) NULL;
|
|
m_popup = (wxWindow*) NULL;
|
|
}
|
|
|
|
void wxComboCtrlBase::DoSetPopupControl(wxComboPopup* iface)
|
|
{
|
|
wxCHECK_RET( iface, wxT("no popup interface set for wxComboCtrl") );
|
|
|
|
DestroyPopup();
|
|
|
|
iface->InitBase(this);
|
|
iface->Init();
|
|
|
|
m_popupInterface = iface;
|
|
|
|
if ( !iface->LazyCreate() )
|
|
{
|
|
CreatePopup();
|
|
}
|
|
else
|
|
{
|
|
m_popup = (wxWindow*) NULL;
|
|
}
|
|
|
|
// This must be done after creation
|
|
if ( m_valueString.length() )
|
|
{
|
|
iface->SetStringValue(m_valueString);
|
|
//Refresh();
|
|
}
|
|
}
|
|
|
|
// Ensures there is atleast the default popup
|
|
void wxComboCtrlBase::EnsurePopupControl()
|
|
{
|
|
if ( !m_popupInterface )
|
|
SetPopupControl(NULL);
|
|
}
|
|
|
|
void wxComboCtrlBase::OnButtonClick()
|
|
{
|
|
// Derived classes can override this method for totally custom
|
|
// popup action
|
|
ShowPopup();
|
|
}
|
|
|
|
void wxComboCtrlBase::ShowPopup()
|
|
{
|
|
EnsurePopupControl();
|
|
wxCHECK_RET( !IsPopupWindowState(Visible), wxT("popup window already shown") );
|
|
|
|
if ( IsPopupWindowState(Animating) )
|
|
return;
|
|
|
|
SetFocus();
|
|
|
|
// Space above and below
|
|
int screenHeight;
|
|
wxPoint scrPos;
|
|
int spaceAbove;
|
|
int spaceBelow;
|
|
int maxHeightPopup;
|
|
wxSize ctrlSz = GetSize();
|
|
|
|
screenHeight = wxSystemSettings::GetMetric( wxSYS_SCREEN_Y );
|
|
scrPos = GetParent()->ClientToScreen(GetPosition());
|
|
|
|
spaceAbove = scrPos.y;
|
|
spaceBelow = screenHeight - spaceAbove - ctrlSz.y;
|
|
|
|
maxHeightPopup = spaceBelow;
|
|
if ( spaceAbove > spaceBelow )
|
|
maxHeightPopup = spaceAbove;
|
|
|
|
// Width
|
|
int widthPopup = ctrlSz.x + m_extLeft + m_extRight;
|
|
|
|
if ( widthPopup < m_widthMinPopup )
|
|
widthPopup = m_widthMinPopup;
|
|
|
|
wxWindow* winPopup = m_winPopup;
|
|
wxWindow* popup;
|
|
|
|
// Need to disable tab traversal of parent
|
|
//
|
|
// NB: This is to fix a bug in wxMSW. In theory it could also be fixed
|
|
// by, for instance, adding check to window.cpp:wxWindowMSW::MSWProcessMessage
|
|
// that if transient popup is open, then tab traversal is to be ignored.
|
|
// However, I think this code would still be needed for cases where
|
|
// transient popup doesn't work yet (wxWinCE?).
|
|
wxWindow* parent = GetParent();
|
|
int parentFlags = parent->GetWindowStyle();
|
|
if ( parentFlags & wxTAB_TRAVERSAL )
|
|
{
|
|
parent->SetWindowStyle( parentFlags & ~(wxTAB_TRAVERSAL) );
|
|
m_iFlags |= wxCC_IFLAG_PARENT_TAB_TRAVERSAL;
|
|
}
|
|
|
|
if ( !winPopup )
|
|
{
|
|
CreatePopup();
|
|
winPopup = m_winPopup;
|
|
popup = m_popup;
|
|
}
|
|
else
|
|
{
|
|
popup = m_popup;
|
|
}
|
|
|
|
winPopup->Enable();
|
|
|
|
wxASSERT( !m_popup || m_popup == popup ); // Consistency check.
|
|
|
|
wxSize adjustedSize = m_popupInterface->GetAdjustedSize(widthPopup,
|
|
m_heightPopup<=0?DEFAULT_POPUP_HEIGHT:m_heightPopup,
|
|
maxHeightPopup);
|
|
|
|
popup->SetSize(adjustedSize);
|
|
popup->Move(0,0);
|
|
m_popupInterface->OnPopup();
|
|
|
|
//
|
|
// Reposition and resize popup window
|
|
//
|
|
|
|
wxSize szp = popup->GetSize();
|
|
|
|
int popupX;
|
|
int popupY = scrPos.y + ctrlSz.y;
|
|
|
|
// Default anchor is wxLEFT
|
|
int anchorSide = m_anchorSide;
|
|
if ( !anchorSide )
|
|
anchorSide = wxLEFT;
|
|
|
|
int rightX = scrPos.x + ctrlSz.x + m_extRight - szp.x;
|
|
int leftX = scrPos.x - m_extLeft;
|
|
|
|
if ( wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft )
|
|
leftX -= ctrlSz.x;
|
|
|
|
int screenWidth = wxSystemSettings::GetMetric( wxSYS_SCREEN_X );
|
|
|
|
// If there is not enough horizontal space, anchor on the other side.
|
|
// If there is no space even then, place the popup at x 0.
|
|
if ( anchorSide == wxRIGHT )
|
|
{
|
|
if ( rightX < 0 )
|
|
{
|
|
if ( (leftX+szp.x) < screenWidth )
|
|
anchorSide = wxLEFT;
|
|
else
|
|
anchorSide = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( (leftX+szp.x) >= screenWidth )
|
|
{
|
|
if ( rightX >= 0 )
|
|
anchorSide = wxRIGHT;
|
|
else
|
|
anchorSide = 0;
|
|
}
|
|
}
|
|
|
|
// Select x coordinate according to the anchor side
|
|
if ( anchorSide == wxRIGHT )
|
|
popupX = rightX;
|
|
else if ( anchorSide == wxLEFT )
|
|
popupX = leftX;
|
|
else
|
|
popupX = 0;
|
|
|
|
int showFlags = CanDeferShow;
|
|
|
|
if ( spaceBelow < szp.y )
|
|
{
|
|
popupY = scrPos.y - szp.y;
|
|
showFlags |= ShowAbove;
|
|
}
|
|
|
|
#if INSTALL_TOPLEV_HANDLER
|
|
// Put top level window event handler into place
|
|
if ( m_popupWinType == POPUPWIN_WXPOPUPWINDOW )
|
|
{
|
|
if ( !m_toplevEvtHandler )
|
|
m_toplevEvtHandler = new wxComboFrameEventHandler(this);
|
|
|
|
wxWindow* toplev = ::wxGetTopLevelParent( this );
|
|
wxASSERT( toplev );
|
|
((wxComboFrameEventHandler*)m_toplevEvtHandler)->OnPopup();
|
|
toplev->PushEventHandler( m_toplevEvtHandler );
|
|
}
|
|
#endif
|
|
|
|
// Set string selection (must be this way instead of SetStringSelection)
|
|
if ( m_text )
|
|
{
|
|
if ( !(m_iFlags & wxCC_NO_TEXT_AUTO_SELECT) )
|
|
m_text->SelectAll();
|
|
|
|
m_popupInterface->SetStringValue( m_text->GetValue() );
|
|
}
|
|
else
|
|
{
|
|
// This is neede since focus/selection indication may change when popup is shown
|
|
Refresh();
|
|
}
|
|
|
|
// This must be after SetStringValue
|
|
m_popupWinState = Animating;
|
|
|
|
wxRect popupWinRect( popupX, popupY, szp.x, szp.y );
|
|
|
|
m_popup = popup;
|
|
if ( (m_iFlags & wxCC_IFLAG_DISABLE_POPUP_ANIM) ||
|
|
AnimateShow( popupWinRect, showFlags ) )
|
|
{
|
|
DoShowPopup( popupWinRect, showFlags );
|
|
}
|
|
}
|
|
|
|
bool wxComboCtrlBase::AnimateShow( const wxRect& WXUNUSED(rect), int WXUNUSED(flags) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void wxComboCtrlBase::DoShowPopup( const wxRect& rect, int WXUNUSED(flags) )
|
|
{
|
|
wxWindow* winPopup = m_winPopup;
|
|
|
|
if ( IsPopupWindowState(Animating) )
|
|
{
|
|
// Make sure the popup window is shown in the right position.
|
|
// Should not matter even if animation already did this.
|
|
|
|
// Some platforms (GTK) may like SetSize and Move to be separate
|
|
// (though the bug was probably fixed).
|
|
winPopup->SetSize( rect );
|
|
|
|
winPopup->Show();
|
|
|
|
m_popupWinState = Visible;
|
|
}
|
|
else if ( IsPopupWindowState(Hidden) )
|
|
{
|
|
// Animation was aborted
|
|
|
|
wxASSERT( !winPopup->IsShown() );
|
|
|
|
m_popupWinState = Hidden;
|
|
}
|
|
}
|
|
|
|
void wxComboCtrlBase::OnPopupDismiss()
|
|
{
|
|
// Just in case, avoid double dismiss
|
|
if ( IsPopupWindowState(Hidden) )
|
|
return;
|
|
|
|
// This must be set before focus - otherwise there will be recursive
|
|
// OnPopupDismisses.
|
|
m_popupWinState = Hidden;
|
|
|
|
//SetFocus();
|
|
m_winPopup->Disable();
|
|
|
|
// Inform popup control itself
|
|
m_popupInterface->OnDismiss();
|
|
|
|
if ( m_popupExtraHandler )
|
|
((wxComboPopupExtraEventHandler*)m_popupExtraHandler)->OnPopupDismiss();
|
|
|
|
#if INSTALL_TOPLEV_HANDLER
|
|
// Remove top level window event handler
|
|
if ( m_toplevEvtHandler )
|
|
{
|
|
wxWindow* toplev = ::wxGetTopLevelParent( this );
|
|
if ( toplev )
|
|
toplev->RemoveEventHandler( m_toplevEvtHandler );
|
|
}
|
|
#endif
|
|
|
|
m_timeCanAcceptClick = ::wxGetLocalTimeMillis();
|
|
|
|
if ( m_popupWinType == POPUPWIN_WXPOPUPTRANSIENTWINDOW )
|
|
m_timeCanAcceptClick += 150;
|
|
|
|
// If cursor not on dropdown button, then clear its state
|
|
// (technically not required by all ports, but do it for all just in case)
|
|
if ( !m_btnArea.Contains(ScreenToClient(::wxGetMousePosition())) )
|
|
m_btnState = 0;
|
|
|
|
// Return parent's tab traversal flag.
|
|
// See ShowPopup for notes.
|
|
if ( m_iFlags & wxCC_IFLAG_PARENT_TAB_TRAVERSAL )
|
|
{
|
|
wxWindow* parent = GetParent();
|
|
parent->SetWindowStyle( parent->GetWindowStyle() | wxTAB_TRAVERSAL );
|
|
m_iFlags &= ~(wxCC_IFLAG_PARENT_TAB_TRAVERSAL);
|
|
}
|
|
|
|
// refresh control (necessary even if m_text)
|
|
Refresh();
|
|
|
|
SetFocus();
|
|
}
|
|
|
|
void wxComboCtrlBase::HidePopup()
|
|
{
|
|
// Should be able to call this without popup interface
|
|
if ( IsPopupWindowState(Hidden) )
|
|
return;
|
|
|
|
// transfer value and show it in textctrl, if any
|
|
if ( !IsPopupWindowState(Animating) )
|
|
SetValue( m_popupInterface->GetStringValue() );
|
|
|
|
m_winPopup->Hide();
|
|
|
|
OnPopupDismiss();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// customization methods
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxComboCtrlBase::SetButtonPosition( int width, int height,
|
|
int side, int spacingX )
|
|
{
|
|
m_btnWid = width;
|
|
m_btnHei = height;
|
|
m_btnSide = side;
|
|
m_btnSpacingX = spacingX;
|
|
|
|
RecalcAndRefresh();
|
|
}
|
|
|
|
wxSize wxComboCtrlBase::GetButtonSize()
|
|
{
|
|
if ( m_btnSize.x > 0 )
|
|
return m_btnSize;
|
|
|
|
wxSize retSize(m_btnWid,m_btnHei);
|
|
|
|
// Need to call CalculateAreas now if button size is
|
|
// is not explicitly specified.
|
|
if ( retSize.x <= 0 || retSize.y <= 0)
|
|
{
|
|
OnResize();
|
|
|
|
retSize = m_btnSize;
|
|
}
|
|
|
|
return retSize;
|
|
}
|
|
|
|
void wxComboCtrlBase::SetButtonBitmaps( const wxBitmap& bmpNormal,
|
|
bool blankButtonBg,
|
|
const wxBitmap& bmpPressed,
|
|
const wxBitmap& bmpHover,
|
|
const wxBitmap& bmpDisabled )
|
|
{
|
|
m_bmpNormal = bmpNormal;
|
|
m_blankButtonBg = blankButtonBg;
|
|
|
|
if ( bmpPressed.Ok() )
|
|
m_bmpPressed = bmpPressed;
|
|
else
|
|
m_bmpPressed = bmpNormal;
|
|
|
|
if ( bmpHover.Ok() )
|
|
m_bmpHover = bmpHover;
|
|
else
|
|
m_bmpHover = bmpNormal;
|
|
|
|
if ( bmpDisabled.Ok() )
|
|
m_bmpDisabled = bmpDisabled;
|
|
else
|
|
m_bmpDisabled = bmpNormal;
|
|
|
|
RecalcAndRefresh();
|
|
}
|
|
|
|
void wxComboCtrlBase::SetCustomPaintWidth( int width )
|
|
{
|
|
if ( m_text )
|
|
{
|
|
// move textctrl accordingly
|
|
wxRect r = m_text->GetRect();
|
|
int inc = width - m_widthCustomPaint;
|
|
r.x += inc;
|
|
r.width -= inc;
|
|
m_text->SetSize( r );
|
|
}
|
|
|
|
m_widthCustomPaint = width;
|
|
|
|
RecalcAndRefresh();
|
|
}
|
|
|
|
void wxComboCtrlBase::SetTextIndent( int indent )
|
|
{
|
|
if ( indent < 0 )
|
|
{
|
|
m_absIndent = GetNativeTextIndent();
|
|
m_iFlags &= ~(wxCC_IFLAG_INDENT_SET);
|
|
}
|
|
else
|
|
{
|
|
m_absIndent = indent;
|
|
m_iFlags |= wxCC_IFLAG_INDENT_SET;
|
|
}
|
|
|
|
RecalcAndRefresh();
|
|
}
|
|
|
|
wxCoord wxComboCtrlBase::GetNativeTextIndent() const
|
|
{
|
|
return DEFAULT_TEXT_INDENT;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// methods forwarded to wxTextCtrl
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxString wxComboCtrlBase::GetValue() const
|
|
{
|
|
if ( m_text )
|
|
return m_text->GetValue();
|
|
return m_valueString;
|
|
}
|
|
|
|
void wxComboCtrlBase::SetValueWithEvent(const wxString& value, bool withEvent)
|
|
{
|
|
if ( m_text )
|
|
{
|
|
if ( !withEvent )
|
|
m_ignoreEvtText++;
|
|
|
|
m_text->SetValue(value);
|
|
if ( !(m_iFlags & wxCC_NO_TEXT_AUTO_SELECT) )
|
|
m_text->SelectAll();
|
|
}
|
|
|
|
m_valueString = value;
|
|
|
|
Refresh();
|
|
|
|
// Since wxComboPopup may want to paint the combo as well, we need
|
|
// to set the string value here (as well as sometimes in ShowPopup).
|
|
if ( m_valueString != value && m_popupInterface )
|
|
{
|
|
m_popupInterface->SetStringValue(value);
|
|
}
|
|
}
|
|
|
|
void wxComboCtrlBase::SetValue(const wxString& value)
|
|
{
|
|
SetValueWithEvent(value, false);
|
|
}
|
|
|
|
// In this SetValue variant wxComboPopup::SetStringValue is not called
|
|
void wxComboCtrlBase::SetText(const wxString& value)
|
|
{
|
|
// Unlike in SetValue(), this must be called here or
|
|
// the behaviour will no be consistent in readonlys.
|
|
EnsurePopupControl();
|
|
|
|
m_valueString = value;
|
|
|
|
if ( m_text )
|
|
{
|
|
m_ignoreEvtText++;
|
|
m_text->SetValue( value );
|
|
}
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void wxComboCtrlBase::Copy()
|
|
{
|
|
if ( m_text )
|
|
m_text->Copy();
|
|
}
|
|
|
|
void wxComboCtrlBase::Cut()
|
|
{
|
|
if ( m_text )
|
|
m_text->Cut();
|
|
}
|
|
|
|
void wxComboCtrlBase::Paste()
|
|
{
|
|
if ( m_text )
|
|
m_text->Paste();
|
|
}
|
|
|
|
void wxComboCtrlBase::SetInsertionPoint(long pos)
|
|
{
|
|
if ( m_text )
|
|
m_text->SetInsertionPoint(pos);
|
|
}
|
|
|
|
void wxComboCtrlBase::SetInsertionPointEnd()
|
|
{
|
|
if ( m_text )
|
|
m_text->SetInsertionPointEnd();
|
|
}
|
|
|
|
long wxComboCtrlBase::GetInsertionPoint() const
|
|
{
|
|
if ( m_text )
|
|
return m_text->GetInsertionPoint();
|
|
|
|
return 0;
|
|
}
|
|
|
|
long wxComboCtrlBase::GetLastPosition() const
|
|
{
|
|
if ( m_text )
|
|
return m_text->GetLastPosition();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void wxComboCtrlBase::Replace(long from, long to, const wxString& value)
|
|
{
|
|
if ( m_text )
|
|
m_text->Replace(from, to, value);
|
|
}
|
|
|
|
void wxComboCtrlBase::Remove(long from, long to)
|
|
{
|
|
if ( m_text )
|
|
m_text->Remove(from, to);
|
|
}
|
|
|
|
void wxComboCtrlBase::SetSelection(long from, long to)
|
|
{
|
|
if ( m_text )
|
|
m_text->SetSelection(from, to);
|
|
}
|
|
|
|
void wxComboCtrlBase::Undo()
|
|
{
|
|
if ( m_text )
|
|
m_text->Undo();
|
|
}
|
|
|
|
#endif // wxUSE_COMBOCTRL
|