1. svn mv assdraw csri hunspell lua51 contrib * See r2749 for full description. Originally committed to SVN as r2754.
3836 lines
103 KiB
C++
3836 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
|