dc404a954e
includes propgrid, the build system for propgrid is brutal rather than fend of users trying to build it themselves it's worth it to just include it with assdraw for now. Originally committed to SVN as r2177.
3835 lines
103 KiB
C++
3835 lines
103 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: odcombo.cpp
|
|
// Purpose: wxPGOwnerDrawnComboBox and related classes implementation
|
|
// Author: Jaakko Salli
|
|
// Modified by:
|
|
// Created: Jan-25-2005
|
|
// RCS-ID: $Id:
|
|
// Copyright: (c) 2005 Jaakko Salli
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
|
|
#pragma implementation "odcombo.h"
|
|
#endif
|
|
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_COMBOBOX
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/app.h"
|
|
#include "wx/log.h"
|
|
|
|
#include "wx/button.h"
|
|
#include "wx/combobox.h"
|
|
#include "wx/textctrl.h"
|
|
#include "wx/dcclient.h"
|
|
#include "wx/settings.h"
|
|
#include "wx/dialog.h"
|
|
|
|
#endif
|
|
|
|
#include "wx/dcbuffer.h"
|
|
#include "wx/tooltip.h"
|
|
#include "wx/timer.h"
|
|
|
|
#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
|
|
#include "wx/msw/uxtheme.h"
|
|
#endif
|
|
|
|
#include "wx/propgrid/odcombo.h"
|
|
|
|
//
|
|
// THESE GO TO BASE FILE
|
|
//
|
|
|
|
#define BMP_BUTTON_MARGIN 4
|
|
|
|
//#define DEFAULT_POPUP_HEIGHT -1
|
|
#define DEFAULT_POPUP_HEIGHT 300
|
|
|
|
#define DEFAULT_TEXT_INDENT 3
|
|
|
|
|
|
#if defined(__WXMSW__)
|
|
|
|
#define ALLOW_FAKE_POPUP 0 // Use only on plats with problems with wxPopupWindow
|
|
#define USE_TRANSIENT_POPUP 1 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
|
|
|
|
//#undef wxUSE_POPUPWIN
|
|
//#define wxUSE_POPUPWIN 0
|
|
|
|
#elif defined(__WXGTK__)
|
|
|
|
// Fake popup windows cause focus problems on GTK2 (but enable on GTK1.2, just in case)
|
|
#if defined(__WXGTK20__)
|
|
#define ALLOW_FAKE_POPUP 0 // Use only on plats with problems with wxPopupWindow
|
|
#else
|
|
#define ALLOW_FAKE_POPUP 1 // Use only on plats with problems with wxPopupWindow
|
|
#endif
|
|
#define USE_TRANSIENT_POPUP 1 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
|
|
|
|
#elif defined(__WXMAC__)
|
|
|
|
#define ALLOW_FAKE_POPUP 1 // Use only on plats with problems with wxPopupWindow
|
|
#define USE_TRANSIENT_POPUP 0 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
|
|
|
|
#else
|
|
|
|
#define ALLOW_FAKE_POPUP 1 // Use only on plats with problems with wxPopupWindow
|
|
#define USE_TRANSIENT_POPUP 0 // Use wxPopupWindowTransient (preferred, if it works properly on platform)
|
|
|
|
#endif
|
|
|
|
|
|
// Popupwin is really only supported on wxMSW (not WINCE) and wxGTK, regardless
|
|
// what the wxUSE_POPUPWIN says.
|
|
#if (!defined(__WXMSW__) && !defined(__WXGTK__)) || defined(__WXWINCE__)
|
|
#undef wxUSE_POPUPWIN
|
|
#define wxUSE_POPUPWIN 0
|
|
#endif
|
|
|
|
#if wxUSE_POPUPWIN
|
|
#include "wx/popupwin.h"
|
|
#else
|
|
#undef USE_TRANSIENT_POPUP
|
|
#define USE_TRANSIENT_POPUP 0
|
|
#endif
|
|
|
|
// For versions < 2.6.2, don't enable transient popup. There may be
|
|
// problems I don't have time to test properly.
|
|
#if !wxCHECK_VERSION(2, 6, 2)
|
|
#undef USE_TRANSIENT_POPUP
|
|
#define USE_TRANSIENT_POPUP 0
|
|
#endif
|
|
|
|
|
|
#if USE_TRANSIENT_POPUP
|
|
#undef ALLOW_FAKE_POPUP
|
|
#define ALLOW_FAKE_POPUP 0
|
|
|
|
#define wxPGComboPopupWindowBase wxPopupTransientWindow
|
|
#define INSTALL_TOPLEV_HANDLER 0
|
|
|
|
#elif wxUSE_POPUPWIN
|
|
|
|
#define wxPGComboPopupWindowBase wxPopupWindow
|
|
#define INSTALL_TOPLEV_HANDLER 1
|
|
|
|
#else
|
|
|
|
#define wxPGComboPopupWindowBase wxDialog
|
|
#if !ALLOW_FAKE_POPUP
|
|
#define INSTALL_TOPLEV_HANDLER 0 // Doesn't need since can monitor active event
|
|
#else
|
|
#define INSTALL_TOPLEV_HANDLER 1
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
//
|
|
// THESE GO TO GENERIC FILE
|
|
//
|
|
|
|
// Some adjustments to make the generic more bearable
|
|
#if defined(__WXUNIVERSAL__)
|
|
|
|
#define TEXTCTRLXADJUST 0 // position adjustment for wxTextCtrl, with zero indent
|
|
#define TEXTCTRLYADJUST 0
|
|
#define TEXTXADJUST 0 // how much is read-only text's x adjusted
|
|
#define DEFAULT_DROPBUTTON_WIDTH 19
|
|
|
|
#elif defined(__WXMSW__)
|
|
|
|
#define TEXTCTRLXADJUST 2 // position adjustment for wxTextCtrl, with zero indent
|
|
#define TEXTCTRLYADJUST 3
|
|
#define TEXTXADJUST 0 // how much is read-only text's x adjusted
|
|
#define DEFAULT_DROPBUTTON_WIDTH 17
|
|
|
|
#elif defined(__WXGTK__)
|
|
|
|
#define TEXTCTRLXADJUST -1 // position adjustment for wxTextCtrl, with zero indent
|
|
#define TEXTCTRLYADJUST 0
|
|
#define TEXTXADJUST 1 // how much is read-only text's x adjusted
|
|
#define DEFAULT_DROPBUTTON_WIDTH 23
|
|
|
|
#elif defined(__WXMAC__)
|
|
|
|
#define TEXTCTRLXADJUST 0 // position adjustment for wxTextCtrl, with zero indent
|
|
#define TEXTCTRLYADJUST 0
|
|
#define TEXTXADJUST 0 // how much is read-only text's x adjusted
|
|
#define DEFAULT_DROPBUTTON_WIDTH 19
|
|
|
|
#else
|
|
|
|
#define TEXTCTRLXADJUST 0 // position adjustment for wxTextCtrl, with zero indent
|
|
#define TEXTCTRLYADJUST 0
|
|
#define TEXTXADJUST 0 // how much is read-only text's x adjusted
|
|
#define DEFAULT_DROPBUTTON_WIDTH 19
|
|
|
|
#endif
|
|
|
|
|
|
// constants
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// TO BASE
|
|
// the margin between the text control and the combo button
|
|
static const wxCoord g_comboMargin = 2;
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPGComboFrameEventHandler takes care of hiding the popup when events happen
|
|
// in its top level parent.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if INSTALL_TOPLEV_HANDLER
|
|
|
|
//
|
|
// This will no longer be necessary after wxTransientPopupWindow
|
|
// works well on all platforms.
|
|
//
|
|
|
|
class wxPGComboFrameEventHandler : public wxEvtHandler
|
|
{
|
|
public:
|
|
wxPGComboFrameEventHandler( wxPGComboControlBase* pCb );
|
|
~wxPGComboFrameEventHandler();
|
|
|
|
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;
|
|
wxPGComboControlBase* m_combo;
|
|
|
|
private:
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
BEGIN_EVENT_TABLE(wxPGComboFrameEventHandler, wxEvtHandler)
|
|
EVT_IDLE(wxPGComboFrameEventHandler::OnIdle)
|
|
EVT_LEFT_DOWN(wxPGComboFrameEventHandler::OnMouseEvent)
|
|
EVT_RIGHT_DOWN(wxPGComboFrameEventHandler::OnMouseEvent)
|
|
EVT_SIZE(wxPGComboFrameEventHandler::OnResize)
|
|
EVT_MOVE(wxPGComboFrameEventHandler::OnMove)
|
|
EVT_MENU_HIGHLIGHT(wxID_ANY,wxPGComboFrameEventHandler::OnMenuEvent)
|
|
EVT_MENU_OPEN(wxPGComboFrameEventHandler::OnMenuEvent)
|
|
EVT_ACTIVATE(wxPGComboFrameEventHandler::OnActivate)
|
|
EVT_CLOSE(wxPGComboFrameEventHandler::OnClose)
|
|
END_EVENT_TABLE()
|
|
|
|
wxPGComboFrameEventHandler::wxPGComboFrameEventHandler( wxPGComboControlBase* combo )
|
|
: wxEvtHandler()
|
|
{
|
|
m_combo = combo;
|
|
}
|
|
|
|
wxPGComboFrameEventHandler::~wxPGComboFrameEventHandler()
|
|
{
|
|
}
|
|
|
|
void wxPGComboFrameEventHandler::OnPopup()
|
|
{
|
|
m_focusStart = ::wxWindow::FindFocus();
|
|
}
|
|
|
|
void wxPGComboFrameEventHandler::OnIdle( wxIdleEvent& event )
|
|
{
|
|
wxWindow* winFocused = ::wxWindow::FindFocus();
|
|
|
|
wxWindow* popup = m_combo->GetPopupControl();
|
|
wxWindow* winpopup = m_combo->GetPopupWindow();
|
|
|
|
if (
|
|
winFocused != m_focusStart &&
|
|
winFocused != popup &&
|
|
winFocused->GetParent() != popup &&
|
|
winFocused != winpopup &&
|
|
winFocused->GetParent() != winpopup &&
|
|
winFocused != m_combo &&
|
|
winFocused != m_combo->GetButton() // GTK (atleast) requires this
|
|
)
|
|
{
|
|
m_combo->HidePopup();
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void wxPGComboFrameEventHandler::OnMenuEvent( wxMenuEvent& event )
|
|
{
|
|
m_combo->HidePopup();
|
|
event.Skip();
|
|
}
|
|
|
|
void wxPGComboFrameEventHandler::OnMouseEvent( wxMouseEvent& event )
|
|
{
|
|
m_combo->HidePopup();
|
|
event.Skip();
|
|
}
|
|
|
|
void wxPGComboFrameEventHandler::OnClose( wxCloseEvent& event )
|
|
{
|
|
m_combo->HidePopup();
|
|
event.Skip();
|
|
}
|
|
|
|
void wxPGComboFrameEventHandler::OnActivate( wxActivateEvent& event )
|
|
{
|
|
m_combo->HidePopup();
|
|
event.Skip();
|
|
}
|
|
|
|
void wxPGComboFrameEventHandler::OnResize( wxSizeEvent& event )
|
|
{
|
|
m_combo->HidePopup();
|
|
event.Skip();
|
|
}
|
|
|
|
void wxPGComboFrameEventHandler::OnMove( wxMoveEvent& event )
|
|
{
|
|
m_combo->HidePopup();
|
|
event.Skip();
|
|
}
|
|
|
|
#endif // INSTALL_TOPLEV_HANDLER
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPGComboPopupWindow is wxPopupWindow customized for
|
|
// wxComboControl.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class wxPGComboPopupWindow : public wxPGComboPopupWindowBase
|
|
{
|
|
public:
|
|
|
|
wxPGComboPopupWindow( wxPGComboControlBase *parent, int style = wxBORDER_NONE );
|
|
|
|
#if USE_TRANSIENT_POPUP
|
|
virtual bool ProcessLeftDown(wxMouseEvent& event);
|
|
#endif
|
|
|
|
void OnKeyEvent(wxKeyEvent& event);
|
|
|
|
void OnMouseEvent( wxMouseEvent& event );
|
|
#if !wxUSE_POPUPWIN
|
|
void OnActivate( wxActivateEvent& event );
|
|
#endif
|
|
|
|
protected:
|
|
|
|
#if USE_TRANSIENT_POPUP
|
|
virtual void OnDismiss();
|
|
#endif
|
|
|
|
private:
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
|
|
BEGIN_EVENT_TABLE(wxPGComboPopupWindow, wxPGComboPopupWindowBase)
|
|
EVT_MOUSE_EVENTS(wxPGComboPopupWindow::OnMouseEvent)
|
|
#if !wxUSE_POPUPWIN
|
|
EVT_ACTIVATE(wxPGComboPopupWindow::OnActivate)
|
|
#endif
|
|
EVT_KEY_DOWN(wxPGComboPopupWindow::OnKeyEvent)
|
|
EVT_KEY_UP(wxPGComboPopupWindow::OnKeyEvent)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
wxPGComboPopupWindow::wxPGComboPopupWindow( wxPGComboControlBase *parent,
|
|
int style )
|
|
#if wxUSE_POPUPWIN
|
|
: wxPGComboPopupWindowBase(parent,style)
|
|
#else
|
|
: wxPGComboPopupWindowBase(parent,
|
|
wxID_ANY,
|
|
wxEmptyString,
|
|
wxPoint(-21,-21),
|
|
wxSize(20,20),
|
|
style)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
void wxPGComboPopupWindow::OnKeyEvent( wxKeyEvent& event )
|
|
{
|
|
// Relay keyboard event to the main child controls
|
|
// (just skipping may just cause the popup to close)
|
|
wxWindowList children = GetChildren();
|
|
wxWindowList::iterator node = children.begin();
|
|
wxWindow* child = (wxWindow*)*node;
|
|
child->AddPendingEvent(event);
|
|
}
|
|
|
|
void wxPGComboPopupWindow::OnMouseEvent( wxMouseEvent& event )
|
|
{
|
|
event.Skip();
|
|
}
|
|
|
|
#if !wxUSE_POPUPWIN
|
|
void wxPGComboPopupWindow::OnActivate( wxActivateEvent& event )
|
|
{
|
|
if ( !event.GetActive() )
|
|
{
|
|
// Tell combo control that we are dismissed.
|
|
wxPGComboControl* combo = (wxPGComboControl*) GetParent();
|
|
wxASSERT( combo );
|
|
wxASSERT( combo->IsKindOf(CLASSINFO(wxPGComboControl)) );
|
|
|
|
combo->HidePopup();
|
|
|
|
event.Skip();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if USE_TRANSIENT_POPUP
|
|
bool wxPGComboPopupWindow::ProcessLeftDown(wxMouseEvent& event )
|
|
{
|
|
return wxPGComboPopupWindowBase::ProcessLeftDown(event);
|
|
}
|
|
#endif
|
|
|
|
#if USE_TRANSIENT_POPUP
|
|
// First thing that happens when a transient popup closes is that this method gets called.
|
|
void wxPGComboPopupWindow::OnDismiss()
|
|
{
|
|
wxPGComboControlBase* combo = (wxPGComboControlBase*) GetParent();
|
|
wxASSERT ( combo->IsKindOf(CLASSINFO(wxPGComboControlBase)) );
|
|
|
|
combo->OnPopupDismiss();
|
|
}
|
|
#endif
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPGComboPopup methods
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxPGComboPopup::~wxPGComboPopup()
|
|
{
|
|
}
|
|
|
|
void wxPGComboPopup::OnPopup()
|
|
{
|
|
}
|
|
|
|
void wxPGComboPopup::OnDismiss()
|
|
{
|
|
}
|
|
|
|
wxSize wxPGComboPopup::GetAdjustedSize( int minWidth,
|
|
int prefHeight,
|
|
int WXUNUSED(maxHeight) )
|
|
{
|
|
return wxSize(minWidth,prefHeight);
|
|
}
|
|
|
|
void wxPGComboPopup::PaintComboControl( wxDC& dc, const wxRect& rect )
|
|
{
|
|
if ( m_combo->GetWindowStyle() & wxCB_READONLY ) // ie. no textctrl
|
|
{
|
|
m_combo->DrawFocusBackground(dc,rect,0);
|
|
|
|
dc.DrawText( GetStringValue(),
|
|
rect.x + m_combo->GetTextIndent(),
|
|
(rect.height-dc.GetCharHeight())/2 + m_combo->m_widthCustomBorder );
|
|
}
|
|
}
|
|
|
|
void wxPGComboPopup::OnComboKeyEvent( wxKeyEvent& event )
|
|
{
|
|
event.Skip();
|
|
}
|
|
|
|
void wxPGComboPopup::OnComboDoubleClick()
|
|
{
|
|
}
|
|
|
|
void wxPGComboPopup::SetStringValue( const wxString& WXUNUSED(value) )
|
|
{
|
|
}
|
|
|
|
bool wxPGComboPopup::LazyCreate()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void wxPGComboPopup::Dismiss()
|
|
{
|
|
m_combo->HidePopup();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPGVListBoxComboPopup is a wxVListBox customized to act as a popup control
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
BEGIN_EVENT_TABLE(wxPGVListBoxComboPopup, wxVListBox)
|
|
EVT_MOTION(wxPGVListBoxComboPopup::OnMouseMove)
|
|
EVT_KEY_DOWN(wxPGVListBoxComboPopup::OnKey)
|
|
EVT_LEFT_UP(wxPGVListBoxComboPopup::OnLeftClick)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
wxPGVListBoxComboPopup::wxPGVListBoxComboPopup(wxPGComboControl* combo)
|
|
: wxVListBox(), wxPGComboPopup(combo)
|
|
{
|
|
//m_callback = callback;
|
|
m_widestWidth = 0;
|
|
m_avgCharWidth = 0;
|
|
m_baseImageWidth = 0;
|
|
m_itemHeight = 0;
|
|
m_value = -1;
|
|
m_itemHover = -1;
|
|
m_clientDataItemsType = wxClientData_None;
|
|
}
|
|
|
|
bool wxPGVListBoxComboPopup::Create(wxWindow* parent)
|
|
{
|
|
if ( !wxVListBox::Create(parent,
|
|
wxID_ANY,
|
|
wxDefaultPosition,
|
|
wxDefaultSize,
|
|
wxBORDER_SIMPLE | wxLB_INT_HEIGHT | wxWANTS_CHARS) )
|
|
return false;
|
|
|
|
m_font = m_combo->GetFont();
|
|
|
|
wxVListBox::SetItemCount(m_strings.GetCount());
|
|
|
|
// TODO: Move this to SetFont
|
|
m_itemHeight = GetCharHeight() + 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
wxPGVListBoxComboPopup::~wxPGVListBoxComboPopup()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
bool wxPGVListBoxComboPopup::LazyCreate()
|
|
{
|
|
// NB: There is a bug with wxVListBox that can be avoided by creating
|
|
// it later (bug causes empty space to be shown if initial selection
|
|
// is at the end of a list longer than the control can show at once).
|
|
return true;
|
|
}
|
|
|
|
// paint the control itself
|
|
void wxPGVListBoxComboPopup::PaintComboControl( wxDC& dc, const wxRect& rect )
|
|
{
|
|
if ( !(m_combo->GetWindowStyle() & wxODCB_STD_CONTROL_PAINT) )
|
|
{
|
|
m_combo->DrawFocusBackground(dc,rect,0);
|
|
if ( m_combo->OnDrawListItem(dc,rect,m_value,wxPGCC_PAINTING_CONTROL) )
|
|
return;
|
|
}
|
|
|
|
wxPGComboPopup::PaintComboControl(dc,rect);
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::OnDrawItem( wxDC& dc, const wxRect& rect, size_t n) const
|
|
{
|
|
dc.SetFont(m_font);
|
|
|
|
bool isHilited = wxVListBox::GetSelection() == (int) n;
|
|
|
|
// Set correct text colour for selected items
|
|
// (must always set the correct colour - atleast GTK may have lost it
|
|
// in between calls).
|
|
if ( isHilited )
|
|
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
|
|
else
|
|
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
|
|
|
|
if ( !m_combo->OnDrawListItem(dc,rect,(int)n,0) )
|
|
dc.DrawText( GetString(n), rect.x + 2, rect.y );
|
|
}
|
|
|
|
wxCoord wxPGVListBoxComboPopup::OnMeasureItem(size_t n) const
|
|
{
|
|
int itemHeight = m_combo->OnMeasureListItem(n);
|
|
if ( itemHeight < 0 )
|
|
itemHeight = m_itemHeight;
|
|
|
|
return itemHeight;
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::OnDrawBackground(wxDC& dc, const wxRect& rect, size_t n) const
|
|
{
|
|
// we need to render selected and current items differently
|
|
if ( IsCurrent(n) )
|
|
{
|
|
m_combo->DrawFocusBackground( dc, rect, wxCONTROL_ISSUBMENU|wxCONTROL_SELECTED );
|
|
}
|
|
//else: do nothing for the normal items
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::DismissWithEvent()
|
|
{
|
|
int selection = wxVListBox::GetSelection();
|
|
|
|
Dismiss();
|
|
|
|
if ( selection != wxNOT_FOUND )
|
|
m_stringValue = m_strings[selection];
|
|
else
|
|
m_stringValue = wxEmptyString;
|
|
|
|
if ( m_stringValue != m_combo->GetValue() )
|
|
m_combo->SetValue(m_stringValue);
|
|
|
|
m_value = selection;
|
|
|
|
SendComboBoxEvent(selection);
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::SendComboBoxEvent( int selection )
|
|
{
|
|
wxCommandEvent evt(wxEVT_COMMAND_COMBOBOX_SELECTED,m_combo->GetId());
|
|
|
|
evt.SetEventObject(m_combo);
|
|
|
|
evt.SetInt(selection);
|
|
|
|
// Set client data, if any
|
|
if ( selection >= 0 && (int)m_clientDatas.GetCount() > selection )
|
|
{
|
|
void* clientData = m_clientDatas[selection];
|
|
if ( m_clientDataItemsType == wxClientData_Object )
|
|
evt.SetClientObject((wxClientData*)clientData);
|
|
else
|
|
evt.SetClientData(clientData);
|
|
}
|
|
|
|
m_combo->GetEventHandler()->AddPendingEvent(evt);
|
|
}
|
|
|
|
// returns true if key was consumed
|
|
bool wxPGVListBoxComboPopup::HandleKey( int keycode, bool saturate )
|
|
{
|
|
int value = m_value;
|
|
int itemCount = GetCount();
|
|
|
|
if ( keycode == WXK_DOWN || keycode == WXK_RIGHT )
|
|
{
|
|
value++;
|
|
}
|
|
else if ( keycode == WXK_UP || keycode == WXK_LEFT )
|
|
{
|
|
value--;
|
|
}
|
|
else if ( keycode == WXK_PAGEDOWN )
|
|
{
|
|
value+=10;
|
|
}
|
|
else if ( keycode == WXK_PAGEUP )
|
|
{
|
|
value-=10;
|
|
}
|
|
/*
|
|
else if ( keycode == WXK_END )
|
|
{
|
|
value = itemCount-1;
|
|
}
|
|
else if ( keycode == WXK_HOME )
|
|
{
|
|
value = 0;
|
|
}
|
|
*/
|
|
else
|
|
return false;
|
|
|
|
if ( saturate )
|
|
{
|
|
if ( value >= itemCount )
|
|
value = itemCount - 1;
|
|
else if ( value < 0 )
|
|
value = 0;
|
|
}
|
|
else
|
|
{
|
|
if ( value >= itemCount )
|
|
value -= itemCount;
|
|
else if ( value < 0 )
|
|
value += itemCount;
|
|
}
|
|
|
|
if ( value == m_value )
|
|
// Even if value was same, don't skip the event
|
|
// (good for consistency)
|
|
return true;
|
|
|
|
m_value = value;
|
|
|
|
wxString valStr;
|
|
if ( value >= 0 )
|
|
m_combo->SetValue(m_strings[value]);
|
|
|
|
SendComboBoxEvent(m_value);
|
|
|
|
return true;
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::OnComboDoubleClick()
|
|
{
|
|
// Cycle on dclick (disable saturation to allow true cycling).
|
|
if ( !::wxGetKeyState(WXK_SHIFT) )
|
|
HandleKey(WXK_DOWN,false);
|
|
else
|
|
HandleKey(WXK_UP,false);
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::OnComboKeyEvent( wxKeyEvent& event )
|
|
{
|
|
// Saturated key movement on
|
|
if ( !HandleKey(event.GetKeyCode(),true) )
|
|
event.Skip();
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::OnPopup()
|
|
{
|
|
// *must* set value after size is set (this is because of a vlbox bug)
|
|
wxVListBox::SetSelection(m_value);
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::OnMouseMove(wxMouseEvent& event)
|
|
{
|
|
// Move selection to cursor if it is inside the popup
|
|
int itemHere = GetItemAtPosition(event.GetPosition());
|
|
if ( itemHere >= 0 )
|
|
wxVListBox::SetSelection(itemHere);
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::OnLeftClick(wxMouseEvent& WXUNUSED(event))
|
|
{
|
|
DismissWithEvent();
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::OnKey(wxKeyEvent& event)
|
|
{
|
|
// Select item if ENTER is pressed
|
|
if ( event.GetKeyCode() == WXK_RETURN || event.GetKeyCode() == WXK_NUMPAD_ENTER )
|
|
{
|
|
DismissWithEvent();
|
|
}
|
|
// Hide popup if ESC is pressed
|
|
else if ( event.GetKeyCode() == WXK_ESCAPE )
|
|
Dismiss();
|
|
else
|
|
event.Skip();
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::CheckWidth( int pos )
|
|
{
|
|
wxCoord x = m_combo->OnMeasureListItemWidth(pos);
|
|
|
|
if ( x < 0 )
|
|
{
|
|
if ( !m_font.Ok() )
|
|
m_font = m_combo->GetFont();
|
|
|
|
wxCoord y;
|
|
m_combo->GetTextExtent(m_strings[pos], &x, &y, 0, 0, &m_font);
|
|
x += 4;
|
|
}
|
|
|
|
if ( m_widestWidth < x )
|
|
{
|
|
m_widestWidth = x;
|
|
}
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::Insert( const wxString& item, int pos )
|
|
{
|
|
// Need to change selection?
|
|
wxString strValue;
|
|
if ( !(m_combo->GetWindowStyle() & wxCB_READONLY) &&
|
|
m_combo->GetValue() == item )
|
|
m_value = pos;
|
|
else if ( m_value >= pos )
|
|
m_value++;
|
|
|
|
m_strings.Insert(item,pos);
|
|
|
|
if ( IsCreated() )
|
|
wxVListBox::SetItemCount( wxVListBox::GetItemCount()+1 );
|
|
|
|
// Calculate width
|
|
CheckWidth(pos);
|
|
}
|
|
|
|
int wxPGVListBoxComboPopup::Append(const wxString& item)
|
|
{
|
|
int pos = (int)m_strings.GetCount();
|
|
|
|
if ( m_combo->GetWindowStyle() & wxCB_SORT )
|
|
{
|
|
// Find position
|
|
// TODO: Could be optimized with binary search
|
|
wxArrayString strings = m_strings;
|
|
unsigned int i;
|
|
|
|
for ( i=0; i<strings.GetCount(); i++ )
|
|
{
|
|
if ( item.Cmp(strings.Item(i)) < 0 )
|
|
{
|
|
pos = (int)i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Insert(item,pos);
|
|
|
|
return pos;
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::Clear()
|
|
{
|
|
wxASSERT(m_combo);
|
|
|
|
m_strings.Empty();
|
|
|
|
ClearClientDatas();
|
|
|
|
if ( IsCreated() )
|
|
wxVListBox::SetItemCount(0);
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::ClearClientDatas()
|
|
{
|
|
if ( m_clientDataItemsType == wxClientData_Object )
|
|
{
|
|
size_t i;
|
|
for ( i=0; i<m_clientDatas.GetCount(); i++ )
|
|
delete (wxClientData*) m_clientDatas[i];
|
|
}
|
|
|
|
m_clientDatas.Empty();
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::SetItemClientData( wxODCIndex n,
|
|
void* clientData,
|
|
wxClientDataType clientDataItemsType )
|
|
{
|
|
// It should be sufficient to update this variable only here
|
|
m_clientDataItemsType = clientDataItemsType;
|
|
|
|
m_clientDatas.SetCount(n+1,NULL);
|
|
m_clientDatas[n] = clientData;
|
|
}
|
|
|
|
void* wxPGVListBoxComboPopup::GetItemClientData(wxODCIndex n) const
|
|
{
|
|
if ( ((wxODCIndex)m_clientDatas.GetCount()) > n )
|
|
return m_clientDatas[n];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::Delete( wxODCIndex item )
|
|
{
|
|
// Remove client data, if set
|
|
if ( m_clientDatas.GetCount() )
|
|
{
|
|
if ( m_clientDataItemsType == wxClientData_Object )
|
|
delete (wxClientData*) m_clientDatas[item];
|
|
|
|
m_clientDatas.RemoveAt(item);
|
|
}
|
|
|
|
m_strings.RemoveAt(item);
|
|
|
|
int sel = GetSelection();
|
|
|
|
if ( IsCreated() )
|
|
wxVListBox::SetItemCount( wxVListBox::GetItemCount()-1 );
|
|
|
|
if ( (int)item < sel )
|
|
SetSelection(sel-1);
|
|
else if ( (int)item == sel )
|
|
SetSelection(wxNOT_FOUND);
|
|
}
|
|
|
|
int wxPGVListBoxComboPopup::FindString(const wxString& s) const
|
|
{
|
|
return m_strings.Index(s);
|
|
}
|
|
|
|
wxODCCount wxPGVListBoxComboPopup::GetCount() const
|
|
{
|
|
return m_strings.GetCount();
|
|
}
|
|
|
|
wxString wxPGVListBoxComboPopup::GetString( int item ) const
|
|
{
|
|
return m_strings[item];
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::SetString( int item, const wxString& str )
|
|
{
|
|
m_strings[item] = str;
|
|
}
|
|
|
|
wxString wxPGVListBoxComboPopup::GetStringValue() const
|
|
{
|
|
return m_stringValue;
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::SetSelection( int item )
|
|
{
|
|
// This seems to be necessary (2.5.3 w/ MingW atleast)
|
|
if ( item < -1 || item >= (int)m_strings.GetCount() )
|
|
item = -1;
|
|
|
|
m_value = item;
|
|
if ( item >= 0 )
|
|
m_stringValue = m_strings[item];
|
|
else
|
|
m_stringValue = wxEmptyString;
|
|
|
|
if ( IsCreated() )
|
|
wxVListBox::SetSelection(item);
|
|
}
|
|
|
|
int wxPGVListBoxComboPopup::GetSelection() const
|
|
{
|
|
return m_value;
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::SetStringValue( const wxString& value )
|
|
{
|
|
int index = m_strings.Index(value);
|
|
|
|
m_stringValue = value;
|
|
|
|
if ( index >= 0 && index < (int)wxVListBox::GetItemCount() )
|
|
{
|
|
wxVListBox::SetSelection(index);
|
|
m_value = index;
|
|
}
|
|
}
|
|
|
|
wxSize wxPGVListBoxComboPopup::GetAdjustedSize( int minWidth, int prefHeight, int maxHeight )
|
|
{
|
|
int height = 250;
|
|
|
|
if ( m_strings.GetCount() )
|
|
{
|
|
if ( prefHeight > 0 )
|
|
height = prefHeight;
|
|
|
|
if ( height > maxHeight )
|
|
height = maxHeight;
|
|
|
|
int totalHeight = GetTotalHeight(); // + 3;
|
|
if ( height >= totalHeight )
|
|
{
|
|
height = totalHeight;
|
|
}
|
|
else
|
|
{
|
|
// Adjust height to a multiple of the height of the first item
|
|
// NB: Calculations that take variable height into account
|
|
// are unnecessary.
|
|
int fih = GetLineHeight(0);
|
|
int shown = height/fih;
|
|
height = shown * fih;
|
|
}
|
|
}
|
|
else
|
|
height = 50;
|
|
|
|
#if defined(__WXMAC__)
|
|
// Set a minimum height since otherwise scrollbars won't draw properly
|
|
height = wxMax(50, height);
|
|
#endif
|
|
|
|
// Take scrollbar into account in width calculations
|
|
int widestWidth = m_widestWidth + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
|
|
return wxSize(minWidth > widestWidth ? minWidth : widestWidth,
|
|
height+2);
|
|
}
|
|
|
|
void wxPGVListBoxComboPopup::Populate( int n, const wxString choices[] )
|
|
{
|
|
int i;
|
|
|
|
for ( i=0; i<n; i++ )
|
|
{
|
|
const wxString& item = choices[i];
|
|
m_strings.Add(item);
|
|
CheckWidth(i);
|
|
}
|
|
|
|
if ( IsCreated() )
|
|
wxVListBox::SetItemCount(n);
|
|
|
|
// Sort the initial choices
|
|
if ( m_combo->GetWindowStyle() & wxCB_SORT )
|
|
m_strings.Sort();
|
|
|
|
// Find initial selection
|
|
wxString strValue = m_combo->GetValue();
|
|
if ( strValue.Length() )
|
|
m_value = m_strings.Index(strValue);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// input handling
|
|
// ----------------------------------------------------------------------------
|
|
|
|
//
|
|
// This is pushed to the event handler queue of either combo box
|
|
// or its textctrl (latter if not readonly combo).
|
|
//
|
|
class wxPGComboBoxTextCtrlHandler : public wxEvtHandler
|
|
{
|
|
public:
|
|
|
|
wxPGComboBoxTextCtrlHandler( wxPGComboControlBase* combo )
|
|
: wxEvtHandler()
|
|
{
|
|
m_combo = combo;
|
|
}
|
|
~wxPGComboBoxTextCtrlHandler() { }
|
|
void OnKey(wxKeyEvent& event);
|
|
void OnFocus(wxFocusEvent& event);
|
|
|
|
protected:
|
|
wxPGComboControlBase* m_combo;
|
|
|
|
private:
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
|
|
BEGIN_EVENT_TABLE(wxPGComboBoxTextCtrlHandler, wxEvtHandler)
|
|
EVT_KEY_DOWN(wxPGComboBoxTextCtrlHandler::OnKey)
|
|
EVT_SET_FOCUS(wxPGComboBoxTextCtrlHandler::OnFocus)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
void wxPGComboBoxTextCtrlHandler::OnKey(wxKeyEvent& event)
|
|
{
|
|
// Let the wxComboCtrl event handler have a go first.
|
|
wxPGComboControlBase* combo = m_combo;
|
|
wxObject* prevObj = event.GetEventObject();
|
|
|
|
event.SetId(combo->GetId());
|
|
event.SetEventObject(combo);
|
|
combo->GetEventHandler()->ProcessEvent(event);
|
|
|
|
event.SetId(((wxWindow*)prevObj)->GetId());
|
|
event.SetEventObject(prevObj);
|
|
}
|
|
|
|
void wxPGComboBoxTextCtrlHandler::OnFocus(wxFocusEvent& event)
|
|
{
|
|
// FIXME: This code does run when control is clicked,
|
|
// yet on Windows it doesn't select all the text.
|
|
if ( !(m_combo->GetInternalFlags() & wxPGCC_NO_TEXT_AUTO_SELECT) )
|
|
{
|
|
if ( m_combo->GetTextCtrl() )
|
|
m_combo->GetTextCtrl()->SelectAll();
|
|
else
|
|
m_combo->SetSelection(-1,-1);
|
|
}
|
|
|
|
// Send focus indication to parent.
|
|
// NB: This is needed for cases where the textctrl gets focus
|
|
// instead of its parent. We'll check if the focus came from
|
|
// in order to prevent a possible infinite recursion.
|
|
if ( m_combo->ConsumingTextCtrlFocusEvent() )
|
|
{
|
|
wxFocusEvent evt2(wxEVT_SET_FOCUS,m_combo->GetId());
|
|
evt2.SetEventObject(m_combo);
|
|
m_combo->GetEventHandler()->ProcessEvent(evt2);
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
|
|
//
|
|
// This is pushed to the event handler queue of the control in popup.
|
|
//
|
|
|
|
class wxPGComboPopupExtraEventHandler : public wxEvtHandler
|
|
{
|
|
public:
|
|
|
|
wxPGComboPopupExtraEventHandler( wxPGComboControlBase* combo )
|
|
: wxEvtHandler()
|
|
{
|
|
m_combo = combo;
|
|
m_beenInside = false;
|
|
}
|
|
~wxPGComboPopupExtraEventHandler() { }
|
|
|
|
void OnMouseEvent( wxMouseEvent& event );
|
|
|
|
// Called from wxPGComboControlBase::OnPopupDismiss
|
|
void OnPopupDismiss()
|
|
{
|
|
m_beenInside = false;
|
|
}
|
|
|
|
protected:
|
|
wxPGComboControlBase* m_combo;
|
|
|
|
bool m_beenInside;
|
|
|
|
private:
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
|
|
BEGIN_EVENT_TABLE(wxPGComboPopupExtraEventHandler, wxEvtHandler)
|
|
EVT_MOUSE_EVENTS(wxPGComboPopupExtraEventHandler::OnMouseEvent)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
void wxPGComboPopupExtraEventHandler::OnMouseEvent( wxMouseEvent& event )
|
|
{
|
|
wxPoint pt = event.GetPosition();
|
|
wxSize sz = m_combo->GetPopupControl()->GetClientSize();
|
|
int evtType = event.GetEventType();
|
|
bool isInside = pt.x >= 0 && pt.y >= 0 && pt.x < sz.x && pt.y < sz.y;
|
|
|
|
if ( evtType == wxEVT_MOTION ||
|
|
evtType == wxEVT_LEFT_DOWN ||
|
|
evtType == wxEVT_RIGHT_DOWN )
|
|
{
|
|
// Block motion and click events outside the popup
|
|
if ( !isInside )
|
|
{
|
|
event.Skip(false);
|
|
return;
|
|
}
|
|
}
|
|
else if ( evtType == wxEVT_LEFT_UP )
|
|
{
|
|
// Don't let left-down events in if outside
|
|
if ( evtType == wxEVT_LEFT_DOWN )
|
|
{
|
|
if ( !isInside )
|
|
return;
|
|
}
|
|
|
|
if ( !m_beenInside )
|
|
{
|
|
if ( isInside )
|
|
{
|
|
m_beenInside = true;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Some mouse events to popup that happen outside it, before cursor
|
|
// has been inside the popu, need to be ignored by it but relayed to
|
|
// the dropbutton.
|
|
//
|
|
wxWindow* btn = m_combo->GetButton();
|
|
if ( btn )
|
|
btn->GetEventHandler()->AddPendingEvent(event);
|
|
else
|
|
m_combo->GetEventHandler()->AddPendingEvent(event);
|
|
|
|
return;
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPGComboControlBase
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
BEGIN_EVENT_TABLE(wxPGComboControlBase, wxControl)
|
|
EVT_TEXT(wxID_ANY,wxPGComboControlBase::OnTextCtrlEvent)
|
|
EVT_SIZE(wxPGComboControlBase::OnSizeEvent)
|
|
EVT_KEY_DOWN(wxPGComboControlBase::OnKeyEvent)
|
|
EVT_SET_FOCUS(wxPGComboControlBase::OnFocusEvent)
|
|
EVT_KILL_FOCUS(wxPGComboControlBase::OnFocusEvent)
|
|
//EVT_BUTTON(wxID_ANY,wxPGComboControlBase::OnButtonClickEvent)
|
|
EVT_TEXT_ENTER(wxID_ANY,wxPGComboControlBase::OnTextCtrlEvent)
|
|
EVT_SYS_COLOUR_CHANGED(wxPGComboControlBase::OnSysColourChanged)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
IMPLEMENT_ABSTRACT_CLASS(wxPGComboControlBase, wxControl)
|
|
|
|
// Have global double buffer - should be enough for multiple combos
|
|
static wxBitmap* gs_doubleBuffer = (wxBitmap*) NULL;
|
|
|
|
void wxPGComboControlBase::Init()
|
|
{
|
|
m_winPopup = (wxWindow *)NULL;
|
|
m_popup = (wxWindow *)NULL;
|
|
m_isPopupShown = false;
|
|
m_btn = (wxWindow*) NULL;
|
|
m_text = (wxTextCtrl*) NULL;
|
|
m_popupInterface = (wxPGComboPopup*) NULL;
|
|
|
|
m_popupExtraHandler = (wxEvtHandler*) NULL;
|
|
m_textEvtHandler = (wxEvtHandler*) NULL;
|
|
|
|
#if INSTALL_TOPLEV_HANDLER
|
|
m_toplevEvtHandler = (wxEvtHandler*) NULL;
|
|
#endif
|
|
|
|
m_heightPopup = -1;
|
|
|
|
m_widthMinPopup = -1;
|
|
|
|
m_widthCustomPaint = 0;
|
|
|
|
m_widthCustomBorder = 0;
|
|
|
|
m_btnState = 0;
|
|
|
|
m_btnWidDefault = 0;
|
|
|
|
m_blankButtonBg = false;
|
|
|
|
m_btnWid = m_btnHei = 0;
|
|
m_btnSide = wxRIGHT;
|
|
m_btnSpacingX = 0;
|
|
|
|
m_extLeft = 0;
|
|
m_extRight = 0;
|
|
|
|
m_absIndent = -1;
|
|
|
|
m_iFlags = 0;
|
|
|
|
m_fakePopupUsage = 0;
|
|
m_skipTextCtrlFocusEvents = 0;
|
|
|
|
m_timeCanAcceptClick = 0;
|
|
}
|
|
|
|
bool wxPGComboControlBase::Create(wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxString& value,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxValidator& validator,
|
|
const wxString& name)
|
|
{
|
|
if ( !wxControl::Create(parent,
|
|
id,
|
|
pos,
|
|
size,
|
|
style | wxWANTS_CHARS,
|
|
validator,
|
|
name) )
|
|
return false;
|
|
|
|
m_valueString = value;
|
|
|
|
// Get colours
|
|
OnThemeChange();
|
|
m_absIndent = GetNativeTextIndent();
|
|
|
|
return true;
|
|
}
|
|
|
|
void wxPGComboControlBase::InstallInputHandlers()
|
|
{
|
|
if ( m_text )
|
|
{
|
|
m_textEvtHandler = new wxPGComboBoxTextCtrlHandler(this);
|
|
m_text->PushEventHandler(m_textEvtHandler);
|
|
}
|
|
}
|
|
|
|
void wxPGComboControlBase::CreateTextCtrl( int extraStyle, const wxValidator& validator )
|
|
{
|
|
if ( !(m_windowStyle & wxCB_READONLY) )
|
|
{
|
|
m_text = new wxTextCtrl(this,
|
|
12345,
|
|
m_valueString,
|
|
wxDefaultPosition,
|
|
wxDefaultSize,
|
|
// 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.
|
|
wxTE_PROCESS_TAB |
|
|
wxTE_PROCESS_ENTER |
|
|
//wxWANTS_CHARS |
|
|
extraStyle,
|
|
validator);
|
|
|
|
#if defined(__WXMSW__) && !defined(__WXWINCE__)
|
|
::SendMessage(GetHwndOf(m_text), EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(3, 3));
|
|
#endif
|
|
|
|
// This is required for some platforms (GTK+ atleast)
|
|
m_text->SetSizeHints(2,4);
|
|
}
|
|
}
|
|
|
|
void wxPGComboControlBase::OnThemeChange()
|
|
{
|
|
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
|
}
|
|
|
|
bool wxPGComboControlBase::Destroy()
|
|
{
|
|
return wxControl::Destroy();
|
|
}
|
|
|
|
wxPGComboControlBase::~wxPGComboControlBase()
|
|
{
|
|
if ( HasCapture() )
|
|
ReleaseMouse();
|
|
|
|
HidePopup();
|
|
|
|
delete gs_doubleBuffer;
|
|
gs_doubleBuffer = (wxBitmap*) NULL;
|
|
|
|
#if INSTALL_TOPLEV_HANDLER
|
|
delete ((wxPGComboFrameEventHandler*)m_toplevEvtHandler);
|
|
m_toplevEvtHandler = (wxEvtHandler*) NULL;
|
|
#endif
|
|
|
|
if ( m_popup )
|
|
m_popup->RemoveEventHandler(m_popupExtraHandler);
|
|
|
|
delete m_popupExtraHandler;
|
|
|
|
delete m_popupInterface;
|
|
delete m_winPopup;
|
|
|
|
if ( m_text )
|
|
m_text->RemoveEventHandler(m_textEvtHandler);
|
|
|
|
delete m_textEvtHandler;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// geometry stuff
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Recalculates button and textctrl areas
|
|
void wxPGComboControlBase::CalculateAreas( int btnWidth )
|
|
{
|
|
wxSize sz = GetClientSize();
|
|
int customBorder = m_widthCustomBorder;
|
|
bool buttonOutside;
|
|
int btnBorder; // border for button only
|
|
|
|
if ( ( (m_iFlags & wxPGCC_BUTTON_OUTSIDE_BORDER) || m_blankButtonBg ) &&
|
|
m_btnSpacingX == 0 && m_btnWid == 0 && m_btnHei == 0 &&
|
|
(!m_bmpNormal.Ok() || m_blankButtonBg) )
|
|
{
|
|
buttonOutside = true;
|
|
m_iFlags |= wxPGCC_IFLAG_BUTTON_OUTSIDE;
|
|
btnBorder = 0;
|
|
}
|
|
else
|
|
{
|
|
buttonOutside = false;
|
|
m_iFlags &= ~(wxPGCC_IFLAG_BUTTON_OUTSIDE);
|
|
btnBorder = customBorder;
|
|
}
|
|
|
|
// Defaul indentation
|
|
if ( m_absIndent < 0 )
|
|
m_absIndent = GetNativeTextIndent();
|
|
|
|
int butWidth = btnWidth;
|
|
|
|
if ( butWidth <= 0 )
|
|
butWidth = m_btnWidDefault;
|
|
else
|
|
m_btnWidDefault = butWidth;
|
|
|
|
if ( butWidth <= 0 )
|
|
return;
|
|
|
|
// Adjust button width
|
|
if ( m_btnWid < 0 )
|
|
butWidth += m_btnWid;
|
|
else if ( m_btnWid > 0 )
|
|
butWidth = m_btnWid;
|
|
|
|
int butHeight = sz.y;
|
|
|
|
butHeight -= btnBorder*2;
|
|
|
|
// Adjust button height
|
|
if ( m_btnHei < 0 )
|
|
butHeight += m_btnHei;
|
|
else if ( m_btnHei > 0 )
|
|
butHeight = m_btnHei;
|
|
|
|
// Use size of normal bitmap if...
|
|
// It is larger
|
|
// OR
|
|
// button width is set to default and blank button bg is not drawn
|
|
if ( m_bmpNormal.Ok() )
|
|
{
|
|
int bmpReqWidth = m_bmpNormal.GetWidth();
|
|
int bmpReqHeight = m_bmpNormal.GetHeight();
|
|
|
|
// If drawing blank button background, we need to add some margin.
|
|
if ( m_blankButtonBg )
|
|
{
|
|
bmpReqWidth += BMP_BUTTON_MARGIN*2;
|
|
bmpReqHeight += BMP_BUTTON_MARGIN*2;
|
|
}
|
|
|
|
if ( butWidth < bmpReqWidth || ( m_btnWid == 0 && !m_blankButtonBg ) )
|
|
butWidth = bmpReqWidth;
|
|
if ( butHeight < bmpReqHeight || ( m_btnHei == 0 && !m_blankButtonBg ) )
|
|
butHeight = bmpReqHeight;
|
|
|
|
// Need to fix height?
|
|
if ( (sz.y-(customBorder*2)) < butHeight && btnWidth == 0 )
|
|
{
|
|
int newY = butHeight+(customBorder*2);
|
|
SetClientSize(-1,newY);
|
|
sz.y = newY;
|
|
}
|
|
}
|
|
|
|
int butAreaWid = butWidth + (m_btnSpacingX*2);
|
|
|
|
m_btnSize.x = butWidth;
|
|
m_btnSize.y = butHeight;
|
|
|
|
m_btnArea.x = ( m_btnSide==wxRIGHT ? sz.x - butAreaWid - btnBorder : btnBorder );
|
|
m_btnArea.y = btnBorder;
|
|
m_btnArea.width = butAreaWid;
|
|
m_btnArea.height = sz.y - (btnBorder*2);
|
|
|
|
if ( m_bmpNormal.Ok() || m_btnArea.width != butWidth || m_btnArea.height != butHeight )
|
|
m_iFlags |= wxPGCC_IFLAG_HAS_NONSTANDARD_BUTTON;
|
|
else
|
|
m_iFlags &= ~wxPGCC_IFLAG_HAS_NONSTANDARD_BUTTON;
|
|
|
|
m_tcArea.x = ( m_btnSide==wxRIGHT ? 0 : butAreaWid ) + customBorder;
|
|
m_tcArea.y = customBorder;
|
|
m_tcArea.width = sz.x - butAreaWid - (customBorder*2);
|
|
m_tcArea.height = sz.y - (customBorder*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 wxPGComboControlBase::PositionTextCtrl( int textCtrlXAdjust, int textCtrlYAdjust )
|
|
{
|
|
if ( !m_text )
|
|
return;
|
|
|
|
wxSize sz = GetClientSize();
|
|
int customBorder = m_widthCustomBorder;
|
|
|
|
if ( (m_text->GetWindowStyleFlag() & wxBORDER_MASK) == wxNO_BORDER )
|
|
{
|
|
// Centre textctrl
|
|
int tcSizeY = m_text->GetBestSize().y;
|
|
int diff = sz.y - tcSizeY;
|
|
int y = textCtrlYAdjust + (diff/2);
|
|
|
|
if ( y < customBorder )
|
|
y = customBorder;
|
|
|
|
m_text->SetSize( m_tcArea.x + m_widthCustomPaint + m_absIndent + textCtrlXAdjust,
|
|
y,
|
|
m_tcArea.width - g_comboMargin -
|
|
(textCtrlXAdjust + m_widthCustomPaint + m_absIndent),
|
|
-1 );
|
|
|
|
// Make sure textctrl doesn't exceed the bottom custom border
|
|
wxSize tsz = m_text->GetSize();
|
|
diff = (y + tsz.y) - (sz.y - customBorder);
|
|
if ( diff >= 0 )
|
|
{
|
|
tsz.y = tsz.y - diff - 1;
|
|
m_text->SetSize(tsz);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_text->SetSize( m_tcArea.x,
|
|
0,
|
|
sz.x - m_btnArea.x - m_widthCustomPaint - customBorder,
|
|
sz.y );
|
|
}
|
|
}
|
|
|
|
wxSize wxPGComboControlBase::DoGetBestSize() const
|
|
{
|
|
wxSize sizeText(150,0);
|
|
|
|
if ( m_text )
|
|
sizeText = m_text->GetBestSize();
|
|
|
|
// TODO: Better method to calculate close-to-native control height.
|
|
|
|
int fhei;
|
|
if ( m_font.Ok() )
|
|
fhei = (m_font.GetPointSize()*2) + 5;
|
|
else if ( wxNORMAL_FONT->Ok() )
|
|
fhei = (wxNORMAL_FONT->GetPointSize()*2) + 5;
|
|
else
|
|
fhei = sizeText.y + 4;
|
|
|
|
// Need to force height to accomodate bitmap?
|
|
int btnSizeY = m_btnSize.y;
|
|
if ( m_bmpNormal.Ok() && fhei < btnSizeY )
|
|
fhei = btnSizeY;
|
|
|
|
// Control height doesn't depend on border
|
|
/*
|
|
// Add border
|
|
int border = m_windowStyle & wxBORDER_MASK;
|
|
if ( border == wxSIMPLE_BORDER )
|
|
fhei += 2;
|
|
else if ( border == wxNO_BORDER )
|
|
fhei += (m_widthCustomBorder*2);
|
|
else
|
|
// Sunken etc.
|
|
fhei += 4;
|
|
*/
|
|
|
|
// Final adjustments
|
|
#ifdef __WXGTK__
|
|
fhei += 1;
|
|
#endif
|
|
|
|
wxSize ret(sizeText.x + g_comboMargin + DEFAULT_DROPBUTTON_WIDTH,
|
|
fhei);
|
|
|
|
CacheBestSize(ret);
|
|
return ret;
|
|
}
|
|
|
|
void wxPGComboControlBase::DoMoveWindow(int x, int y, int width, int height)
|
|
{
|
|
// SetSize is called last in create, so it marks the end of creation
|
|
m_iFlags |= wxPGCC_IFLAG_CREATED;
|
|
|
|
wxControl::DoMoveWindow(x, y, width, height);
|
|
}
|
|
|
|
void wxPGComboControlBase::OnSizeEvent( wxSizeEvent& event )
|
|
{
|
|
if ( !IsCreated() )
|
|
return;
|
|
|
|
// defined by actual wxComboControls
|
|
OnResize();
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// standard operations
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxPGComboControlBase::Enable(bool enable)
|
|
{
|
|
if ( !wxControl::Enable(enable) )
|
|
return false;
|
|
|
|
if ( m_btn )
|
|
m_btn->Enable(enable);
|
|
if ( m_text )
|
|
m_text->Enable(enable);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxPGComboControlBase::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 wxPGComboControlBase::SetFont ( const wxFont& font )
|
|
{
|
|
if ( !wxControl::SetFont(font) )
|
|
return false;
|
|
|
|
if (m_text)
|
|
m_text->SetFont(font);
|
|
|
|
return true;
|
|
}
|
|
|
|
#if wxUSE_TOOLTIPS
|
|
void wxPGComboControlBase::DoSetToolTip(wxToolTip *tooltip)
|
|
{
|
|
wxControl::DoSetToolTip(tooltip);
|
|
|
|
// Set tool tip for button and text box
|
|
if ( tooltip )
|
|
{
|
|
const wxString &tip = tooltip->GetTip();
|
|
if ( m_text ) m_text->SetToolTip(tip);
|
|
if ( m_btn ) m_btn->SetToolTip(tip);
|
|
}
|
|
else
|
|
{
|
|
if ( m_text ) m_text->SetToolTip( (wxToolTip*) NULL );
|
|
if ( m_btn ) m_btn->SetToolTip( (wxToolTip*) NULL );
|
|
}
|
|
}
|
|
#endif // wxUSE_TOOLTIPS
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// painting
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// draw focus background on area in a way typical on platform
|
|
void wxPGComboControlBase::DrawFocusBackground( wxDC& dc, const wxRect& rect, int flags )
|
|
{
|
|
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();
|
|
|
|
// Windows-style: for smaller size control (and for disabled background) use less spacing
|
|
//focusSpacingX = isEnabled ? 2 : 1;
|
|
focusSpacingX = 1;
|
|
focusSpacingY = sz.y > (GetCharHeight()+500) && isEnabled ? 2 : 1;
|
|
}
|
|
else
|
|
{
|
|
// Drawing a list item
|
|
isEnabled = true; // they are never disabled
|
|
doDrawFocusRect = flags & wxCONTROL_SELECTED ? true : false;
|
|
|
|
focusSpacingX = 0;
|
|
focusSpacingY = 0;
|
|
}
|
|
|
|
// Set the background sub-rectangle for selection, disabled etc
|
|
wxRect selRect(rect);
|
|
selRect.y += focusSpacingY;
|
|
selRect.height -= (focusSpacingY*2);
|
|
int wcp = 0;
|
|
|
|
if ( !(flags & wxCONTROL_ISSUBMENU) )
|
|
wcp += m_widthCustomPaint;
|
|
|
|
selRect.x += wcp + focusSpacingX;
|
|
selRect.width -= wcp + (focusSpacingX*2);
|
|
|
|
wxColour bgCol;
|
|
bool doDrawSelRect = true;
|
|
|
|
if ( isEnabled )
|
|
{
|
|
// If popup is hidden and this control is focused,
|
|
// then draw the focus-indicator (selbgcolor background etc.).
|
|
if ( doDrawFocusRect )
|
|
{
|
|
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
|
|
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
|
|
}
|
|
else
|
|
{
|
|
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
|
|
bgCol = GetBackgroundColour();
|
|
doDrawSelRect = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) );
|
|
bgCol = GetBackgroundColour();
|
|
}
|
|
|
|
dc.SetBrush( bgCol );
|
|
if ( doDrawSelRect )
|
|
{
|
|
dc.SetPen( bgCol );
|
|
dc.DrawRectangle( selRect );
|
|
}
|
|
}
|
|
|
|
void wxPGComboControlBase::DrawButton( wxDC& dc, const wxRect& rect, int flags )
|
|
{
|
|
int drawState = m_btnState;
|
|
|
|
if ( (m_iFlags & wxPGCC_BUTTON_STAYS_DOWN) &&
|
|
IsPopupShown() )
|
|
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;
|
|
|
|
if ( !m_bmpNormal.Ok() )
|
|
{
|
|
if ( flags & Button_BitmapOnly )
|
|
return;
|
|
|
|
// Need to clear button background even if m_btn is present
|
|
if ( flags & Button_PaintBackground )
|
|
{
|
|
wxColour bgCol;
|
|
|
|
if ( m_iFlags & wxPGCC_IFLAG_BUTTON_OUTSIDE )
|
|
bgCol = GetParent()->GetBackgroundColour();
|
|
else
|
|
bgCol = GetBackgroundColour();
|
|
|
|
dc.SetBrush(bgCol);
|
|
dc.SetPen(bgCol);
|
|
dc.DrawRectangle(rect);
|
|
}
|
|
|
|
// 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 wxCHECK_VERSION(2, 7, 0)
|
|
if ( m_blankButtonBg )
|
|
{
|
|
// If using blank button background, we need to clear its background
|
|
// with button face colour instead of colour for rest of the control.
|
|
if ( flags & Button_PaintBackground )
|
|
{
|
|
wxColour bgCol = GetParent()->GetBackgroundColour(); //wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
|
|
//wxColour bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
|
dc.SetPen(bgCol);
|
|
dc.SetBrush(bgCol);
|
|
dc.DrawRectangle(rect);
|
|
}
|
|
|
|
if ( !(flags & Button_BitmapOnly) )
|
|
{
|
|
wxRendererNative::Get().DrawPushButton(this,
|
|
dc,
|
|
drawRect,
|
|
drawState);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// Need to clear button background even if m_btn is present
|
|
// (assume non-button background was cleared just before this call so brushes are good)
|
|
if ( flags & Button_PaintBackground )
|
|
dc.DrawRectangle(rect);
|
|
}
|
|
|
|
// Draw bitmap centered in drawRect
|
|
dc.DrawBitmap(*pBmp,
|
|
drawRect.x + (drawRect.width-pBmp->GetWidth())/2,
|
|
drawRect.y + (drawRect.height-pBmp->GetHeight())/2,
|
|
true);
|
|
}
|
|
}
|
|
|
|
void wxPGComboControlBase::RecalcAndRefresh()
|
|
{
|
|
if ( IsCreated() )
|
|
{
|
|
wxSizeEvent evt(GetSize(),GetId());
|
|
GetEventHandler()->ProcessEvent(evt);
|
|
Refresh();
|
|
}
|
|
}
|
|
|
|
bool wxPGComboControlBase::OnDrawListItem( wxDC& WXUNUSED(dc),
|
|
const wxRect& WXUNUSED(rect),
|
|
int WXUNUSED(item),
|
|
int WXUNUSED(flags) )
|
|
{
|
|
return false; // signals caller to make default drawing
|
|
}
|
|
|
|
wxCoord wxPGComboControlBase::OnMeasureListItem( int WXUNUSED(item) )
|
|
{
|
|
return -1; // signals caller to use default
|
|
}
|
|
|
|
wxCoord wxPGComboControlBase::OnMeasureListItemWidth( int WXUNUSED(item) )
|
|
{
|
|
return -1; // signals caller to use default
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// miscellaneous event handlers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxPGComboControlBase::OnTextCtrlEvent(wxCommandEvent& event)
|
|
{
|
|
// Change event id and relay it forward
|
|
event.SetId(GetId());
|
|
event.Skip();
|
|
}
|
|
|
|
// call if cursor is on button area or mouse is captured for the button
|
|
bool wxPGComboControlBase::HandleButtonMouseEvent( wxMouseEvent& event,
|
|
int flags )
|
|
{
|
|
int type = event.GetEventType();
|
|
|
|
if ( type == wxEVT_MOTION )
|
|
{
|
|
if ( flags & wxPGCC_MF_ON_BUTTON )
|
|
{
|
|
if ( !(m_btnState & wxCONTROL_CURRENT) )
|
|
{
|
|
// Mouse hover begins
|
|
m_btnState |= wxCONTROL_CURRENT;
|
|
if ( HasCapture() ) // Retain pressed state.
|
|
m_btnState |= wxCONTROL_PRESSED;
|
|
Refresh();
|
|
}
|
|
}
|
|
else if ( (m_btnState & wxCONTROL_CURRENT) )
|
|
{
|
|
// Mouse hover ends
|
|
m_btnState &= ~(wxCONTROL_CURRENT|wxCONTROL_PRESSED);
|
|
Refresh();
|
|
}
|
|
}
|
|
else if ( type == wxEVT_LEFT_DOWN )
|
|
{
|
|
// Only accept event if it wasn't right after popup dismiss
|
|
//if ( ::wxGetLocalTimeMillis() > m_timeCanClick )
|
|
{
|
|
// Need to test this, because it might be outside.
|
|
if ( flags & wxPGCC_MF_ON_BUTTON )
|
|
{
|
|
m_btnState |= wxCONTROL_PRESSED;
|
|
Refresh();
|
|
|
|
if ( !(m_iFlags & wxPGCC_POPUP_ON_MOUSE_UP) )
|
|
OnButtonClick();
|
|
else
|
|
// If showing popup now, do not capture mouse or there will be interference
|
|
CaptureMouse();
|
|
}
|
|
}
|
|
/*else
|
|
{
|
|
m_btnState = 0;
|
|
}*/
|
|
}
|
|
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 & wxPGCC_POPUP_ON_MOUSE_UP )
|
|
{
|
|
if ( flags & wxPGCC_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 ( !m_isPopupShown )
|
|
{
|
|
m_btnState &= ~(wxCONTROL_PRESSED);
|
|
Refresh();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Conversion to double-clicks and some basic filtering
|
|
// returns true if event was consumed or filtered
|
|
bool wxPGComboControlBase::PreprocessMouseEvent( wxMouseEvent& event,
|
|
int WXUNUSED(flags) )
|
|
{
|
|
wxLongLong t = ::wxGetLocalTimeMillis();
|
|
int evtType = event.GetEventType();
|
|
|
|
if ( m_isPopupShown &&
|
|
( evtType == wxEVT_LEFT_DOWN || evtType == wxEVT_RIGHT_DOWN ) )
|
|
{
|
|
HidePopup();
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Generate our own double-clicks
|
|
// (to allow on-focus dc-event on double-clicks instead of triple-clicks)
|
|
/*if ( (m_windowStyle & wxPGCC_DCLICK_CYCLES) &&
|
|
!m_isPopupShown &&
|
|
//!(handlerFlags & wxPGCC_MF_ON_BUTTON) )
|
|
!(flags & wxPGCC_MF_ON_BUTTON) )
|
|
{
|
|
if ( evtType == wxEVT_LEFT_DOWN )
|
|
{
|
|
// Set value to avoid up-events without corresponding downs
|
|
m_downReceived = true;
|
|
}
|
|
else if ( evtType == wxEVT_LEFT_DCLICK )
|
|
{
|
|
// We'll make our own double-clicks
|
|
//evtType = 0;
|
|
event.SetEventType(0);
|
|
return true;
|
|
}
|
|
else if ( evtType == wxEVT_LEFT_UP )
|
|
{
|
|
if ( m_downReceived || m_timeLastMouseUp == 1 )
|
|
{
|
|
wxLongLong timeFromLastUp = (t-m_timeLastMouseUp);
|
|
|
|
if ( timeFromLastUp < DOUBLE_CLICK_CONVERSION_TRESHOLD )
|
|
{
|
|
//type = wxEVT_LEFT_DCLICK;
|
|
event.SetEventType(wxEVT_LEFT_DCLICK);
|
|
m_timeLastMouseUp = 1;
|
|
}
|
|
else
|
|
{
|
|
m_timeLastMouseUp = t;
|
|
}
|
|
|
|
//m_downReceived = false;
|
|
}
|
|
}
|
|
}*/
|
|
|
|
// Filter out clicks on button immediately after popup dismiss (Windows like behaviour)
|
|
if ( evtType == wxEVT_LEFT_DOWN && t < m_timeCanAcceptClick )
|
|
{
|
|
event.SetEventType(0);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void wxPGComboControlBase::HandleNormalMouseEvent( wxMouseEvent& event )
|
|
{
|
|
int evtType = event.GetEventType();
|
|
|
|
if ( (evtType == wxEVT_LEFT_DOWN || evtType == wxEVT_LEFT_DCLICK) &&
|
|
(m_windowStyle & wxCB_READONLY) )
|
|
{
|
|
if ( m_isPopupShown )
|
|
{
|
|
#if !wxUSE_POPUPWIN
|
|
// Normally do nothing - evt handler should close it for us
|
|
#if ALLOW_FAKE_POPUP
|
|
if ( m_fakePopupUsage == 2 )
|
|
HidePopup();
|
|
#endif
|
|
#elif !USE_TRANSIENT_POPUP
|
|
// Click here always hides the popup.
|
|
HidePopup();
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if ( !(m_windowStyle & wxPGCC_DCLICK_CYCLES) )
|
|
{
|
|
// 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 ( m_isPopupShown )
|
|
{
|
|
// relay (some) mouse events to the popup
|
|
if ( evtType == wxEVT_MOUSEWHEEL )
|
|
m_popup->AddPendingEvent(event);
|
|
}
|
|
else if ( evtType )
|
|
event.Skip();
|
|
}
|
|
|
|
void wxPGComboControlBase::OnKeyEvent( wxKeyEvent& event )
|
|
{
|
|
int keycode = event.GetKeyCode();
|
|
|
|
if ( keycode == WXK_TAB &&
|
|
!IsPopupShown() )
|
|
{
|
|
wxNavigationKeyEvent evt;
|
|
evt.SetFlags(wxNavigationKeyEvent::FromTab|
|
|
(!event.ShiftDown()?wxNavigationKeyEvent::IsForward:
|
|
wxNavigationKeyEvent::IsBackward));
|
|
evt.SetEventObject(this);
|
|
GetParent()->GetEventHandler()->AddPendingEvent(evt);
|
|
return;
|
|
}
|
|
|
|
if ( IsPopupShown() )
|
|
{
|
|
// pass it to the popped up control
|
|
GetPopupControl()->AddPendingEvent(event);
|
|
}
|
|
else // no popup
|
|
{
|
|
int comboStyle = GetWindowStyle();
|
|
wxPGComboPopup* popupInterface = GetPopup();
|
|
|
|
if ( !popupInterface )
|
|
{
|
|
event.Skip();
|
|
return;
|
|
}
|
|
|
|
if ( (comboStyle & wxCB_READONLY) ||
|
|
( keycode != WXK_RIGHT && keycode != WXK_LEFT )
|
|
)
|
|
{
|
|
// Alternate keys: UP and DOWN show the popup instead of cycling
|
|
if ( (comboStyle & wxPGCC_ALT_KEYS) )
|
|
{
|
|
if ( keycode == WXK_UP || keycode == WXK_DOWN )
|
|
{
|
|
OnButtonClick();
|
|
return;
|
|
}
|
|
else
|
|
event.Skip();
|
|
}
|
|
else
|
|
popupInterface->OnComboKeyEvent(event);
|
|
}
|
|
else
|
|
event.Skip();
|
|
}
|
|
}
|
|
|
|
void wxPGComboControlBase::OnFocusEvent( wxFocusEvent& event )
|
|
{
|
|
if ( event.GetEventType() == wxEVT_SET_FOCUS )
|
|
{
|
|
if ( m_text && m_text != ::wxWindow::FindFocus() )
|
|
{
|
|
m_skipTextCtrlFocusEvents++;
|
|
m_text->SetFocus();
|
|
}
|
|
}
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void wxPGComboControlBase::OnSysColourChanged(wxSysColourChangedEvent& WXUNUSED(event))
|
|
{
|
|
OnThemeChange();
|
|
// indentation may also have changed
|
|
if ( !(m_iFlags & wxPGCC_IFLAG_INDENT_SET) )
|
|
m_absIndent = GetNativeTextIndent();
|
|
RecalcAndRefresh();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// popup handling
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Create popup window and the child control
|
|
void wxPGComboControlBase::CreatePopup()
|
|
{
|
|
wxPGComboPopup* popupInterface = m_popupInterface;
|
|
wxWindow* popup;
|
|
|
|
if ( !m_winPopup )
|
|
m_winPopup = new wxPGComboPopupWindow( this, wxNO_BORDER );
|
|
|
|
popupInterface->Create(m_winPopup);
|
|
m_popup = popup = popupInterface->GetControl();
|
|
|
|
m_popupExtraHandler = new wxPGComboPopupExtraEventHandler(this);
|
|
popup->PushEventHandler( m_popupExtraHandler );
|
|
|
|
popupInterface->m_iFlags |= wxPGCP_IFLAG_CREATED;
|
|
}
|
|
|
|
void wxPGComboControlBase::SetPopup( wxPGComboPopup* iface )
|
|
{
|
|
delete m_popupInterface;
|
|
delete m_winPopup;
|
|
|
|
m_popupInterface = iface;
|
|
|
|
#if ALLOW_FAKE_POPUP
|
|
m_fakePopupUsage = 0;
|
|
#endif
|
|
|
|
if ( !iface->LazyCreate() || m_winPopup )
|
|
{
|
|
CreatePopup();
|
|
/*
|
|
m_winPopup = new wxPGComboPopupWindow( this, wxNO_BORDER );
|
|
|
|
// Create popup right away
|
|
iface->Create(m_winPopup);
|
|
m_popup = iface->GetControl();
|
|
m_popupExtraHandler = new wxPGComboPopupExtraEventHandler(this);
|
|
m_popup->PushEventHandler( m_popupExtraHandler );
|
|
|
|
// Add interface as event handler
|
|
//m_popup->PushEventHandler( iface );
|
|
*/
|
|
|
|
// FIXME: This bypasses wxGTK popupwindow bug
|
|
// (i.e. window is not initially hidden when it should be)
|
|
m_winPopup->Hide();
|
|
|
|
#if ALLOW_FAKE_POPUP
|
|
m_fakePopupUsage = 1;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
m_popup = (wxWindow*) NULL;
|
|
}
|
|
|
|
// This must be after creation
|
|
if ( m_valueString.length() )
|
|
iface->SetStringValue(m_valueString);
|
|
|
|
}
|
|
|
|
void wxPGComboControlBase::OnButtonClick()
|
|
{
|
|
// Derived classes can override this method for totally custom
|
|
// popup action
|
|
ShowPopup();
|
|
}
|
|
|
|
void wxPGComboControlBase::ShowPopup()
|
|
{
|
|
wxCHECK_RET( m_popupInterface, wxT("no popup interface set for wxComboControl") );
|
|
wxCHECK_RET( !IsPopupShown(), wxT("popup window already shown") );
|
|
|
|
SetFocus();
|
|
|
|
// Space above and below
|
|
int screenHeight;
|
|
wxPoint scrPos;
|
|
int spaceAbove;
|
|
int spaceBelow;
|
|
int maxHeightPopup;
|
|
wxSize ctrlSz = GetSize();
|
|
|
|
#if ALLOW_FAKE_POPUP
|
|
|
|
int existingHeight = 200;
|
|
if ( m_popup )
|
|
existingHeight = m_popup->GetSize().y;
|
|
|
|
int screenWidth;
|
|
GetParent()->GetClientSize(&screenWidth,&screenHeight);
|
|
screenWidth -= 2;
|
|
scrPos = GetPosition();
|
|
|
|
spaceAbove = scrPos.y - 2;
|
|
spaceBelow = screenHeight - spaceAbove - ctrlSz.y - 4;
|
|
|
|
maxHeightPopup = spaceBelow;
|
|
if ( spaceAbove > spaceBelow )
|
|
maxHeightPopup = spaceAbove;
|
|
|
|
if ( maxHeightPopup >= existingHeight )
|
|
{
|
|
if ( m_winPopup && m_fakePopupUsage!=2 )
|
|
{
|
|
delete m_winPopup;
|
|
m_winPopup = (wxWindow*) NULL;
|
|
m_popup = (wxWindow*) NULL;
|
|
}
|
|
m_fakePopupUsage = 2;
|
|
}
|
|
else
|
|
{
|
|
if ( m_winPopup && m_fakePopupUsage!=1 )
|
|
{
|
|
delete m_winPopup;
|
|
m_winPopup = (wxWindow*) NULL;
|
|
m_popup = (wxWindow*) NULL;
|
|
}
|
|
m_fakePopupUsage = 1;
|
|
#else
|
|
{
|
|
#endif
|
|
|
|
screenHeight = wxSystemSettings::GetMetric( wxSYS_SCREEN_Y );
|
|
scrPos = GetParent()->ClientToScreen(GetPosition());
|
|
|
|
spaceAbove = scrPos.y;
|
|
spaceBelow = screenHeight - spaceAbove - ctrlSz.y;
|
|
|
|
maxHeightPopup = spaceBelow;
|
|
if ( spaceAbove > spaceBelow )
|
|
maxHeightPopup = spaceAbove;
|
|
|
|
}
|
|
|
|
// Width
|
|
int widthPopup = ctrlSz.x + m_extLeft + m_extRight;
|
|
|
|
if ( widthPopup < m_widthMinPopup )
|
|
widthPopup = m_widthMinPopup;
|
|
|
|
wxWindow* winPopup = m_winPopup;
|
|
wxWindow* popup;
|
|
|
|
// Need to disable tab traversal of parent
|
|
//
|
|
// NB: This is to fix a bug in wxMSW. In theory it could also be fixed
|
|
// by, for instance, adding check to window.cpp:wxWindowMSW::MSWProcessMessage
|
|
// that if transient popup is open, then tab traversal is to be ignored.
|
|
// However, I think this code would still be needed for cases where
|
|
// transient popup doesn't work yet (wxWINCE?).
|
|
wxWindow* parent = GetParent();
|
|
int parentFlags = parent->GetWindowStyle();
|
|
if ( parentFlags & wxTAB_TRAVERSAL )
|
|
{
|
|
parent->SetWindowStyle( parentFlags & ~(wxTAB_TRAVERSAL) );
|
|
m_iFlags |= wxPGCC_IFLAG_PARENT_TAB_TRAVERSAL;
|
|
}
|
|
|
|
if ( !winPopup )
|
|
{
|
|
#if ALLOW_FAKE_POPUP
|
|
if ( m_fakePopupUsage == 2 )
|
|
{
|
|
winPopup = new wxWindow();
|
|
#ifdef __WXMSW__
|
|
// Only wxMSW supports this
|
|
winPopup->Hide();
|
|
#endif
|
|
winPopup->Create( GetParent(), -1 );
|
|
m_winPopup = winPopup;
|
|
}
|
|
#endif
|
|
CreatePopup();
|
|
winPopup = m_winPopup;
|
|
popup = m_popup;
|
|
}
|
|
else
|
|
{
|
|
popup = m_popup;
|
|
}
|
|
|
|
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();
|
|
|
|
#if ALLOW_FAKE_POPUP
|
|
// Make sure fake popup didn't get too big
|
|
if ( m_fakePopupUsage == 2 && popup->GetSize().x > screenWidth )
|
|
{
|
|
popup->SetSize(screenWidth-2,popup->GetSize().y);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Reposition and resize popup window
|
|
//
|
|
|
|
wxSize szp = popup->GetSize();
|
|
|
|
int popupX;
|
|
int popupY = scrPos.y + ctrlSz.y;
|
|
|
|
// Anchor popup to the side the dropbutton is on
|
|
if ( m_btnSide == wxRIGHT )
|
|
popupX = scrPos.x + ctrlSz.x + m_extRight- szp.x;
|
|
else
|
|
popupX = scrPos.x - m_extLeft;
|
|
|
|
#if ALLOW_FAKE_POPUP
|
|
if ( m_fakePopupUsage == 2 )
|
|
{
|
|
if ( spaceBelow < szp.y )
|
|
{
|
|
if ( spaceAbove > spaceBelow )
|
|
{
|
|
if ( szp.y > spaceAbove )
|
|
{
|
|
popup->SetSize(szp.x,spaceAbove);
|
|
szp.y = spaceAbove;
|
|
}
|
|
popupY = scrPos.y - szp.y;
|
|
}
|
|
else
|
|
{
|
|
if ( szp.y > spaceBelow )
|
|
{
|
|
popup->SetSize(szp.x,spaceBelow);
|
|
szp.y = spaceBelow;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
if ( spaceBelow < szp.y )
|
|
{
|
|
popupY = scrPos.y - szp.y;
|
|
}
|
|
|
|
// Move to position
|
|
//wxLogDebug(wxT("popup scheduled position1: %i,%i"),ptp.x,ptp.y);
|
|
//wxLogDebug(wxT("popup position1: %i,%i"),winPopup->GetPosition().x,winPopup->GetPosition().y);
|
|
|
|
// Some platforms (GTK) may need these two to be separate
|
|
winPopup->SetSize( szp.x, szp.y );
|
|
winPopup->Move( popupX, popupY );
|
|
|
|
//wxLogDebug(wxT("popup position2: %i,%i"),winPopup->GetPosition().x,winPopup->GetPosition().y);
|
|
|
|
m_popup = popup;
|
|
|
|
// Set string selection (must be this way instead of SetStringSelection)
|
|
if ( m_text )
|
|
{
|
|
if ( !(m_iFlags & wxPGCC_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
|
|
// FIXME: But in that case, would m_isPopupShown need to go before this?
|
|
Refresh();
|
|
}
|
|
|
|
// This must be after SetStringValue
|
|
m_isPopupShown = true;
|
|
|
|
// Show it
|
|
#if USE_TRANSIENT_POPUP
|
|
((wxPopupTransientWindow*)winPopup)->Popup(popup);
|
|
#else
|
|
winPopup->Show();
|
|
#endif
|
|
|
|
#if INSTALL_TOPLEV_HANDLER
|
|
// If our real popup is wxDialog, then only install handler
|
|
// incase of fake popup.
|
|
#if !wxUSE_POPUPWIN
|
|
if ( m_fakePopupUsage != 2 )
|
|
{
|
|
if ( m_toplevEvtHandler )
|
|
{
|
|
delete m_toplevEvtHandler;
|
|
m_toplevEvtHandler = (wxEvtHandler*) NULL;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// Put top level window event handler into place
|
|
if ( !m_toplevEvtHandler )
|
|
m_toplevEvtHandler = new wxPGComboFrameEventHandler(this);
|
|
|
|
wxWindow* toplev = ::wxGetTopLevelParent( this );
|
|
wxASSERT( toplev );
|
|
((wxPGComboFrameEventHandler*)m_toplevEvtHandler)->OnPopup();
|
|
toplev->PushEventHandler( m_toplevEvtHandler );
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
void wxPGComboControlBase::OnPopupDismiss()
|
|
{
|
|
// Just in case, avoid double dismiss
|
|
if ( !m_isPopupShown )
|
|
return;
|
|
|
|
// *Must* set this before focus etc.
|
|
m_isPopupShown = false;
|
|
|
|
// Inform popup control itself
|
|
m_popupInterface->OnDismiss();
|
|
|
|
//((wxComboDropButton*)m_btn)->SetPopup( (wxWindow*) NULL );
|
|
|
|
if ( m_popupExtraHandler )
|
|
((wxPGComboPopupExtraEventHandler*)m_popupExtraHandler)->OnPopupDismiss();
|
|
|
|
#if INSTALL_TOPLEV_HANDLER
|
|
// Remove top level window event handler
|
|
if ( m_toplevEvtHandler )
|
|
{
|
|
wxWindow* toplev = ::wxGetTopLevelParent( this );
|
|
if ( toplev )
|
|
toplev->RemoveEventHandler( m_toplevEvtHandler );
|
|
}
|
|
#endif
|
|
|
|
#if !wxUSE_POPUPWIN
|
|
if ( m_fakePopupUsage != 2 )
|
|
GetParent()->SetFocus();
|
|
#endif
|
|
|
|
m_timeCanAcceptClick = ::wxGetLocalTimeMillis() + 150;
|
|
|
|
// If cursor not on dropdown button, then clear its state
|
|
// (technically not required by all ports, but do it for all just in case)
|
|
if ( !m_btnArea.wxPGRectContains(ScreenToClient(::wxGetMousePosition())) )
|
|
m_btnState = 0;
|
|
|
|
// Return parent's tab traversal flag.
|
|
// See ShowPopup for notes.
|
|
if ( m_iFlags & wxPGCC_IFLAG_PARENT_TAB_TRAVERSAL )
|
|
{
|
|
wxWindow* parent = GetParent();
|
|
parent->SetWindowStyle( parent->GetWindowStyle() | wxTAB_TRAVERSAL );
|
|
m_iFlags &= ~(wxPGCC_IFLAG_PARENT_TAB_TRAVERSAL);
|
|
}
|
|
|
|
// refresh control (necessary even if m_text)
|
|
Refresh();
|
|
|
|
}
|
|
|
|
void wxPGComboControlBase::HidePopup()
|
|
{
|
|
// Should be able to call this without popup interface
|
|
//wxCHECK_RET( m_popupInterface, _T("no popup interface") );
|
|
if ( !m_isPopupShown )
|
|
return;
|
|
|
|
// transfer value and show it in textctrl, if any
|
|
SetValue( m_popupInterface->GetStringValue() );
|
|
|
|
#if USE_TRANSIENT_POPUP
|
|
((wxPopupTransientWindow*)m_winPopup)->Dismiss();
|
|
#else
|
|
m_winPopup->Hide();
|
|
#endif
|
|
|
|
OnPopupDismiss();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// customization methods
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxPGComboControlBase::SetButtonPosition( int width, int height,
|
|
int side, int spacingX )
|
|
{
|
|
m_btnWid = width;
|
|
m_btnHei = height;
|
|
m_btnSide = side;
|
|
m_btnSpacingX = spacingX;
|
|
|
|
RecalcAndRefresh();
|
|
}
|
|
|
|
void wxPGComboControlBase::SetButtonBitmaps( const wxBitmap& bmpNormal,
|
|
bool blankButtonBg,
|
|
const wxBitmap& bmpPressed,
|
|
const wxBitmap& bmpHover,
|
|
const wxBitmap& bmpDisabled )
|
|
{
|
|
m_bmpNormal = bmpNormal;
|
|
m_blankButtonBg = blankButtonBg;
|
|
|
|
if ( bmpPressed.Ok() )
|
|
m_bmpPressed = bmpPressed;
|
|
else
|
|
m_bmpPressed = bmpNormal;
|
|
|
|
if ( bmpHover.Ok() )
|
|
m_bmpHover = bmpHover;
|
|
else
|
|
m_bmpHover = bmpNormal;
|
|
|
|
if ( bmpDisabled.Ok() )
|
|
m_bmpDisabled = bmpDisabled;
|
|
else
|
|
m_bmpDisabled = bmpNormal;
|
|
|
|
RecalcAndRefresh();
|
|
}
|
|
|
|
void wxPGComboControlBase::SetCustomPaintWidth( int width )
|
|
{
|
|
if ( m_text )
|
|
{
|
|
// move textctrl accordingly
|
|
wxRect r = m_text->GetRect();
|
|
int inc = width - m_widthCustomPaint;
|
|
r.x += inc;
|
|
r.width -= inc;
|
|
m_text->SetSize( r );
|
|
}
|
|
|
|
m_widthCustomPaint = width;
|
|
|
|
RecalcAndRefresh();
|
|
}
|
|
|
|
void wxPGComboControlBase::SetTextIndent( int indent )
|
|
{
|
|
if ( indent < 0 )
|
|
{
|
|
m_absIndent = GetNativeTextIndent();
|
|
m_iFlags &= ~(wxPGCC_IFLAG_INDENT_SET);
|
|
}
|
|
else
|
|
{
|
|
m_absIndent = indent;
|
|
m_iFlags |= wxPGCC_IFLAG_INDENT_SET;
|
|
}
|
|
|
|
RecalcAndRefresh();
|
|
}
|
|
|
|
wxCoord wxPGComboControlBase::GetNativeTextIndent() const
|
|
{
|
|
return DEFAULT_TEXT_INDENT;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// methods forwarded to wxTextCtrl
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxString wxPGComboControlBase::GetValue() const
|
|
{
|
|
if ( m_text )
|
|
return m_text->GetValue();
|
|
return m_valueString;
|
|
}
|
|
|
|
void wxPGComboControlBase::SetValue(const wxString& value)
|
|
{
|
|
if ( m_text )
|
|
{
|
|
m_text->SetValue(value);
|
|
if ( !(m_iFlags & wxPGCC_NO_TEXT_AUTO_SELECT) )
|
|
m_text->SelectAll();
|
|
}
|
|
|
|
// Since wxPGComboPopup may want to paint the combo as well, we need
|
|
// to set the string value here (as well as sometimes in ShowPopup).
|
|
if ( m_valueString != value && m_popupInterface )
|
|
{
|
|
m_popupInterface->SetStringValue(value);
|
|
}
|
|
|
|
m_valueString = value;
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void wxPGComboControlBase::Copy()
|
|
{
|
|
if ( m_text )
|
|
m_text->Copy();
|
|
}
|
|
|
|
void wxPGComboControlBase::Cut()
|
|
{
|
|
if ( m_text )
|
|
m_text->Cut();
|
|
}
|
|
|
|
void wxPGComboControlBase::Paste()
|
|
{
|
|
if ( m_text )
|
|
m_text->Paste();
|
|
}
|
|
|
|
void wxPGComboControlBase::SetInsertionPoint(long pos)
|
|
{
|
|
if ( m_text )
|
|
m_text->SetInsertionPoint(pos);
|
|
}
|
|
|
|
void wxPGComboControlBase::SetInsertionPointEnd()
|
|
{
|
|
if ( m_text )
|
|
m_text->SetInsertionPointEnd();
|
|
}
|
|
|
|
long wxPGComboControlBase::GetInsertionPoint() const
|
|
{
|
|
if ( m_text )
|
|
return m_text->GetInsertionPoint();
|
|
|
|
return 0;
|
|
}
|
|
|
|
long wxPGComboControlBase::GetLastPosition() const
|
|
{
|
|
if ( m_text )
|
|
return m_text->GetLastPosition();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void wxPGComboControlBase::Replace(long from, long to, const wxString& value)
|
|
{
|
|
if ( m_text )
|
|
m_text->Replace(from, to, value);
|
|
}
|
|
|
|
void wxPGComboControlBase::Remove(long from, long to)
|
|
{
|
|
if ( m_text )
|
|
m_text->Remove(from, to);
|
|
}
|
|
|
|
void wxPGComboControlBase::SetSelection(long from, long to)
|
|
{
|
|
if ( m_text )
|
|
m_text->SetSelection(from, to);
|
|
}
|
|
|
|
void wxPGComboControlBase::Undo()
|
|
{
|
|
if ( m_text )
|
|
m_text->Undo();
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPGGenericComboControl
|
|
// ----------------------------------------------------------------------------
|
|
|
|
BEGIN_EVENT_TABLE(wxPGGenericComboControl, wxPGComboControlBase)
|
|
//EVT_SIZE(wxPGGenericComboControl::OnSizeEvent)
|
|
EVT_PAINT(wxPGGenericComboControl::OnPaintEvent)
|
|
EVT_MOUSE_EVENTS(wxPGGenericComboControl::OnMouseEvent)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxPGGenericComboControl, wxPGComboControlBase)
|
|
|
|
void wxPGGenericComboControl::Init()
|
|
{
|
|
}
|
|
|
|
bool wxPGGenericComboControl::Create(wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxString& value,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxValidator& validator,
|
|
const wxString& name)
|
|
{
|
|
|
|
// Set border
|
|
long border = style & wxBORDER_MASK;
|
|
|
|
#if defined(__WXUNIVERSAL__)
|
|
if ( !border )
|
|
border = wxBORDER_SIMPLE;
|
|
#elif defined(__WXMSW__)
|
|
if ( !border )
|
|
border = wxBORDER_SIMPLE;
|
|
#else
|
|
if ( !border )
|
|
{
|
|
border = wxBORDER_NONE;
|
|
m_widthCustomBorder = 1;
|
|
}
|
|
|
|
Customize( wxPGCC_BUTTON_OUTSIDE_BORDER |
|
|
wxPGCC_NO_TEXT_AUTO_SELECT |
|
|
wxPGCC_BUTTON_STAYS_DOWN );
|
|
|
|
#endif
|
|
|
|
style = (style & ~(wxBORDER_MASK)) | border;
|
|
|
|
// create main window
|
|
if ( !wxPGComboControlBase::Create(parent,
|
|
id,
|
|
value,
|
|
wxDefaultPosition,
|
|
wxDefaultSize,
|
|
style | wxFULL_REPAINT_ON_RESIZE,
|
|
wxDefaultValidator,
|
|
name) )
|
|
return false;
|
|
|
|
// Create textctrl, if necessary
|
|
CreateTextCtrl( wxBORDER_NONE, validator );
|
|
|
|
// Add keyboard input handlers for main control and textctrl
|
|
InstallInputHandlers();
|
|
|
|
// Set background
|
|
SetBackgroundStyle( wxBG_STYLE_CUSTOM ); // for double-buffering
|
|
|
|
// SetSize should be called last
|
|
SetSize(pos.x,pos.y,size.x,size.y);
|
|
|
|
return true;
|
|
}
|
|
|
|
wxPGGenericComboControl::~wxPGGenericComboControl()
|
|
{
|
|
}
|
|
|
|
void wxPGGenericComboControl::OnResize()
|
|
{
|
|
|
|
// Recalculates button and textctrl areas
|
|
CalculateAreas(DEFAULT_DROPBUTTON_WIDTH);
|
|
|
|
#if 0
|
|
// Move separate button control, if any, to correct position
|
|
if ( m_btn )
|
|
{
|
|
wxSize sz = GetClientSize();
|
|
m_btn->SetSize( m_btnArea.x + m_btnSpacingX,
|
|
(sz.y-m_btnSize.y)/2,
|
|
m_btnSize.x,
|
|
m_btnSize.y );
|
|
}
|
|
#endif
|
|
|
|
// Move textctrl, if any, accordingly
|
|
PositionTextCtrl( TEXTCTRLXADJUST, TEXTCTRLYADJUST );
|
|
}
|
|
|
|
void wxPGGenericComboControl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
|
|
{
|
|
wxSize sz = GetClientSize();
|
|
|
|
#if !wxCHECK_VERSION(2, 7, 1)
|
|
// If size is larger, recalculate double buffer bitmap
|
|
if ( !gs_doubleBuffer ||
|
|
sz.x > gs_doubleBuffer->GetWidth() ||
|
|
sz.y > gs_doubleBuffer->GetHeight() )
|
|
{
|
|
delete gs_doubleBuffer;
|
|
gs_doubleBuffer = new wxBitmap(sz.x+25,sz.y);
|
|
}
|
|
|
|
wxBufferedPaintDC dc(this,*gs_doubleBuffer);
|
|
#else
|
|
wxAutoBufferedPaintDC dc(this);
|
|
#endif
|
|
|
|
const wxRect& rectb = m_btnArea;
|
|
wxRect rect = m_tcArea;
|
|
|
|
// artificial simple border
|
|
if ( m_widthCustomBorder )
|
|
{
|
|
int customBorder = m_widthCustomBorder;
|
|
|
|
// Set border colour
|
|
wxPen pen1( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT),
|
|
customBorder,
|
|
wxSOLID );
|
|
dc.SetPen( pen1 );
|
|
|
|
// area around both controls
|
|
wxRect rect2(0,0,sz.x,sz.y);
|
|
if ( m_iFlags & wxPGCC_IFLAG_BUTTON_OUTSIDE )
|
|
{
|
|
rect2 = m_tcArea;
|
|
if ( customBorder == 1 )
|
|
{
|
|
rect2.Inflate(1);
|
|
}
|
|
else
|
|
{
|
|
#ifdef __WXGTK__
|
|
rect2.x -= 1;
|
|
rect2.y -= 1;
|
|
#else
|
|
rect2.x -= customBorder;
|
|
rect2.y -= customBorder;
|
|
#endif
|
|
rect2.width += 1 + customBorder;
|
|
rect2.height += 1 + customBorder;
|
|
}
|
|
}
|
|
|
|
dc.SetBrush( *wxTRANSPARENT_BRUSH );
|
|
dc.DrawRectangle(rect2);
|
|
}
|
|
|
|
wxColour winCol = GetBackgroundColour();
|
|
dc.SetBrush(winCol);
|
|
dc.SetPen(winCol);
|
|
|
|
//wxLogDebug(wxT("hei: %i tcy: %i tchei: %i"),GetClientSize().y,m_tcArea.y,m_tcArea.height);
|
|
//wxLogDebug(wxT("btnx: %i tcx: %i tcwid: %i"),m_btnArea.x,m_tcArea.x,m_tcArea.width);
|
|
|
|
// clear main background
|
|
dc.DrawRectangle(rect);
|
|
|
|
if ( !m_btn )
|
|
// Standard button rendering
|
|
DrawButton(dc, rectb);
|
|
|
|
// paint required portion on the control
|
|
if ( !m_text || m_widthCustomPaint )
|
|
{
|
|
wxASSERT( m_widthCustomPaint >= 0 );
|
|
|
|
// this is intentionally here to allow drawed rectangle's
|
|
// right edge to be hidden
|
|
if ( m_text )
|
|
rect.width = m_widthCustomPaint;
|
|
|
|
dc.SetFont( GetFont() );
|
|
|
|
dc.SetClippingRegion(rect);
|
|
m_popupInterface->PaintComboControl(dc, rect);
|
|
}
|
|
}
|
|
|
|
void wxPGGenericComboControl::OnMouseEvent( wxMouseEvent& event )
|
|
{
|
|
bool isOnButtonArea = m_btnArea.wxPGRectContains(event.m_x,event.m_y);
|
|
int handlerFlags = isOnButtonArea ? wxPGCC_MF_ON_BUTTON : 0;
|
|
|
|
// Preprocessing fabricates double-clicks and prevents
|
|
// (it may also do other common things in future)
|
|
if ( PreprocessMouseEvent(event,handlerFlags) )
|
|
return;
|
|
|
|
#ifdef __WXMSW__
|
|
const bool ctrlIsButton = true;
|
|
#else
|
|
const bool ctrlIsButton = false;
|
|
#endif
|
|
|
|
if ( ctrlIsButton &&
|
|
(m_windowStyle & (wxPGCC_DCLICK_CYCLES|wxCB_READONLY)) == wxCB_READONLY )
|
|
{
|
|
// if no textctrl and no special double-click, then the entire control acts
|
|
// as a button
|
|
handlerFlags |= wxPGCC_MF_ON_BUTTON;
|
|
if ( HandleButtonMouseEvent(event,handlerFlags) )
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if ( isOnButtonArea || m_btnState & wxCONTROL_PRESSED )
|
|
{
|
|
if ( HandleButtonMouseEvent(event,handlerFlags) )
|
|
return;
|
|
}
|
|
else if ( m_btnState )
|
|
{
|
|
// otherwise need to clear the hover status
|
|
m_btnState = 0;
|
|
RefreshRect(m_btnArea);
|
|
}
|
|
}
|
|
|
|
//
|
|
// This will handle left_down and left_dclick events outside button in a Windows/GTK-like manner.
|
|
// See header file for further information on this method.
|
|
HandleNormalMouseEvent(event);
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxComboControl
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
|
|
|
|
// Change to #if 1 to include tmschema.h for easier testing of theme
|
|
// parameters.
|
|
#if 0
|
|
#include <tmschema.h>
|
|
#include <VSStyle.h>
|
|
#else
|
|
//----------------------------------
|
|
#define EP_EDITTEXT 1
|
|
#define ETS_NORMAL 1
|
|
#define ETS_HOT 2
|
|
#define ETS_SELECTED 3
|
|
#define ETS_DISABLED 4
|
|
#define ETS_FOCUSED 5
|
|
#define ETS_READONLY 6
|
|
#define ETS_ASSIST 7
|
|
#define TMT_FILLCOLOR 3802
|
|
#define TMT_TEXTCOLOR 3803
|
|
#define TMT_BORDERCOLOR 3801
|
|
#define TMT_EDGEFILLCOLOR 3808
|
|
#define TMT_BGTYPE 4001
|
|
|
|
#define BT_IMAGEFILE 0
|
|
#define BT_BORDERFILL 1
|
|
|
|
#define CP_DROPDOWNBUTTON 1
|
|
#define CP_BACKGROUND 2 // This and above are Vista and later only
|
|
#define CP_TRANSPARENTBACKGROUND 3
|
|
#define CP_BORDER 4
|
|
#define CP_READONLY 5
|
|
#define CP_DROPDOWNBUTTONRIGHT 6
|
|
#define CP_DROPDOWNBUTTONLEFT 7
|
|
#define CP_CUEBANNER 8
|
|
|
|
#define CBXS_NORMAL 1
|
|
#define CBXS_HOT 2
|
|
#define CBXS_PRESSED 3
|
|
#define CBXS_DISABLED 4
|
|
|
|
#define CBXSR_NORMAL 1
|
|
#define CBXSR_HOT 2
|
|
#define CBXSR_PRESSED 3
|
|
#define CBXSR_DISABLED 4
|
|
|
|
#define CBXSL_NORMAL 1
|
|
#define CBXSL_HOT 2
|
|
#define CBXSL_PRESSED 3
|
|
#define CBXSL_DISABLED 4
|
|
|
|
#define CBTBS_NORMAL 1
|
|
#define CBTBS_HOT 2
|
|
#define CBTBS_DISABLED 3
|
|
#define CBTBS_FOCUSED 4
|
|
|
|
#define CBB_NORMAL 1
|
|
#define CBB_HOT 2
|
|
#define CBB_FOCUSED 3
|
|
#define CBB_DISABLED 4
|
|
|
|
#define CBRO_NORMAL 1
|
|
#define CBRO_HOT 2
|
|
#define CBRO_PRESSED 3
|
|
#define CBRO_DISABLED 4
|
|
|
|
#define CBCB_NORMAL 1
|
|
#define CBCB_HOT 2
|
|
#define CBCB_PRESSED 3
|
|
#define CBCB_DISABLED 4
|
|
|
|
#endif
|
|
|
|
#define NATIVE_TEXT_INDENT_XP 4
|
|
#define NATIVE_TEXT_INDENT_CLASSIC 2
|
|
|
|
#define TEXTCTRLXADJUST_XP 0
|
|
#define TEXTCTRLYADJUST_XP 4
|
|
#define TEXTCTRLXADJUST_CLASSIC 0
|
|
#define TEXTCTRLYADJUST_CLASSIC 4
|
|
|
|
|
|
BEGIN_EVENT_TABLE(wxPGComboControl, wxPGComboControlBase)
|
|
EVT_PAINT(wxPGComboControl::OnPaintEvent)
|
|
EVT_MOUSE_EVENTS(wxPGComboControl::OnMouseEvent)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxPGComboControl, wxPGComboControlBase)
|
|
|
|
void wxPGComboControl::Init()
|
|
{
|
|
}
|
|
|
|
bool wxPGComboControl::Create(wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxString& value,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxValidator& validator,
|
|
const wxString& name)
|
|
{
|
|
|
|
// Set border
|
|
long border = style & wxBORDER_MASK;
|
|
|
|
wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
|
|
|
|
if ( !border )
|
|
{
|
|
// For XP, have 1-width custom border, for older version use sunken
|
|
if ( theme )
|
|
{
|
|
border = wxBORDER_NONE;
|
|
m_widthCustomBorder = 1;
|
|
}
|
|
else
|
|
border = wxBORDER_SUNKEN;
|
|
|
|
style = (style & ~(wxBORDER_MASK)) | border;
|
|
}
|
|
|
|
//Customize( wxPGCC_BUTTON_OUTSIDE_BORDER );
|
|
|
|
// create main window
|
|
if ( !wxPGComboControlBase::Create(parent,
|
|
id,
|
|
value,
|
|
wxDefaultPosition,
|
|
wxDefaultSize,
|
|
style | wxFULL_REPAINT_ON_RESIZE,
|
|
wxDefaultValidator,
|
|
name) )
|
|
return false;
|
|
|
|
if ( theme )
|
|
{
|
|
#if wxCHECK_VERSION(2, 8, 0)
|
|
const bool isVista = (::wxGetWinVersion() >= wxWinVersion_6);
|
|
#else
|
|
int Major = 0;
|
|
int family = wxGetOsVersion(&Major, NULL);
|
|
const bool isVista = ((family == wxWINDOWS_NT) && (Major >= 6));
|
|
#endif
|
|
|
|
if ( isVista )
|
|
m_iFlags |= wxPGCC_BUTTON_STAYS_DOWN;
|
|
}
|
|
|
|
// Create textctrl, if necessary
|
|
CreateTextCtrl( wxNO_BORDER, validator );
|
|
|
|
// Add keyboard input handlers for main control and textctrl
|
|
InstallInputHandlers();
|
|
|
|
// Prepare background for double-buffering
|
|
SetBackgroundStyle( wxBG_STYLE_CUSTOM );
|
|
|
|
// SetSize should be called last
|
|
SetSize(pos.x,pos.y,size.x,size.y);
|
|
|
|
return true;
|
|
}
|
|
|
|
wxPGComboControl::~wxPGComboControl()
|
|
{
|
|
}
|
|
|
|
void wxPGComboControl::OnThemeChange()
|
|
{
|
|
wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
|
|
if ( theme )
|
|
{
|
|
wxUxThemeHandle hTheme(this, L"COMBOBOX");
|
|
|
|
COLORREF col;
|
|
theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_FILLCOLOR,&col);
|
|
SetBackgroundColour(wxRGBToColour(col));
|
|
theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&col);
|
|
SetForegroundColour(wxRGBToColour(col));
|
|
}
|
|
else
|
|
{
|
|
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
|
SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
|
}
|
|
}
|
|
|
|
//void wxPGComboControl::OnSizeEvent( wxSizeEvent& event )
|
|
void wxPGComboControl::OnResize()
|
|
{
|
|
//
|
|
// Recalculates button and textctrl areas
|
|
|
|
int textCtrlXAdjust;
|
|
int textCtrlYAdjust;
|
|
|
|
if ( wxUxThemeEngine::GetIfActive() )
|
|
{
|
|
textCtrlXAdjust = TEXTCTRLXADJUST_XP;
|
|
textCtrlYAdjust = TEXTCTRLYADJUST_XP;
|
|
}
|
|
else
|
|
{
|
|
textCtrlXAdjust = TEXTCTRLXADJUST_CLASSIC;
|
|
textCtrlYAdjust = TEXTCTRLYADJUST_CLASSIC;
|
|
}
|
|
|
|
// Technically Classic Windows style combo has more narrow button,
|
|
// but the native renderer doesn't paint it well like that.
|
|
int btnWidth = 17;
|
|
CalculateAreas(btnWidth);
|
|
|
|
// Position textctrl using standard routine
|
|
PositionTextCtrl(textCtrlXAdjust,textCtrlYAdjust);
|
|
}
|
|
|
|
/*
|
|
// Draws non-XP GUI dotted line around the focus area
|
|
static void wxMSWDrawFocusRect( wxDC& dc, const wxRect& rect )
|
|
{
|
|
#if !defined(__WXWINCE__)
|
|
dc.SetLogicalFunction(wxINVERT);
|
|
|
|
wxPen pen(*wxBLACK,1,wxDOT);
|
|
pen.SetCap(wxCAP_BUTT);
|
|
dc.SetPen(pen);
|
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
|
|
|
dc.DrawRectangle(rect);
|
|
|
|
dc.SetLogicalFunction(wxCOPY);
|
|
#else
|
|
dc.SetLogicalFunction(wxINVERT);
|
|
|
|
dc.SetPen(wxPen(*wxBLACK,1,wxDOT));
|
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
|
|
|
dc.DrawRectangle(rect);
|
|
|
|
dc.SetLogicalFunction(wxCOPY);
|
|
#endif
|
|
}
|
|
*/
|
|
/*
|
|
// draw focus background on area in a way typical on platform
|
|
void wxPGComboControl::DrawFocusBackground( wxDC& dc, const wxRect& rect, int flags )
|
|
{
|
|
wxUxThemeEngine* theme = (wxUxThemeEngine*) NULL;
|
|
wxUxThemeHandle hTheme(this, L"COMBOBOX");
|
|
//COLORREF cref;
|
|
|
|
wxSize sz = GetClientSize();
|
|
bool isEnabled;
|
|
bool isFocused; // also selected
|
|
|
|
// For smaller size control (and for disabled background) use less spacing
|
|
int focusSpacingX;
|
|
int focusSpacingY;
|
|
|
|
if ( !(flags & wxCONTROL_ISSUBMENU) )
|
|
{
|
|
// Drawing control
|
|
isEnabled = IsEnabled();
|
|
isFocused = ShouldDrawFocus();
|
|
|
|
// Windows-style: for smaller size control (and for disabled background) use less spacing
|
|
if ( hTheme )
|
|
{
|
|
// WinXP Theme
|
|
focusSpacingX = isEnabled ? 2 : 1;
|
|
focusSpacingY = sz.y > (GetCharHeight()+2) && isEnabled ? 2 : 1;
|
|
}
|
|
else
|
|
{
|
|
// Classic Theme
|
|
if ( isEnabled )
|
|
{
|
|
focusSpacingX = 1;
|
|
focusSpacingY = 1;
|
|
}
|
|
else
|
|
{
|
|
focusSpacingX = 0;
|
|
focusSpacingY = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Drawing a list item
|
|
isEnabled = true; // they are never disabled
|
|
isFocused = flags & wxCONTROL_SELECTED ? true : false;
|
|
|
|
focusSpacingX = 0;
|
|
focusSpacingY = 0;
|
|
}
|
|
|
|
// Set the background sub-rectangle for selection, disabled etc
|
|
wxRect selRect(rect);
|
|
selRect.y += focusSpacingY;
|
|
selRect.height -= (focusSpacingY*2);
|
|
int wcp = 0;
|
|
|
|
if ( !(flags & wxCONTROL_ISSUBMENU) )
|
|
wcp += m_widthCustomPaint;
|
|
|
|
selRect.x += wcp + focusSpacingX;
|
|
selRect.width -= wcp + (focusSpacingX*2);
|
|
|
|
if ( hTheme )
|
|
theme = wxUxThemeEngine::GetIfActive();
|
|
|
|
wxColour bgCol;
|
|
bool drawDottedEdge = false;
|
|
|
|
if ( isEnabled )
|
|
{
|
|
// If popup is hidden and this control is focused,
|
|
// then draw the focus-indicator (selbgcolor background etc.).
|
|
if ( isFocused )
|
|
{
|
|
#if 0
|
|
// TODO: Proper theme color getting (JMS: I don't know which parts/colors to use,
|
|
// those below don't work)
|
|
if ( hTheme )
|
|
{
|
|
theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_SELECTED,TMT_TEXTCOLOR,&cref);
|
|
dc.SetTextForeground( wxRGBToColour(cref) );
|
|
theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_SELECTED,TMT_FILLCOLOR,&cref);
|
|
bgCol = wxRGBToColour(cref);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
|
|
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
|
|
if ( m_windowStyle & wxCB_READONLY )
|
|
drawDottedEdge = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bgCol = GetBackgroundColour();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) );
|
|
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
|
|
}
|
|
|
|
dc.SetBrush(bgCol);
|
|
dc.SetPen(bgCol);
|
|
dc.DrawRectangle(selRect);
|
|
//if ( drawDottedEdge )
|
|
// wxMSWDrawFocusRect(dc,selRect);
|
|
}
|
|
*/
|
|
void wxPGComboControl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
|
|
{
|
|
// TODO: Convert drawing in this function to Windows API Code
|
|
|
|
wxSize sz = GetClientSize();
|
|
|
|
#if !wxCHECK_VERSION(2, 7, 1)
|
|
// If size is larger, recalculate double buffer bitmap
|
|
if ( !gs_doubleBuffer ||
|
|
sz.x > gs_doubleBuffer->GetWidth() ||
|
|
sz.y > gs_doubleBuffer->GetHeight() )
|
|
{
|
|
delete gs_doubleBuffer;
|
|
gs_doubleBuffer = new wxBitmap(sz.x+25,sz.y);
|
|
}
|
|
|
|
wxBufferedPaintDC dc(this,*gs_doubleBuffer);
|
|
#else
|
|
wxAutoBufferedPaintDC dc(this);
|
|
#endif
|
|
|
|
const wxRect& rectButton = m_btnArea;
|
|
wxRect rectTextField = m_tcArea;
|
|
const bool isEnabled = IsEnabled();
|
|
wxColour bgCol = GetBackgroundColour();
|
|
|
|
HDC hDc = GetHdcOf(dc);
|
|
HWND hWnd = GetHwndOf(this);
|
|
|
|
wxUxThemeEngine* theme = NULL;
|
|
wxUxThemeHandle hTheme(this, L"COMBOBOX");
|
|
|
|
if ( hTheme )
|
|
theme = wxUxThemeEngine::GetIfActive();
|
|
|
|
wxRect borderRect(0,0,sz.x,sz.y);
|
|
|
|
if ( m_iFlags & wxPGCC_IFLAG_BUTTON_OUTSIDE )
|
|
{
|
|
borderRect = m_tcArea;
|
|
borderRect.Inflate(1);
|
|
}
|
|
|
|
int drawButFlags = 0;
|
|
|
|
if ( hTheme )
|
|
{
|
|
#if wxCHECK_VERSION(2, 8, 0)
|
|
const bool useVistaComboBox = (::wxGetWinVersion() >= wxWinVersion_6);
|
|
#else
|
|
int Major = 0;
|
|
int family = wxGetOsVersion(&Major, NULL);
|
|
const bool useVistaComboBox = ((family == wxWINDOWS_NT) && (Major >= 6));
|
|
#endif
|
|
|
|
RECT rFull;
|
|
wxCopyRectToRECT(borderRect, rFull);
|
|
|
|
RECT rButton;
|
|
wxCopyRectToRECT(rectButton, rButton);
|
|
|
|
RECT rBorder;
|
|
wxCopyRectToRECT(borderRect, rBorder);
|
|
|
|
bool isNonStdButton = (m_iFlags & wxPGCC_IFLAG_BUTTON_OUTSIDE) ||
|
|
(m_iFlags & wxPGCC_IFLAG_HAS_NONSTANDARD_BUTTON);
|
|
|
|
//
|
|
// Get some states for themed drawing
|
|
int butState;
|
|
|
|
if ( !isEnabled )
|
|
{
|
|
butState = CBXS_DISABLED;
|
|
}
|
|
// Vista will display the drop-button as depressed always
|
|
// when the popup window is visilbe
|
|
else if ( (m_btnState & wxCONTROL_PRESSED) ||
|
|
(useVistaComboBox && IsPopupShown()) )
|
|
{
|
|
butState = CBXS_PRESSED;
|
|
}
|
|
else if ( m_btnState & wxCONTROL_CURRENT )
|
|
{
|
|
butState = CBXS_HOT;
|
|
}
|
|
else
|
|
{
|
|
butState = CBXS_NORMAL;
|
|
}
|
|
|
|
int comboBoxPart = 0; // For XP, use the 'default' part
|
|
RECT* rUseForBg = &rBorder;
|
|
|
|
bool drawFullButton = false;
|
|
int bgState = butState;
|
|
const bool isFocused = IsFocused();
|
|
|
|
if ( useVistaComboBox )
|
|
{
|
|
// FIXME: Either SetBackgroundColour or GetBackgroundColour
|
|
// doesn't work under Vista, so here's a temporary
|
|
// workaround.
|
|
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
|
|
|
// Draw the entire control as a single button?
|
|
/*
|
|
if ( !isNonStdButton )
|
|
{
|
|
if ( HasFlag(wxCB_READONLY) )
|
|
drawFullButton = true;
|
|
}
|
|
*/
|
|
|
|
if ( drawFullButton )
|
|
{
|
|
comboBoxPart = CP_READONLY;
|
|
rUseForBg = &rFull;
|
|
|
|
// It should be safe enough to update this flag here.
|
|
m_iFlags |= wxPGCC_FULL_BUTTON;
|
|
}
|
|
else
|
|
{
|
|
comboBoxPart = CP_BORDER;
|
|
m_iFlags &= ~wxPGCC_FULL_BUTTON;
|
|
|
|
if ( isFocused )
|
|
bgState = CBB_FOCUSED;
|
|
else
|
|
bgState = CBB_NORMAL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Draw parent's background, if necessary
|
|
RECT* rUseForTb = NULL;
|
|
|
|
if ( theme->IsThemeBackgroundPartiallyTransparent( hTheme, comboBoxPart, bgState ) )
|
|
rUseForTb = &rFull;
|
|
else if ( m_iFlags & wxPGCC_IFLAG_BUTTON_OUTSIDE )
|
|
rUseForTb = &rButton;
|
|
|
|
if ( rUseForTb )
|
|
theme->DrawThemeParentBackground( hWnd, hDc, rUseForTb );
|
|
|
|
//
|
|
// Draw the control background (including the border)
|
|
if ( m_widthCustomBorder > 0 )
|
|
{
|
|
theme->DrawThemeBackground( hTheme, hDc, comboBoxPart, bgState, rUseForBg, NULL );
|
|
}
|
|
else
|
|
{
|
|
// No border. We can't use theme, since it cannot be relied on
|
|
// to deliver borderless drawing, even with DrawThemeBackgroundEx.
|
|
dc.SetBrush(bgCol);
|
|
dc.SetPen(bgCol);
|
|
dc.DrawRectangle(borderRect);
|
|
}
|
|
|
|
//
|
|
// Draw the drop-button
|
|
if ( !isNonStdButton )
|
|
{
|
|
drawButFlags = Button_BitmapOnly;
|
|
|
|
int butPart = CP_DROPDOWNBUTTON;
|
|
|
|
if ( useVistaComboBox && m_widthCustomBorder > 0 )
|
|
{
|
|
if ( drawFullButton )
|
|
{
|
|
// We need to alter the button style slightly before
|
|
// drawing the actual button (but it was good above
|
|
// when background etc was done).
|
|
if ( butState == CBXS_HOT || butState == CBXS_PRESSED )
|
|
butState = CBXS_NORMAL;
|
|
}
|
|
|
|
if ( m_btnSide == wxRIGHT )
|
|
butPart = CP_DROPDOWNBUTTONRIGHT;
|
|
else
|
|
butPart = CP_DROPDOWNBUTTONLEFT;
|
|
|
|
}
|
|
theme->DrawThemeBackground( hTheme, hDc, butPart, butState, &rButton, NULL );
|
|
}
|
|
else if ( useVistaComboBox &&
|
|
(m_iFlags & wxPGCC_IFLAG_BUTTON_OUTSIDE) )
|
|
{
|
|
// We'll do this, because DrawThemeParentBackground
|
|
// doesn't seem to be reliable on Vista.
|
|
drawButFlags |= Button_PaintBackground;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Windows 2000 and earlier
|
|
drawButFlags = Button_PaintBackground;
|
|
|
|
dc.SetBrush(bgCol);
|
|
dc.SetPen(bgCol);
|
|
dc.DrawRectangle(borderRect);
|
|
}
|
|
|
|
// Button rendering (may only do the bitmap on button, depending on the flags)
|
|
DrawButton( dc, rectButton, drawButFlags );
|
|
|
|
// Paint required portion of the custom image on the control
|
|
if ( (!m_text || m_widthCustomPaint) )
|
|
{
|
|
wxASSERT( m_widthCustomPaint >= 0 );
|
|
|
|
// this is intentionally here to allow drawed rectangle's
|
|
// right edge to be hidden
|
|
if ( m_text )
|
|
rectTextField.width = m_widthCustomPaint;
|
|
|
|
dc.SetFont( GetFont() );
|
|
|
|
dc.SetClippingRegion(rectTextField);
|
|
m_popupInterface->PaintComboControl(dc,rectTextField);
|
|
}
|
|
}
|
|
|
|
void wxPGComboControl::OnMouseEvent( wxMouseEvent& event )
|
|
{
|
|
bool isOnButtonArea = m_btnArea.wxPGRectContains(event.m_x,event.m_y);
|
|
int handlerFlags = isOnButtonArea ? wxPGCC_MF_ON_BUTTON : 0;
|
|
|
|
// Preprocessing fabricates double-clicks and prevents
|
|
// (it may also do other common things in future)
|
|
if ( PreprocessMouseEvent(event,isOnButtonArea) )
|
|
return;
|
|
|
|
if ( (m_windowStyle & (wxPGCC_DCLICK_CYCLES|wxCB_READONLY)) == wxCB_READONLY )
|
|
{
|
|
// if no textctrl and no special double-click, then the entire control acts
|
|
// as a button
|
|
handlerFlags |= wxPGCC_MF_ON_BUTTON;
|
|
if ( HandleButtonMouseEvent(event,handlerFlags) )
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if ( isOnButtonArea || m_btnState & wxCONTROL_PRESSED )
|
|
{
|
|
if ( HandleButtonMouseEvent(event,handlerFlags) )
|
|
return;
|
|
}
|
|
else if ( m_btnState )
|
|
{
|
|
// otherwise need to clear the hover status
|
|
m_btnState = 0;
|
|
RefreshRect(m_btnArea);
|
|
}
|
|
}
|
|
|
|
//
|
|
// This will handle left_down and left_dclick events outside button in a Windows-like manner.
|
|
// See header file for further information on this method.
|
|
HandleNormalMouseEvent(event);
|
|
|
|
}
|
|
|
|
wxCoord wxPGComboControl::GetNativeTextIndent() const
|
|
{
|
|
if ( wxUxThemeEngine::GetIfActive() )
|
|
return NATIVE_TEXT_INDENT_XP;
|
|
return NATIVE_TEXT_INDENT_CLASSIC;
|
|
}
|
|
|
|
#else
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxPGComboControl, wxPGComboControlBase)
|
|
|
|
#endif // #if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPGOwnerDrawnComboBox
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxPGOwnerDrawnComboBox, wxPGComboControl)
|
|
|
|
|
|
BEGIN_EVENT_TABLE(wxPGOwnerDrawnComboBox, wxPGComboControl)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
void wxPGOwnerDrawnComboBox::Init()
|
|
{
|
|
}
|
|
|
|
bool wxPGOwnerDrawnComboBox::Create(wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxString& value,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxValidator& validator,
|
|
const wxString& name)
|
|
{
|
|
return wxPGComboControl::Create(parent,id,value,pos,size,style,validator,name);
|
|
}
|
|
|
|
wxPGOwnerDrawnComboBox::wxPGOwnerDrawnComboBox(wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxString& value,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
const wxArrayString& choices,
|
|
long style,
|
|
const wxValidator& validator,
|
|
const wxString& name)
|
|
: wxPGComboControl()
|
|
{
|
|
Init();
|
|
|
|
Create(parent,id,value,pos,size,choices,style, validator, name);
|
|
}
|
|
|
|
bool wxPGOwnerDrawnComboBox::Create(wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxString& value,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
const wxArrayString& choices,
|
|
long style,
|
|
const wxValidator& validator,
|
|
const wxString& name)
|
|
{
|
|
wxCArrayString chs(choices);
|
|
|
|
return Create(parent, id, value, pos, size, chs.GetCount(),
|
|
chs.GetStrings(), /*callback,*/ style, validator, name);
|
|
}
|
|
|
|
bool wxPGOwnerDrawnComboBox::Create(wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxString& value,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
int n,
|
|
const wxString choices[],
|
|
long style,
|
|
const wxValidator& validator,
|
|
const wxString& name)
|
|
{
|
|
|
|
if ( !Create(parent, id, value, pos, size, style,
|
|
validator, name) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
wxPGVListBoxComboPopup* iface = new wxPGVListBoxComboPopup(this);
|
|
SetPopup(iface);
|
|
m_popupInterface = iface;
|
|
|
|
// Add initial choices to the interface
|
|
iface->Populate(n,choices);
|
|
|
|
return true;
|
|
}
|
|
|
|
wxPGOwnerDrawnComboBox::~wxPGOwnerDrawnComboBox()
|
|
{
|
|
if ( m_popupInterface )
|
|
m_popupInterface->ClearClientDatas();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxPGOwnerDrawnComboBox item manipulation methods
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxPGOwnerDrawnComboBox::Clear()
|
|
{
|
|
wxASSERT( m_popupInterface );
|
|
|
|
m_popupInterface->Clear();
|
|
|
|
GetTextCtrl()->SetValue(wxEmptyString);
|
|
}
|
|
|
|
void wxPGOwnerDrawnComboBox::Delete(wxODCIndex n)
|
|
{
|
|
wxCHECK_RET( (n >= 0) && (n < GetCount()), _T("invalid index in wxPGOwnerDrawnComboBox::Delete") );
|
|
|
|
if ( GetSelection() == (int) n )
|
|
SetValue(wxEmptyString);
|
|
|
|
m_popupInterface->Delete(n);
|
|
}
|
|
|
|
wxODCCount wxPGOwnerDrawnComboBox::GetCount() const
|
|
{
|
|
wxASSERT( m_popupInterface );
|
|
return m_popupInterface->GetCount();
|
|
}
|
|
|
|
wxString wxPGOwnerDrawnComboBox::GetString(wxODCIndex n) const
|
|
{
|
|
wxCHECK_MSG( (n >= 0) && (n < GetCount()), wxEmptyString, _T("invalid index in wxPGOwnerDrawnComboBox::GetString") );
|
|
return m_popupInterface->GetString(n);
|
|
}
|
|
|
|
void wxPGOwnerDrawnComboBox::SetString(wxODCIndex n, const wxString& s)
|
|
{
|
|
wxCHECK_RET( (n >= 0) && (n < GetCount()), _T("invalid index in wxPGOwnerDrawnComboBox::SetString") );
|
|
m_popupInterface->SetString(n,s);
|
|
}
|
|
|
|
int wxPGOwnerDrawnComboBox::FindString(const wxString& s) const
|
|
{
|
|
wxASSERT( m_popupInterface );
|
|
return m_popupInterface->FindString(s);
|
|
}
|
|
|
|
void wxPGOwnerDrawnComboBox::Select(int n)
|
|
{
|
|
wxCHECK_RET( (n >= -1) && (n < (int)GetCount()), _T("invalid index in wxPGOwnerDrawnComboBox::Select") );
|
|
wxASSERT( m_popupInterface );
|
|
|
|
m_popupInterface->SetSelection(n);
|
|
|
|
wxString str;
|
|
if ( n >= 0 )
|
|
str = m_popupInterface->GetString(n);
|
|
|
|
// Refresh text portion in control
|
|
if ( m_text )
|
|
m_text->SetValue( str );
|
|
else
|
|
m_valueString = str;
|
|
|
|
Refresh();
|
|
}
|
|
|
|
int wxPGOwnerDrawnComboBox::GetSelection() const
|
|
{
|
|
wxASSERT( m_popupInterface );
|
|
return m_popupInterface->GetSelection();
|
|
}
|
|
|
|
int wxPGOwnerDrawnComboBox::DoAppend(const wxString& item)
|
|
{
|
|
wxASSERT( m_popupInterface );
|
|
return m_popupInterface->Append(item);
|
|
}
|
|
|
|
int wxPGOwnerDrawnComboBox::DoInsert(const wxString& item, wxODCIndex pos)
|
|
{
|
|
wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT), -1, wxT("can't insert into sorted list"));
|
|
wxCHECK_MSG((pos>=0) && (pos<=GetCount()), -1, wxT("invalid index"));
|
|
|
|
m_popupInterface->Insert(item,pos);
|
|
|
|
return pos;
|
|
}
|
|
|
|
#if wxCHECK_VERSION(2,9,0)
|
|
int wxPGOwnerDrawnComboBox::DoInsertItems(const wxArrayStringsAdapter& items,
|
|
unsigned int pos,
|
|
void **clientData,
|
|
wxClientDataType type)
|
|
{
|
|
unsigned int i;
|
|
for ( i=0; i<items.GetCount(); i++ )
|
|
{
|
|
DoInsert(items[i], pos);
|
|
if ( clientData )
|
|
{
|
|
if ( type == wxClientData_Object )
|
|
DoSetItemClientObject(pos, (wxClientData*)clientData[i]);
|
|
else
|
|
DoSetItemClientData(pos, clientData[i]);
|
|
}
|
|
pos++;
|
|
}
|
|
return pos - 1;
|
|
}
|
|
#endif
|
|
|
|
void wxPGOwnerDrawnComboBox::DoSetItemClientData(wxODCIndex n, void* clientData)
|
|
{
|
|
wxASSERT(m_popupInterface);
|
|
m_popupInterface->SetItemClientData(n,clientData,
|
|
#if wxCHECK_VERSION(2,9,0)
|
|
GetClientDataType()
|
|
#else
|
|
m_clientDataItemsType
|
|
#endif
|
|
);
|
|
}
|
|
|
|
void* wxPGOwnerDrawnComboBox::DoGetItemClientData(wxODCIndex n) const
|
|
{
|
|
wxASSERT(m_popupInterface);
|
|
return m_popupInterface->GetItemClientData(n);
|
|
}
|
|
|
|
void wxPGOwnerDrawnComboBox::DoSetItemClientObject(wxODCIndex n, wxClientData* clientData)
|
|
{
|
|
DoSetItemClientData(n, (void*) clientData);
|
|
}
|
|
|
|
wxClientData* wxPGOwnerDrawnComboBox::DoGetItemClientObject(wxODCIndex n) const
|
|
{
|
|
return (wxClientData*) DoGetItemClientData(n);
|
|
}
|
|
|
|
#endif // wxUSE_COMBOBOX
|