This style never did anything useful in wxMSW and did nothing at all in all the other ports, so always was rather useless, but became actively harmful not that WS_EX_COMPOSITED is used in wxMSW because using it and WS_EX_TRANSPARENT, enabled by wxTRANSPARENT_WINDOW, together doesn't work and results in an endless stream of WM_PAINT messages being sent to the _siblings_ of the window with wxTRANSPARENT_WINDOW style.
2735 lines
72 KiB
C++
2735 lines
72 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(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
|
|
|
|
#include "wx/gtk/private.h"
|
|
|
|
#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 ) override;
|
|
virtual bool ProcessLeftDown(wxMouseEvent& event) override;
|
|
protected:
|
|
virtual void OnDismiss() override;
|
|
#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);
|
|
}
|
|
}
|
|
|
|
void wxComboCtrlBase::WXHandleDPIChanged(wxDPIChangedEvent& event)
|
|
{
|
|
// Ensure it is really recalculated.
|
|
m_btnSize = wxDefaultSize;
|
|
// And calculate it again
|
|
m_btnSize = GetButtonSize();
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxComboCtrlTextCtrl
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class wxComboCtrlTextCtrl : public wxTextCtrl
|
|
{
|
|
public:
|
|
wxComboCtrlTextCtrl() : wxTextCtrl() { }
|
|
virtual ~wxComboCtrlTextCtrl() { }
|
|
|
|
virtual wxWindow *GetMainWindowOfCompositeControl() override
|
|
{
|
|
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 = nullptr;
|
|
m_popup = nullptr;
|
|
m_popupWinState = Hidden;
|
|
m_btn = nullptr;
|
|
m_text = nullptr;
|
|
m_mainWindow = nullptr;
|
|
m_popupInterface = nullptr;
|
|
|
|
#if !wxUSE_POPUPWIN
|
|
m_toplevEvtHandler = nullptr;
|
|
#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;
|
|
|
|
Bind(wxEVT_DPI_CHANGED, &wxComboCtrlBase::WXHandleDPIChanged, this);
|
|
}
|
|
|
|
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::SetMainControl(wxWindow* win)
|
|
{
|
|
// We can't have both a custom window and a text control, so get rid of the
|
|
// latter if we have it.
|
|
if ( m_text )
|
|
{
|
|
m_text->Destroy();
|
|
|
|
// Note that we currently always set it to nullptr, even if the custom
|
|
// window is a (subclass of) wxTextCtrl because our m_text must be a
|
|
// wxComboCtrlTextCtrl for things to work correctly.
|
|
m_text = nullptr;
|
|
}
|
|
|
|
// We don't do anything with the previous main window, if any, it's the
|
|
// caller's responsibility to delete or hide it, as needed.
|
|
m_mainWindow = win;
|
|
}
|
|
|
|
void
|
|
wxComboCtrlBase::CreateTextCtrl(int style)
|
|
{
|
|
// If we're using a custom main window explicitly set using
|
|
// SetMainControl(), don't recreate it and just keep using it.
|
|
if ( m_mainWindow && !m_text )
|
|
return;
|
|
|
|
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_mainWindow = m_text;
|
|
|
|
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 = nullptr;
|
|
#endif
|
|
|
|
DestroyPopup();
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// geometry stuff
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Recalculates button and textctrl areas
|
|
void wxComboCtrlBase::CalculateAreas( int btnWidth )
|
|
{
|
|
wxSize sz = GetClientSize();
|
|
int btnBorder; // border for button only
|
|
|
|
// check if button should really be outside the border: we'll do 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 = m_widthCustomBorder;
|
|
}
|
|
|
|
// 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() )
|
|
{
|
|
wxSize bmpReqSize = m_bmpNormal.GetPreferredLogicalSizeFor(this);
|
|
int bmpReqWidth = bmpReqSize.GetWidth();
|
|
int bmpReqHeight = bmpReqSize.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-(m_widthCustomBorder*2)) < butHeight && btnWidth == 0 )
|
|
{
|
|
int newY = butHeight+(m_widthCustomBorder*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 ) + m_widthCustomBorder;
|
|
m_tcArea.y = m_widthCustomBorder + FOCUS_RING;
|
|
m_tcArea.width = sz.x - butAreaWid - (m_widthCustomBorder*2) - FOCUS_RING;
|
|
m_tcArea.height = sz.y - ((m_widthCustomBorder+FOCUS_RING)*2);
|
|
|
|
/*
|
|
if ( m_mainWindow )
|
|
{
|
|
::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_mainWindow || !m_mainWindow->GetHandle() )
|
|
return;
|
|
|
|
wxSize sz = GetClientSize();
|
|
|
|
// This function actually positions any main window, not just a text
|
|
// control, but it only does any special adjustments for m_text.
|
|
if ( m_text && (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 < m_widthCustomBorder )
|
|
y = m_widthCustomBorder;
|
|
|
|
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 - m_widthCustomBorder);
|
|
if ( diff1 >= 0 )
|
|
{
|
|
tsz.y = tsz.y - diff1 - 1;
|
|
m_text->SetSize(tsz);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If the main window has border or is not a text control at all, have
|
|
// it fill the entire available space.
|
|
int w = m_tcArea.width - m_widthCustomPaint;
|
|
if (w < 0) w = 0;
|
|
m_mainWindow->SetSize( m_tcArea.x + m_widthCustomPaint,
|
|
m_tcArea.y,
|
|
w,
|
|
m_tcArea.height );
|
|
}
|
|
}
|
|
|
|
wxSize wxComboCtrlBase::DoGetBestSize() const
|
|
{
|
|
int width = m_mainWindow && m_mainWindow->GetHandle()
|
|
? m_mainWindow->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_mainWindow )
|
|
m_mainWindow->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_mainWindow)
|
|
m_mainWindow->Show(show);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxComboCtrlBase::SetFont ( const wxFont& font )
|
|
{
|
|
if ( !wxControl::SetFont(font) )
|
|
return false;
|
|
|
|
if ( m_mainWindow )
|
|
{
|
|
// Without hiding the wxTextCtrl there would be some
|
|
// visible 'flicker' (at least on Windows XP).
|
|
m_mainWindow->Hide();
|
|
m_mainWindow->SetFont(font);
|
|
OnResize();
|
|
m_mainWindow->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_mainWindow ) m_mainWindow->SetToolTip(tip);
|
|
if ( m_btn ) m_btn->SetToolTip(tip);
|
|
}
|
|
else
|
|
{
|
|
if ( m_mainWindow ) m_mainWindow->SetToolTip( nullptr );
|
|
if ( m_btn ) m_btn->SetToolTip( nullptr );
|
|
}
|
|
}
|
|
#endif // wxUSE_TOOLTIPS
|
|
|
|
bool wxComboCtrlBase::SetForegroundColour(const wxColour& colour)
|
|
{
|
|
if ( wxControl::SetForegroundColour(colour) )
|
|
{
|
|
if ( m_mainWindow )
|
|
m_mainWindow->SetForegroundColour(colour);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool wxComboCtrlBase::SetBackgroundColour(const wxColour& colour)
|
|
{
|
|
if ( m_mainWindow )
|
|
m_mainWindow->SetBackgroundColour(colour);
|
|
m_tcBgCol = colour;
|
|
m_hasTcBgCol = true;
|
|
return true;
|
|
}
|
|
|
|
wxColour wxComboCtrlBase::GetBackgroundColour() const
|
|
{
|
|
if ( m_mainWindow )
|
|
return m_mainWindow->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
|
|
|
|
wxBitmapBundle* 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
|
|
wxBitmap currentBmp = pBmp->GetBitmapFor(this);
|
|
dc.DrawBitmap(currentBmp,
|
|
drawRect.x + (drawRect.width-currentBmp.GetLogicalWidth())/2,
|
|
drawRect.y + (drawRect.height-currentBmp.GetLogicalHeight())/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 && m_mainWindow && !m_mainWindow->HasFocus() )
|
|
{
|
|
m_resetFocus = true;
|
|
m_mainWindow->SetFocus();
|
|
m_resetFocus = false;
|
|
}
|
|
}
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void wxComboCtrlBase::OnIdleEvent( wxIdleEvent& WXUNUSED(event) )
|
|
{
|
|
if ( m_resetFocus )
|
|
{
|
|
m_resetFocus = false;
|
|
if ( m_mainWindow )
|
|
m_mainWindow->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 = nullptr;
|
|
}
|
|
|
|
if ( m_winPopup )
|
|
{
|
|
m_winPopup->Destroy();
|
|
m_winPopup = nullptr;
|
|
}
|
|
|
|
m_popup = nullptr;
|
|
}
|
|
|
|
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 = nullptr;
|
|
}
|
|
|
|
// 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(nullptr);
|
|
}
|
|
|
|
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 not explicitly specified.
|
|
if ( retSize.x <= 0 || retSize.y <= 0)
|
|
{
|
|
OnResize();
|
|
|
|
retSize = m_btnSize;
|
|
}
|
|
|
|
return retSize;
|
|
}
|
|
|
|
void wxComboCtrlBase::SetButtonBitmaps( const wxBitmapBundle& bmpNormal,
|
|
bool blankButtonBg,
|
|
const wxBitmapBundle& bmpPressed,
|
|
const wxBitmapBundle& bmpHover,
|
|
const wxBitmapBundle& 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_mainWindow )
|
|
{
|
|
// move the main window accordingly
|
|
wxRect r = m_mainWindow->GetRect();
|
|
int inc = width - m_widthCustomPaint;
|
|
r.x += inc;
|
|
r.width -= inc;
|
|
m_mainWindow->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);
|
|
}
|
|
|
|
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
|