Files
wxWidgets/src/common/combocmn.cpp
Vadim Zeitlin e631e7da62 Avoid using wxDynamicCast() with wxComboCtrl-derived object
In wxUniv wxComboBox does derive from wxComboCtrl, but its wxRTTI
doesn't use wxComboCtrl as the base class for consistency with the other
ports, meaning that wxDynamicCast() to wxComboCtrl fails.

Avoid this problem by storing wxComboCtrl in wxComboPopupWindow, as then
we can just use it later without any casts -- at the price of storing an
extra pointer in a transient object, i.e. without any real cost.

See https://github.com/wxWidgets/wxWidgets/pull/2418
2021-07-10 22:32:24 +01:00

2720 lines
71 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/common/combocmn.cpp
// Purpose: wxComboCtrlBase
// Author: Jaakko Salli
// Modified by:
// Created: Apr-30-2006
// Copyright: (c) 2005 Jaakko Salli
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "wx/wxprec.h"
#include "wx/combo.h"
#include "wx/display.h"
#ifdef __WXMSW__
#include "wx/msw/private.h"
#endif
#if wxUSE_COMBOBOX
#include "wx/combobox.h"
extern WXDLLEXPORT_DATA(const char) wxComboBoxNameStr[] = "comboBox";
// ----------------------------------------------------------------------------
// XTI
// ----------------------------------------------------------------------------
wxDEFINE_FLAGS( wxComboBoxStyle )
wxBEGIN_FLAGS( wxComboBoxStyle )
// new style border flags, we put them first to
// use them for streaming out
wxFLAGS_MEMBER(wxBORDER_SIMPLE)
wxFLAGS_MEMBER(wxBORDER_SUNKEN)
wxFLAGS_MEMBER(wxBORDER_DOUBLE)
wxFLAGS_MEMBER(wxBORDER_RAISED)
wxFLAGS_MEMBER(wxBORDER_STATIC)
wxFLAGS_MEMBER(wxBORDER_NONE)
// old style border flags
wxFLAGS_MEMBER(wxSIMPLE_BORDER)
wxFLAGS_MEMBER(wxSUNKEN_BORDER)
wxFLAGS_MEMBER(wxDOUBLE_BORDER)
wxFLAGS_MEMBER(wxRAISED_BORDER)
wxFLAGS_MEMBER(wxSTATIC_BORDER)
wxFLAGS_MEMBER(wxBORDER)
// standard window styles
wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
wxFLAGS_MEMBER(wxCLIP_CHILDREN)
wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
wxFLAGS_MEMBER(wxWANTS_CHARS)
wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
wxFLAGS_MEMBER(wxVSCROLL)
wxFLAGS_MEMBER(wxHSCROLL)
wxFLAGS_MEMBER(wxCB_SIMPLE)
wxFLAGS_MEMBER(wxCB_SORT)
wxFLAGS_MEMBER(wxCB_READONLY)
wxFLAGS_MEMBER(wxCB_DROPDOWN)
wxEND_FLAGS( wxComboBoxStyle )
wxIMPLEMENT_DYNAMIC_CLASS_XTI(wxComboBox, wxControl, "wx/combobox.h");
wxBEGIN_PROPERTIES_TABLE(wxComboBox)
wxEVENT_PROPERTY( Select, wxEVT_COMBOBOX, wxCommandEvent )
wxEVENT_PROPERTY( TextEnter, wxEVT_TEXT_ENTER, wxCommandEvent )
// TODO DELEGATES
wxPROPERTY( Font, wxFont, SetFont, GetFont, wxEMPTY_PARAMETER_VALUE, \
0 /*flags*/, wxT("Helpstring"), wxT("group"))
wxPROPERTY_COLLECTION( Choices, wxArrayString, wxString, AppendString, \
GetStrings, 0 /*flags*/, wxT("Helpstring"), wxT("group"))
wxPROPERTY( Value,wxString, SetValue, GetValue, wxEMPTY_PARAMETER_VALUE, \
0 /*flags*/, wxT("Helpstring"), wxT("group"))
wxPROPERTY( Selection,int, SetSelection, GetSelection, wxEMPTY_PARAMETER_VALUE, \
0 /*flags*/, wxT("Helpstring"), wxT("group"))
wxPROPERTY_FLAGS( WindowStyle, wxComboBoxStyle, long, SetWindowStyleFlag, \
GetWindowStyleFlag, wxEMPTY_PARAMETER_VALUE, 0 /*flags*/, \
wxT("Helpstring"), wxT("group")) // style
wxEND_PROPERTIES_TABLE()
wxEMPTY_HANDLERS_TABLE(wxComboBox)
wxCONSTRUCTOR_5( wxComboBox, wxWindow*, Parent, wxWindowID, Id, \
wxString, Value, wxPoint, Position, wxSize, Size )
#endif // wxUSE_COMBOBOX
#if wxUSE_COMBOCTRL
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/log.h"
#include "wx/dcclient.h"
#include "wx/settings.h"
#include "wx/timer.h"
#include "wx/textctrl.h"
#endif
#include "wx/tooltip.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__)
// Let's use wxFrame as a fall-back solution until wxMSW gets wxNonOwnedWindow
#include "wx/frame.h"
#define wxCC_GENERIC_TLW_IS_FRAME
#define wxComboCtrlGenericTLW wxFrame
#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).
#include "wx/dialog.h"
#define wxComboCtrlGenericTLW wxDialog
#if defined(__WXGTK20__)
# include "wx/gtk/private.h"
#else
# include "wx/gtk1/private.h"
#endif
#elif defined(__WXMAC__)
#include "wx/nonownedwnd.h"
#define wxCC_GENERIC_TLW_IS_NONOWNEDWINDOW
#define wxComboCtrlGenericTLW wxNonOwnedWindow
#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
#include "wx/dialog.h"
#define wxComboCtrlGenericTLW wxDialog
// Assume we can't use wxPopupTransientWindow on the other platforms.
#undef wxUSE_POPUPWIN
#define wxUSE_POPUPWIN 0
#endif
// No focus ring by default.
#ifndef FOCUS_RING
#define FOCUS_RING 0
#endif
#if wxUSE_POPUPWIN
#include "wx/popupwin.h"
#define wxComboPopupWindowBase wxPopupTransientWindow
#else
#define wxComboPopupWindowBase wxComboCtrlGenericTLW
#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 !wxUSE_POPUPWIN
//
// 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:
wxDECLARE_EVENT_TABLE();
};
wxBEGIN_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)
wxEND_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 || (
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(true);
}
event.Skip();
}
void wxComboFrameEventHandler::OnMenuEvent( wxMenuEvent& event )
{
m_combo->HidePopup(true);
event.Skip();
}
void wxComboFrameEventHandler::OnMouseEvent( wxMouseEvent& event )
{
m_combo->HidePopup(true);
event.Skip();
}
void wxComboFrameEventHandler::OnClose( wxCloseEvent& event )
{
m_combo->HidePopup(true);
event.Skip();
}
void wxComboFrameEventHandler::OnActivate( wxActivateEvent& event )
{
m_combo->HidePopup(true);
event.Skip();
}
void wxComboFrameEventHandler::OnResize( wxSizeEvent& event )
{
m_combo->HidePopup(true);
event.Skip();
}
void wxComboFrameEventHandler::OnMove( wxMoveEvent& event )
{
m_combo->HidePopup(true);
event.Skip();
}
#endif // !wxUSE_POPUPWIN
// ----------------------------------------------------------------------------
// wxComboPopupWindow is, in essence, wxPopupWindow customized for
// wxComboCtrl.
// ----------------------------------------------------------------------------
class wxComboPopupWindow : public wxComboPopupWindowBase
{
public:
wxComboPopupWindow( wxComboCtrlBase *parent,
int style )
#if wxUSE_POPUPWIN
: wxComboPopupWindowBase(parent,
style | wxPU_CONTAINS_CONTROLS)
#else
: wxComboPopupWindowBase(parent,
wxID_ANY,
wxEmptyString,
wxPoint(-21,-21),
wxSize(20,20),
style)
#endif
, m_combo(parent)
{
m_inShow = 0;
}
#if wxUSE_POPUPWIN
virtual bool Show( bool show ) wxOVERRIDE;
virtual bool ProcessLeftDown(wxMouseEvent& event) wxOVERRIDE;
protected:
virtual void OnDismiss() wxOVERRIDE;
#endif
private:
// This is the same as our parent, but has the right type, so that we can
// avoid using casts later.
wxComboCtrlBase* const m_combo;
wxByte m_inShow;
wxDECLARE_NO_COPY_CLASS(wxComboPopupWindow);
};
#if wxUSE_POPUPWIN
bool wxComboPopupWindow::Show( bool show )
{
// Guard against recursion
if ( m_inShow )
return wxComboPopupWindowBase::Show(show);
m_inShow++;
wxPopupTransientWindow* const
ptw = static_cast<wxPopupTransientWindow*>(this);
if ( show != ptw->IsShown() )
{
if ( show )
// We used to do wxPopupTransientWindow::Popup here,
// but this would hide normal Show, which we are
// also going to need.
ptw->Show();
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()
{
m_combo->OnPopupDismiss(true);
}
#endif // wxUSE_POPUPWIN
// ----------------------------------------------------------------------------
// custom event handling for popup window is done in wxComboCtrlBase so we can
// have different types of popup windows.
// ----------------------------------------------------------------------------
void wxComboCtrlBase::OnPopupSize( wxSizeEvent& WXUNUSED(event) )
{
// Block the event so that the popup control does not get auto-resized.
}
void wxComboCtrlBase::OnPopupKey( wxKeyEvent& event )
{
// Relay keyboard event to the main child controls
wxWindowList children = GetPopupWindow()->GetChildren();
wxWindowList::iterator node = children.begin();
wxWindow* child = (wxWindow*)*node;
child->GetEventHandler()->ProcessEvent(event);
}
#if !wxUSE_POPUPWIN
void wxComboCtrlBase::OnPopupActivate( wxActivateEvent& event )
{
if ( !event.GetActive() )
{
// Tell combo control that we are dismissed.
#ifdef __WXMSW__
// We need to hide the popup but calling ::ShowWindow() directly from WM_ACTIVATE
// event handler causes some side effects like calling this handler again (Win 7)
// or setting the focus improperly (Win 10), so postpone it slightly.
// See wxPopupTransientWindow::MSWHandleMessage().
CallAfter(&wxComboCtrlBase::Dismiss);
#else // !__WXMSW__
Dismiss();
#endif // __WXMSW__ / !__WXMSW__
event.Skip();
}
}
#endif
// ----------------------------------------------------------------------------
// wxComboPopup
//
// ----------------------------------------------------------------------------
wxComboPopup::~wxComboPopup()
{
}
void wxComboPopup::OnPopup()
{
}
void wxComboPopup::OnDismiss()
{
}
wxComboCtrl* wxComboPopup::GetComboCtrl() const
{
return wxStaticCast(m_combo, wxComboCtrl);
}
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->m_marginLeft,
(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::OnComboCharEvent( wxKeyEvent& event )
{
event.Skip();
}
void wxComboPopup::OnComboDoubleClick()
{
}
void wxComboPopup::SetStringValue( const wxString& WXUNUSED(value) )
{
}
bool wxComboPopup::FindItem(const wxString& WXUNUSED(item),
wxString* WXUNUSED(trueItem))
{
return true;
}
bool wxComboPopup::LazyCreate()
{
return false;
}
void wxComboPopup::Dismiss()
{
m_combo->HidePopup(true);
}
void wxComboPopup::DestroyPopup()
{
// Here we make sure that the popup control's Destroy() gets called.
// This is necessary for the wxPersistentWindow to work properly.
wxWindow* popupCtrl = GetControl();
if ( popupCtrl )
{
// While all wxComboCtrl examples have m_popupInterface and
// popupCtrl as the same class (that will be deleted via the
// Destroy() call below), it is technically still possible to
// have implementations where they are in fact not same
// multiple-inherited class. Here we use C++ RTTI to check for
// this rare case.
#ifndef wxNO_RTTI
// It is probably better to delete m_popupInterface first, so
// that it retains access to its popup control window.
if ( dynamic_cast<void*>(this) !=
dynamic_cast<void*>(popupCtrl) )
delete this;
#endif
popupCtrl->Destroy();
}
else
{
delete this;
}
}
// ----------------------------------------------------------------------------
// input handling
// ----------------------------------------------------------------------------
void wxComboCtrlBase::OnTextKey(wxKeyEvent& event)
{
// Let the wxComboCtrl event handler have a go first.
wxKeyEvent redirectedEvent(event);
redirectedEvent.SetId(GetId());
redirectedEvent.SetEventObject(this);
if ( !GetEventHandler()->ProcessEvent(redirectedEvent) )
{
event.Skip();
}
}
void wxComboCtrlBase::OnTextFocus(wxFocusEvent& event)
{
// FIXME: This code does run when control is clicked,
// yet on Windows it doesn't select all the text.
if ( event.GetEventType() == wxEVT_SET_FOCUS &&
!(GetInternalFlags() & wxCC_NO_TEXT_AUTO_SELECT) )
{
if ( GetTextCtrl() )
GetTextCtrl()->SelectAll();
else
SelectAll();
}
// 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(event);
evt2.SetId(GetId());
evt2.SetEventObject(this);
GetEventHandler()->ProcessEvent(evt2);
event.Skip();
}
void wxComboCtrlBase::OnPopupMouseEvent( wxMouseEvent& event )
{
wxPoint pt = event.GetPosition();
wxSize sz = GetPopupControl()->GetControl()->GetClientSize();
int evtType = event.GetEventType();
bool isInside = pt.x >= 0 && pt.y >= 0 && pt.x < sz.x && pt.y < sz.y;
bool relayToButton = false;
event.Skip();
if ( !isInside || !IsPopupShown() )
{
// Mouse is outside the popup or popup is not actually shown (yet)
if ( evtType == wxEVT_MOTION ||
evtType == wxEVT_LEFT_DOWN ||
evtType == wxEVT_LEFT_UP ||
evtType == wxEVT_RIGHT_DOWN )
{
// Block motion and click events outside the popup
event.Skip(false);
}
}
else
{
// Mouse is inside the popup, which is fully shown
m_beenInsidePopup = true;
// Do not let the popup control respond to mouse events until
// mouse press used to display the popup has been lifted. This
// is important for users with slower mouse fingers or mouse
// drivers. Note that we have some redundancy here, just in
// case the popup is some native control that does not emit all
// mouse event types.
if ( evtType == wxEVT_MOTION )
{
if ( m_blockEventsToPopup )
{
if ( event.LeftIsDown() )
event.Skip(false);
else
m_blockEventsToPopup = false;
}
}
else if ( evtType == wxEVT_LEFT_DOWN )
{
if ( m_blockEventsToPopup )
m_blockEventsToPopup = false;
}
else if ( evtType == wxEVT_LEFT_UP )
{
if ( m_blockEventsToPopup )
{
// On first left up, stop blocking mouse events (but still
// block this one)
m_blockEventsToPopup = false;
event.Skip(false);
// Also, this button press was (probably) used to display
// the popup, so relay it back to the drop-down button
// (which supposedly originated it). This is necessary to
// refresh it properly.
relayToButton = true;
}
}
else if ( m_blockEventsToPopup )
{
event.Skip(false);
}
}
//
// Some mouse events to popup that happen outside it, before cursor
// has been inside the popup, need to be ignored by it but relayed to
// the dropbutton.
//
if ( evtType == wxEVT_LEFT_UP )
{
if ( !IsPopupShown() )
{
event.Skip(false);
relayToButton = true;
}
else if ( !isInside && !m_beenInsidePopup )
{
// Popup is shown but the cursor is not inside, nor it has been
relayToButton = true;
}
}
if ( relayToButton )
{
wxWindow* btn = GetButton();
if ( btn )
btn->GetEventHandler()->ProcessEvent(event);
else
// Bypass the event handling mechanism. Using it would be
// confusing for the platform-specific wxComboCtrl
// implementations.
HandleButtonMouseEvent(event, 0);
}
}
// ----------------------------------------------------------------------------
// wxComboCtrlTextCtrl
// ----------------------------------------------------------------------------
class wxComboCtrlTextCtrl : public wxTextCtrl
{
public:
wxComboCtrlTextCtrl() : wxTextCtrl() { }
virtual ~wxComboCtrlTextCtrl() { }
virtual wxWindow *GetMainWindowOfCompositeControl() wxOVERRIDE
{
wxComboCtrl* combo = (wxComboCtrl*) GetParent();
// Returning this instead of just 'parent' lets FindFocus work
// correctly even when parent control is a child of a composite
// generic control (as is case with wxGenericDatePickerCtrl).
return combo->GetMainWindowOfCompositeControl();
}
};
// ----------------------------------------------------------------------------
// wxComboCtrlBase
// ----------------------------------------------------------------------------
wxBEGIN_EVENT_TABLE(wxComboCtrlBase, wxControl)
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_CHAR(wxComboCtrlBase::OnCharEvent)
EVT_SYS_COLOUR_CHANGED(wxComboCtrlBase::OnSysColourChanged)
wxEND_EVENT_TABLE()
wxIMPLEMENT_ABSTRACT_CLASS(wxComboCtrlBase, wxControl);
void wxComboCtrlBase::Init()
{
m_winPopup = NULL;
m_popup = NULL;
m_popupWinState = Hidden;
m_btn = NULL;
m_text = NULL;
m_popupInterface = NULL;
#if !wxUSE_POPUPWIN
m_toplevEvtHandler = 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_btnWid = m_btnHei = -1;
m_btnSide = wxRIGHT;
m_btnSpacingX = 0;
m_extLeft = 0;
m_extRight = 0;
m_marginLeft = -1;
m_iFlags = 0;
m_textCtrlStyle = 0;
m_timeCanAcceptClick = 0;
m_resetFocus = false;
m_hasTcBgCol = false;
m_beenInsidePopup = false;
// Let's make it so that the popup control will not receive mouse
// events until mouse left button has been up.
m_blockEventsToPopup = true;
}
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_marginLeft = GetNativeTextIndent();
m_iFlags |= wxCC_IFLAG_CREATED;
// If x and y indicate valid size, wxSizeEvent won't be
// emitted automatically, so we need to add artificial one.
if ( size.x > 0 && size.y > 0 )
{
wxSizeEvent evt(size,GetId());
evt.SetEventObject(this);
GetEventHandler()->AddPendingEvent(evt);
}
return true;
}
void
wxComboCtrlBase::CreateTextCtrl(int style)
{
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 | m_textCtrlStyle;
if ( HasFlag(wxTE_PROCESS_ENTER) )
style |= wxTE_PROCESS_ENTER;
m_text = new wxComboCtrlTextCtrl();
m_text->Create(this, wxID_ANY, m_valueString,
wxDefaultPosition, wxSize(10,-1),
style);
// Connecting the events is currently the most reliable way
m_text->Bind(wxEVT_TEXT, &wxComboCtrlBase::OnTextCtrlEvent, this);
if ( style & wxTE_PROCESS_ENTER )
{
m_text->Bind(wxEVT_TEXT_ENTER, &wxComboCtrlBase::OnTextCtrlEvent, this);
}
m_text->Bind(wxEVT_SET_FOCUS, &wxComboCtrlBase::OnTextFocus, this);
m_text->Bind(wxEVT_KILL_FOCUS, &wxComboCtrlBase::OnTextFocus, this);
m_text->Bind(wxEVT_KEY_DOWN, &wxComboCtrlBase::OnTextKey, this);
m_text->Bind(wxEVT_CHAR, &wxComboCtrlBase::OnTextKey, this);
m_text->Bind(wxEVT_KEY_UP, &wxComboCtrlBase::OnTextKey, this);
m_text->SetHint(m_hintText);
}
}
void wxComboCtrlBase::OnThemeChange()
{
// Because wxComboCtrl has transparent parts on most platforms, we
// don't want to touch the actual background colour. Instead, we just
// usually re-obtain m_tcBgCol here.
#if defined(__WXMSW__) || defined(__WXGTK__)
wxVisualAttributes vattrs = wxComboBox::GetClassDefaultAttributes();
#else
wxVisualAttributes vattrs;
vattrs.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
vattrs.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
#endif
if ( !m_hasTcBgCol )
m_tcBgCol = vattrs.colBg;
#ifndef __WXMAC__
// Only change the colours if application has not specified
// custom ones.
if ( !m_hasFgCol )
{
SetOwnForegroundColour(vattrs.colFg);
}
if ( !HasTransparentBackground() )
{
SetOwnBackgroundColour(GetParent()->GetBackgroundColour());
}
#endif // !__WXMAC__
}
wxComboCtrlBase::~wxComboCtrlBase()
{
if ( HasCapture() )
ReleaseMouse();
#if !wxUSE_POPUPWIN
delete ((wxComboFrameEventHandler*)m_toplevEvtHandler);
m_toplevEvtHandler = NULL;
#endif
DestroyPopup();
}
// ----------------------------------------------------------------------------
// 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.IsOk() && m_blankButtonBg) ) &&
m_btnSpacingX == 0 &&
m_btnHei <= 0 )
{
m_iFlags |= wxCC_IFLAG_BUTTON_OUTSIDE;
btnBorder = 0;
}
else if ( (m_iFlags & wxCC_BUTTON_COVERS_BORDER) &&
m_btnSpacingX == 0 && !m_bmpNormal.IsOk() )
{
m_iFlags &= ~(wxCC_IFLAG_BUTTON_OUTSIDE);
btnBorder = 0;
}
else
{
m_iFlags &= ~(wxCC_IFLAG_BUTTON_OUTSIDE);
btnBorder = customBorder;
}
// Defaul indentation
if ( m_marginLeft < 0 )
m_marginLeft = 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 > FromDIP(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.IsOk() )
{
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);
if ( m_bmpNormal.IsOk() || m_btnArea.width != butWidth || m_btnArea.height != butHeight )
m_iFlags |= wxCC_IFLAG_HAS_NONSTANDARD_BUTTON;
else
m_iFlags &= ~wxCC_IFLAG_HAS_NONSTANDARD_BUTTON;
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;
m_tcArea.y = customBorder + FOCUS_RING;
m_tcArea.width = sz.x - butAreaWid - (customBorder*2) - FOCUS_RING;
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;
wxSize sz = GetClientSize();
int customBorder = m_widthCustomBorder;
if ( (m_text->GetWindowStyleFlag() & wxBORDER_MASK) == wxNO_BORDER )
{
int x;
if ( !m_widthCustomPaint )
{
// No special custom paint area - we can use 0 left margin
// with wxTextCtrl.
if ( m_text->SetMargins(0) )
textCtrlXAdjust = 0;
x = m_tcArea.x + m_marginLeft + textCtrlXAdjust;
}
else
{
// There is special custom paint area - it is better to
// use some margin with the wxTextCtrl.
m_text->SetMargins(m_marginLeft);
x = m_tcArea.x + m_widthCustomPaint +
m_marginLeft + textCtrlXAdjust;
}
// Centre textctrl vertically
int tcSizeY = m_text->GetBestSize().y;
int diff0 = sz.y - tcSizeY;
int y = textCtrlYAdjust + (diff0/2);
if ( y < customBorder )
y = customBorder;
m_text->SetSize(x,
y,
m_tcArea.width - m_tcArea.x - x,
-1 );
// Make sure textctrl doesn't exceed the bottom custom border
wxSize tsz = m_text->GetSize();
int diff1 = (y + tsz.y) - (sz.y - customBorder);
if ( diff1 >= 0 )
{
tsz.y = tsz.y - diff1 - 1;
m_text->SetSize(tsz);
}
}
else
{
// If it has border, have textctrl fill the entire text field.
int w = m_tcArea.width - m_widthCustomPaint;
if (w < 0) w = 0;
m_text->SetSize( m_tcArea.x + m_widthCustomPaint,
m_tcArea.y,
w,
m_tcArea.height );
}
}
wxSize wxComboCtrlBase::DoGetBestSize() const
{
int width = m_text ? m_text->GetBestSize().x : FromDIP(80);
return GetSizeFromTextSize(width);
}
wxSize wxComboCtrlBase::DoGetSizeFromTextSize(int xlen, int ylen) const
{
// Calculate close-to-native control height
int fhei;
#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
fhei = EDIT_HEIGHT_FROM_CHAR_HEIGHT(GetCharHeight());
#elif defined(__WXGTK__) && !defined(__WXUNIVERSAL__)
// Control creation is not entirely cheap, so cache the heights to
// avoid repeatedly creating dummy controls:
static wxString s_last_font;
static int s_last_fhei = -1;
wxString fontdesc;
if ( m_font.IsOk() )
fontdesc = m_font.GetNativeFontInfoDesc();
if ( s_last_fhei != -1 && fontdesc == s_last_font )
{
fhei = s_last_fhei;
}
else
{
wxComboBox* cb = new wxComboBox;
#ifndef __WXGTK3__
// GTK3 returns zero for the preferred size of a hidden widget
cb->Hide();
#endif
cb->Create(const_cast<wxComboCtrlBase*>(this), wxID_ANY);
if ( m_font.IsOk() )
cb->SetFont(m_font);
s_last_font = fontdesc;
s_last_fhei = fhei = cb->GetBestSize().y;
cb->Destroy();
}
#else
if ( m_font.IsOk() )
fhei = (m_font.GetPointSize()*2) + 5;
else if ( wxNORMAL_FONT->IsOk() )
fhei = (wxNORMAL_FONT->GetPointSize()*2) + 5;
else
fhei = 22;
#endif // only for wxComboBox on MSW or GTK
// Need to force height to accommodate bitmap?
int btnSizeY = m_btnSize.y;
if ( m_bmpNormal.IsOk() && 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;
*/
#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;
// Calculate width
int fwid = GetNativeTextIndent() + xlen + FOCUS_RING + COMBO_MARGIN + m_btnArea.width;
// Add the margins we have previously set
wxPoint marg( GetMargins() );
fwid += wxMax(0, marg.x);
fhei += wxMax(0, marg.y);
if ( ylen > 0 )
fhei += ylen - GetCharHeight();
return wxSize(fwid, fhei);
}
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);
Refresh();
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 )
{
// Without hiding the wxTextCtrl there would be some
// visible 'flicker' (at least on Windows XP).
m_text->Hide();
m_text->SetFont(font);
OnResize();
m_text->Show();
}
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( NULL );
if ( m_btn ) m_btn->SetToolTip( NULL );
}
}
#endif // wxUSE_TOOLTIPS
bool wxComboCtrlBase::SetForegroundColour(const wxColour& colour)
{
if ( wxControl::SetForegroundColour(colour) )
{
if ( m_text )
m_text->SetForegroundColour(colour);
return true;
}
return false;
}
bool wxComboCtrlBase::SetBackgroundColour(const wxColour& colour)
{
if ( m_text )
m_text->SetBackgroundColour(colour);
m_tcBgCol = colour;
m_hasTcBgCol = true;
return true;
}
wxColour wxComboCtrlBase::GetBackgroundColour() const
{
if ( m_text )
return m_text->GetBackgroundColour();
return m_tcBgCol;
}
// ----------------------------------------------------------------------------
// 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 doDrawFocusRect; // 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();
doDrawFocusRect = ShouldDrawFocus() && !(m_iFlags & wxCC_FULL_BUTTON);
// 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
doDrawFocusRect = (flags & wxCONTROL_SELECTED) != 0;
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;
wxColour fgCol;
bool doDrawSelRect = true;
// Determine foreground colour
if ( isEnabled )
{
if ( doDrawFocusRect )
{
fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
}
else if ( m_hasFgCol )
{
// Honour the custom foreground colour
fgCol = GetForegroundColour();
}
else
{
fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
}
}
else
{
fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
}
// Determine background colour
if ( isEnabled )
{
if ( doDrawFocusRect )
{
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
}
else if ( m_hasTcBgCol )
{
// Honour the custom background colour
bgCol = m_tcBgCol;
}
else
{
#ifndef __WXMAC__ // see note in OnThemeChange
doDrawSelRect = false;
bgCol = GetBackgroundColour();
#else
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
#endif
}
}
else
{
#ifndef __WXMAC__ // see note in OnThemeChange
bgCol = GetBackgroundColour();
#else
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
#endif
}
dc.SetTextForeground( fgCol );
dc.SetBrush( bgCol );
if ( doDrawSelRect )
{
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 flags )
{
int drawState = m_btnState;
if ( (m_iFlags & wxCC_BUTTON_STAYS_DOWN) &&
GetPopupWindowState() >= Animating )
drawState |= wxCONTROL_PRESSED;
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;
// Need to clear button background even if m_btn is present
// and also when using custom bitmap for the button
if ( (flags & Button_PaintBackground) &&
(!HasTransparentBackground() ||
!(m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE)) )
{
wxColour bgCol;
if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
bgCol = GetParent()->GetBackgroundColour();
else
bgCol = GetBackgroundColour();
dc.SetBrush(bgCol);
dc.SetPen(bgCol);
dc.DrawRectangle(rect);
}
if ( !m_bmpNormal.IsOk() )
{
if ( flags & Button_BitmapOnly )
return;
// Draw standard button
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 ( !(flags & Button_BitmapOnly) )
{
wxRendererNative::Get().DrawPushButton(this,
dc,
drawRect,
drawState);
}
}
// 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());
evt.SetEventObject(this);
GetEventHandler()->ProcessEvent(evt);
Refresh();
}
}
// ----------------------------------------------------------------------------
// miscellaneous event handlers
// ----------------------------------------------------------------------------
void wxComboCtrlBase::OnTextCtrlEvent(wxCommandEvent& event)
{
// Avoid infinite recursion
if ( event.GetEventObject() == this )
{
event.Skip();
return;
}
if ( event.GetEventType() == wxEVT_TEXT )
{
if ( m_ignoreEvtText > 0 )
{
m_ignoreEvtText--;
return;
}
}
// For safety, completely re-create a new wxCommandEvent
wxCommandEvent evt2(event);
evt2.SetId(GetId());
evt2.SetEventObject(this);
HandleWindowEvent(evt2);
event.StopPropagation();
}
// 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) &&
IsPopupWindowState(Hidden) )
{
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;
// Never have 'hot' state when popup is being shown
// (this is mostly needed because of the animation).
if ( !IsPopupWindowState(Hidden) )
m_btnState &= ~wxCONTROL_CURRENT;
return true;
}
// returns true if event was consumed or filtered
bool wxComboCtrlBase::PreprocessMouseEvent( wxMouseEvent& event,
int WXUNUSED(flags) )
{
wxMilliClock_t t = ::wxGetLocalTimeMillis();
int evtType = event.GetEventType();
#if !wxUSE_POPUPWIN
if ( IsPopupWindowState(Visible) &&
( evtType == wxEVT_LEFT_DOWN || evtType == wxEVT_RIGHT_DOWN ) )
{
HidePopup(true);
return true;
}
#endif
// Filter out clicks on button immediately after popup dismiss
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 !wxUSE_POPUPWIN
// Click here always hides the popup.
HidePopup(true);
#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 ( evtType == wxEVT_MOUSEWHEEL )
{
if ( IsPopupShown() )
{
// relay (some) mouse events to the popup
m_popup->GetEventHandler()->ProcessEvent(event);
}
else if ( event.GetWheelAxis() == 0 &&
event.GetWheelRotation() != 0 &&
event.GetModifiers() == 0 )
{
// Translate mousewheel actions into key up/down. This is
// the simplest way of getting native behaviour: scrolling the
// wheel moves selection up/down by one item.
wxKeyEvent kevent(wxEVT_KEY_DOWN);
kevent.m_keyCode = event.GetWheelRotation() > 0
? WXK_UP
: WXK_DOWN;
if (!GetEventHandler()->ProcessEvent(kevent))
event.Skip();
}
else
{
event.Skip();
}
}
else if ( evtType )
{
event.Skip();
}
}
void wxComboCtrlBase::OnKeyEvent(wxKeyEvent& event)
{
if ( IsPopupShown() )
{
// pass it to the popped up control
GetPopupControl()->GetControl()->GetEventHandler()->ProcessEvent(event);
}
else // no popup
{
wxWindow* mainCtrl = GetMainWindowOfCompositeControl();
if ( mainCtrl->GetParent()->HasFlag(wxTAB_TRAVERSAL) )
{
if ( mainCtrl->HandleAsNavigationKey(event) )
return;
}
if ( IsKeyPopupToggle(event) )
{
OnButtonClick();
return;
}
int comboStyle = GetWindowStyle();
wxComboPopup* popupInterface = GetPopupControl();
if ( !popupInterface )
{
event.Skip();
return;
}
int keycode = event.GetKeyCode();
if ( (comboStyle & wxCB_READONLY) ||
(keycode != WXK_RIGHT && keycode != WXK_LEFT) )
{
popupInterface->OnComboKeyEvent(event);
}
else
event.Skip();
}
}
void wxComboCtrlBase::OnCharEvent(wxKeyEvent& event)
{
if ( IsPopupShown() )
{
// pass it to the popped up control
GetPopupControl()->GetControl()->GetEventHandler()->ProcessEvent(event);
}
else // no popup
{
wxComboPopup* popupInterface = GetPopupControl();
if ( popupInterface )
{
popupInterface->OnComboCharEvent(event);
}
else
{
event.Skip();
}
}
}
void wxComboCtrlBase::OnFocusEvent( wxFocusEvent& event )
{
// Always let default handling of focus events to take place.
event.Skip();
// On Mac, setting focus here led to infinite recursion so
// m_resetFocus is used as a guard
if ( event.GetEventType() == wxEVT_SET_FOCUS )
{
if ( !m_resetFocus && GetTextCtrl() && !GetTextCtrl()->HasFocus() )
{
m_resetFocus = true;
GetTextCtrl()->SetFocus();
m_resetFocus = false;
}
}
Refresh();
}
void wxComboCtrlBase::OnIdleEvent( wxIdleEvent& WXUNUSED(event) )
{
if ( m_resetFocus )
{
m_resetFocus = false;
if ( GetTextCtrl() )
GetTextCtrl()->SetFocus();
}
}
void wxComboCtrlBase::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event))
{
OnThemeChange();
// left margin may also have changed
if ( !(m_iFlags & wxCC_IFLAG_LEFT_MARGIN_SET) )
m_marginLeft = GetNativeTextIndent();
RecalcAndRefresh();
}
// ----------------------------------------------------------------------------
// popup handling
// ----------------------------------------------------------------------------
// Create popup window and the child control
void wxComboCtrlBase::CreatePopup()
{
wxComboPopup* popupInterface = m_popupInterface;
if ( !m_winPopup )
{
m_winPopup = new wxComboPopupWindow( this, wxNO_BORDER );
m_winPopup->Bind(wxEVT_KEY_DOWN, &wxComboCtrlBase::OnPopupKey, this);
m_winPopup->Bind(wxEVT_CHAR, &wxComboCtrlBase::OnPopupKey, this);
m_winPopup->Bind(wxEVT_KEY_UP, &wxComboCtrlBase::OnPopupKey, this);
#if !wxUSE_POPUPWIN
m_winPopup->Bind(wxEVT_ACTIVATE, &wxComboCtrlBase::OnPopupActivate, this);
#endif
m_winPopup->Bind(wxEVT_SIZE, &wxComboCtrlBase::OnPopupSize, this);
}
popupInterface->Create(m_winPopup);
m_popup = popupInterface->GetControl();
// Bind all mouse events, as used to be done by EVT_MOUSE_EVENTS() event
// table macro, to this handler.
const wxEventTypeTag<wxMouseEvent> allMouseEventTypes[] =
{
wxEVT_LEFT_DOWN,
wxEVT_LEFT_UP,
wxEVT_LEFT_DCLICK,
wxEVT_MIDDLE_DOWN,
wxEVT_MIDDLE_UP,
wxEVT_MIDDLE_DCLICK,
wxEVT_RIGHT_DOWN,
wxEVT_RIGHT_UP,
wxEVT_RIGHT_DCLICK,
wxEVT_AUX1_DOWN,
wxEVT_AUX1_UP,
wxEVT_AUX1_DCLICK,
wxEVT_AUX2_DOWN,
wxEVT_AUX2_UP,
wxEVT_AUX2_DCLICK,
wxEVT_MOTION,
wxEVT_LEAVE_WINDOW,
wxEVT_ENTER_WINDOW,
wxEVT_MOUSEWHEEL,
wxEVT_MAGNIFY,
};
for ( size_t n = 0; n < WXSIZEOF(allMouseEventTypes); ++n )
{
m_popup->Bind(allMouseEventTypes[n],
&wxComboCtrlBase::OnPopupMouseEvent, this);
}
// 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(true);
if ( m_popupInterface )
{
// NB: DestroyPopup() performs 'delete this'.
m_popupInterface->DestroyPopup();
m_popupInterface = NULL;
}
if ( m_winPopup )
{
m_winPopup->Destroy();
m_winPopup = NULL;
}
m_popup = 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 = NULL;
}
// This must be done after creation
if ( !m_valueString.empty() )
{
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
switch ( GetPopupWindowState() )
{
case Hidden:
{
Popup();
break;
}
case Animating:
case Visible:
{
HidePopup(true);
break;
}
}
}
void wxComboCtrlBase::Popup()
{
wxCommandEvent event(wxEVT_COMBOBOX_DROPDOWN, GetId());
event.SetEventObject(this);
HandleWindowEvent(event);
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();
wxRect displayRect = wxDisplay(this).GetGeometry();
screenHeight = displayRect.GetHeight();
scrPos = GetScreenPosition();
spaceAbove = scrPos.y - displayRect.GetY();
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.
wxWindow* mainCtrl = GetMainWindowOfCompositeControl();
wxWindow* parent = mainCtrl->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 = displayRect.GetWidth();
// 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 !wxUSE_POPUPWIN
// Put top level window event handler into place
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 );
}
}
#ifdef __WXMAC__
bool wxComboCtrlBase::AnimateShow( const wxRect& rect, int WXUNUSED(flags) )
{
// Overridden AnimateShow() will call Raise() and ShowWithEffect() so do
// here to avoid duplication. Raise and Show are needed for some contained
// control's scrollbars, selection highlights, hit-test accuracy and popup
// close via left mousedown when the mouse is not over the parent app.
if ( GetPopupWindow() )
{
GetPopupWindow()->SetSize(rect);
GetPopupWindow()->Raise();
GetPopupWindow()->Show();
}
return true;
}
#else
bool wxComboCtrlBase::AnimateShow( const wxRect& WXUNUSED(rect), int WXUNUSED(flags) )
{
return true;
}
#endif
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 );
#if wxUSE_POPUPWIN
((wxPopupTransientWindow*)winPopup)->Popup(m_popup);
#else
winPopup->Show();
#if !defined(__WXX11__)
m_popup->SetFocus();
#endif
#endif
m_popupWinState = Visible;
}
else if ( IsPopupWindowState(Hidden) )
{
// Animation was aborted
wxASSERT( !winPopup->IsShown() );
m_popupWinState = Hidden;
}
Refresh();
}
void wxComboCtrlBase::OnPopupDismiss(bool generateEvent)
{
// 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();
// Reset popup-related flags.
m_beenInsidePopup = false;
m_blockEventsToPopup = true;
#if !wxUSE_POPUPWIN
// Remove top level window event handler
if ( m_toplevEvtHandler )
{
wxWindow* toplev = ::wxGetTopLevelParent( this );
if ( toplev )
toplev->RemoveEventHandler( m_toplevEvtHandler );
}
#endif
m_timeCanAcceptClick = ::wxGetLocalTimeMillis();
#if wxUSE_POPUPWIN
m_timeCanAcceptClick += 150;
#endif
// 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();
#if !defined(__WXX11__)
SetFocus();
#endif
if ( generateEvent )
{
wxCommandEvent event(wxEVT_COMBOBOX_CLOSEUP, GetId());
event.SetEventObject(this);
HandleWindowEvent(event);
}
}
void wxComboCtrlBase::HidePopup(bool generateEvent)
{
// Should be able to call this without popup interface
if ( IsPopupWindowState(Hidden) || IsPopupWindowState(Closing) )
return;
m_popupWinState = Closing; // To prevent from reentering
// transfer value and show it in textctrl, if any
if ( !IsPopupWindowState(Animating) )
SetValueByUser( m_popupInterface->GetStringValue() );
m_winPopup->Hide();
OnPopupDismiss(generateEvent);
}
// ----------------------------------------------------------------------------
// 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;
if ( width > 0 || height > 0 || spacingX )
m_iFlags |= wxCC_IFLAG_HAS_NONSTANDARD_BUTTON;
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.IsOk() )
m_bmpPressed = bmpPressed;
else
m_bmpPressed = bmpNormal;
if ( bmpHover.IsOk() )
m_bmpHover = bmpHover;
else
m_bmpHover = bmpNormal;
if ( bmpDisabled.IsOk() )
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();
}
bool wxComboCtrlBase::DoSetMargins(const wxPoint& margins)
{
// For general sanity's sake, we ignore top margin. Instead
// we will always try to center the text vertically.
bool res = true;
if ( margins.x != -1 )
{
m_marginLeft = margins.x;
m_iFlags |= wxCC_IFLAG_LEFT_MARGIN_SET;
}
else
{
m_marginLeft = GetNativeTextIndent();
m_iFlags &= ~(wxCC_IFLAG_LEFT_MARGIN_SET);
}
if ( margins.y != -1 )
{
res = false;
}
RecalcAndRefresh();
return res;
}
wxPoint wxComboCtrlBase::DoGetMargins() const
{
return wxPoint(m_marginLeft, -1);
}
#if WXWIN_COMPATIBILITY_2_8
void wxComboCtrlBase::SetTextIndent( int indent )
{
if ( indent < 0 )
{
m_marginLeft = GetNativeTextIndent();
m_iFlags &= ~(wxCC_IFLAG_LEFT_MARGIN_SET);
}
else
{
m_marginLeft = indent;
m_iFlags |= wxCC_IFLAG_LEFT_MARGIN_SET;
}
RecalcAndRefresh();
}
wxCoord wxComboCtrlBase::GetTextIndent() const
{
return m_marginLeft;
}
#endif
wxCoord wxComboCtrlBase::GetNativeTextIndent() const
{
return DEFAULT_TEXT_INDENT;
}
void wxComboCtrlBase::SetTextCtrlStyle( int style )
{
m_textCtrlStyle = style;
if ( m_text )
m_text->SetWindowStyle(style);
}
// ----------------------------------------------------------------------------
// wxTextEntry interface
// ----------------------------------------------------------------------------
wxString wxComboCtrlBase::DoGetValue() const
{
if ( m_text )
return m_text->GetValue();
return m_valueString;
}
void wxComboCtrlBase::SetValueWithEvent(const wxString& value,
bool withEvent)
{
DoSetValue(value, withEvent ? SetValue_SendEvent : 0);
}
void wxComboCtrlBase::OnSetValue(const wxString& value)
{
// Note: before wxComboCtrl inherited from wxTextEntry,
// this code used to be in SetValueWithEvent().
// 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 )
{
bool found = true;
wxString trueValue = value;
// Conform to wxComboBox behaviour: read-only control can only accept
// valid list items and empty string
if ( m_popupInterface && HasFlag(wxCB_READONLY) && value.length() )
{
found = m_popupInterface->FindItem(value,
&trueValue);
}
if ( found )
{
m_valueString = trueValue;
EnsurePopupControl();
if ( m_popupInterface )
m_popupInterface->SetStringValue(trueValue);
}
}
Refresh();
}
void wxComboCtrlBase::SetValueByUser(const wxString& value)
{
// NB: Order of function calls is important here. Otherwise
// the SelectAll() may not work.
if ( m_text )
{
m_text->SetValue(value);
if ( !(m_iFlags & wxCC_NO_TEXT_AUTO_SELECT) )
m_text->SelectAll();
}
OnSetValue(value);
}
// 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);
}
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::WriteText(const wxString& text)
{
if ( m_text )
{
m_text->WriteText(text);
OnSetValue(m_text->GetValue());
}
else
{
OnSetValue(text);
}
}
void wxComboCtrlBase::DoSetValue(const wxString& value, int flags)
{
if ( m_text )
{
if ( flags & SetValue_SendEvent )
m_text->SetValue(value);
else
m_text->ChangeValue(value);
}
OnSetValue(value);
}
void wxComboCtrlBase::Replace(long from, long to, const wxString& value)
{
if ( m_text )
{
m_text->Replace(from, to, value);
OnSetValue(m_text->GetValue());
}
}
void wxComboCtrlBase::Remove(long from, long to)
{
if ( m_text )
{
m_text->Remove(from, to);
OnSetValue(m_text->GetValue());
}
}
void wxComboCtrlBase::SetSelection(long from, long to)
{
if ( m_text )
m_text->SetSelection(from, to);
}
void wxComboCtrlBase::GetSelection(long *from, long *to) const
{
if ( m_text )
{
m_text->GetSelection(from, to);
}
else
{
*from = 0;
*to = 0;
}
}
bool wxComboCtrlBase::IsEditable() const
{
if ( m_text )
return m_text->IsEditable();
return false;
}
void wxComboCtrlBase::SetEditable(bool editable)
{
if ( m_text )
m_text->SetEditable(editable);
}
void wxComboCtrlBase::Undo()
{
if ( m_text )
m_text->Undo();
}
void wxComboCtrlBase::Redo()
{
if ( m_text )
m_text->Redo();
}
bool wxComboCtrlBase::CanUndo() const
{
if ( m_text )
return m_text->CanUndo();
return false;
}
bool wxComboCtrlBase::CanRedo() const
{
if ( m_text )
return m_text->CanRedo();
return false;
}
bool wxComboCtrlBase::SetHint(const wxString& hint)
{
m_hintText = hint;
bool res = true;
if ( m_text )
res = m_text->SetHint(hint);
Refresh();
return res;
}
wxString wxComboCtrlBase::GetHint() const
{
return m_hintText;
}
#endif // wxUSE_COMBOCTRL