use GtkComboBox instead of deprecated GtkOptionMenu for wxChoice; this also allows to derive wxComboBox from wxChoice in wxGTK as in wxMSW (ticket #9150)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@53641 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2008-05-18 17:26:28 +00:00
parent f6753003f1
commit a2c9411060
5 changed files with 183 additions and 665 deletions

View File

@@ -61,6 +61,8 @@ public:
const wxValidator& validator = wxDefaultValidator, const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxChoiceNameStr ); const wxString& name = wxChoiceNameStr );
void SendSelectionChangedEvent(wxEventType evt_type);
int GetSelection() const; int GetSelection() const;
void SetSelection(int n); void SetSelection(int n);
@@ -69,38 +71,29 @@ public:
virtual wxString GetString(unsigned int n) const; virtual wxString GetString(unsigned int n) const;
virtual void SetString(unsigned int n, const wxString& string); virtual void SetString(unsigned int n, const wxString& string);
virtual void DisableEvents();
virtual void EnableEvents();
static wxVisualAttributes static wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant variant = wxWINDOW_VARIANT_NORMAL); GetClassDefaultAttributes(wxWindowVariant variant = wxWINDOW_VARIANT_NORMAL);
protected: protected:
void DoDeleteOneItem(unsigned int n);
void DoClear();
wxArrayPtrVoid m_clientData; // contains the client data for the items
virtual wxSize DoGetBestSize() const;
virtual void DoApplyWidgetStyle(GtkRcStyle *style);
virtual GdkWindow *GTKGetWindow(wxArrayGdkWindows& windows) const;
virtual int DoInsertItems(const wxArrayStringsAdapter& items,
unsigned int pos,
void **clientData, wxClientDataType type);
virtual void DoSetItemClientData(unsigned int n, void* clientData);
virtual void* DoGetItemClientData(unsigned int n) const;
private:
// DoInsertItems() helper
int GtkAddHelper(GtkWidget *menu, unsigned int pos, const wxString& item);
// this array is only used for controls with wxCB_SORT style, so only // this array is only used for controls with wxCB_SORT style, so only
// allocate it if it's needed (hence using pointer) // allocate it if it's needed (hence using pointer)
wxSortedArrayString *m_strings; wxSortedArrayString *m_strings;
public: // contains the client data for the items
// this circumvents a GTK+ 2.0 bug so that the selection is wxArrayPtrVoid m_clientData;
// invalidated properly
int m_selection_hack; virtual int DoInsertItems(const wxArrayStringsAdapter& items,
unsigned int pos,
void **clientData, wxClientDataType type);
virtual void DoSetItemClientData(unsigned int n, void* clientData);
virtual void* DoGetItemClientData(unsigned int n) const;
virtual void DoClear();
virtual void DoDeleteOneItem(unsigned int n);
virtual GdkWindow *GTKGetWindow(wxArrayGdkWindows& windows) const;
private: private:
DECLARE_DYNAMIC_CLASS(wxChoice) DECLARE_DYNAMIC_CLASS(wxChoice)

View File

@@ -11,14 +11,16 @@
#ifndef _WX_GTK_COMBOBOX_H_ #ifndef _WX_GTK_COMBOBOX_H_
#define _WX_GTK_COMBOBOX_H_ #define _WX_GTK_COMBOBOX_H_
#include "wx/choice.h"
typedef struct _GtkEntry GtkEntry; typedef struct _GtkEntry GtkEntry;
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// wxComboBox // wxComboBox
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
class WXDLLIMPEXP_CORE wxComboBox : public wxControl, class WXDLLIMPEXP_CORE wxComboBox : public wxChoice,
public wxComboBoxBase public wxTextEntry
{ {
public: public:
wxComboBox() { m_strings = NULL; } wxComboBox() { m_strings = NULL; }
@@ -47,8 +49,6 @@ public:
Create(parent, id, value, pos, size, choices, style, validator, name); Create(parent, id, value, pos, size, choices, style, validator, name);
} }
virtual ~wxComboBox();
bool Create(wxWindow *parent, wxWindowID id, bool Create(wxWindow *parent, wxWindowID id,
const wxString& value = wxEmptyString, const wxString& value = wxEmptyString,
const wxPoint& pos = wxDefaultPosition, const wxPoint& pos = wxDefaultPosition,
@@ -66,33 +66,28 @@ public:
const wxValidator& validator = wxDefaultValidator, const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxComboBoxNameStr); const wxString& name = wxComboBoxNameStr);
// From wxItemContainerImmutable: // Set/GetSelection() from wxTextEntry and wxChoice
virtual unsigned int GetCount() const;
virtual wxString GetString(unsigned int n) const;
virtual void SetString(unsigned int n, const wxString &text);
virtual int FindString(const wxString& s, bool bCase = false) const;
virtual void SetSelection(int n);
virtual int GetSelection() const;
// from wxTextEntry: we need to override them to avoid virtual function virtual void SetSelection(int n) { wxChoice::SetSelection(n); }
// hiding
virtual void SetSelection(long from, long to) virtual void SetSelection(long from, long to)
{ { wxTextEntry::SetSelection(from, to); }
wxTextEntry::SetSelection(from, to);
}
virtual int GetSelection() const { return wxChoice::GetSelection(); }
virtual void GetSelection(long *from, long *to) const virtual void GetSelection(long *from, long *to) const
{ { return wxTextEntry::GetSelection(from, to); }
return wxTextEntry::GetSelection(from, to);
}
virtual wxString GetStringSelection() const virtual wxString GetStringSelection() const
{ {
return wxItemContainer::GetStringSelection(); return wxItemContainer::GetStringSelection();
} }
// From wxComboBoxBase: virtual void Clear()
virtual int GetCurrentSelection() const; {
wxTextEntry::Clear();
wxItemContainer::Clear();
}
bool IsEmpty() const { return wxItemContainer::IsEmpty(); }
void OnChar( wxKeyEvent &event ); void OnChar( wxKeyEvent &event );
@@ -113,37 +108,17 @@ public:
void OnUpdateDelete(wxUpdateUIEvent& event); void OnUpdateDelete(wxUpdateUIEvent& event);
void OnUpdateSelectAll(wxUpdateUIEvent& event); void OnUpdateSelectAll(wxUpdateUIEvent& event);
bool m_ignoreNextUpdate:1; virtual void DisableEvents();
wxArrayPtrVoid m_clientData; virtual void EnableEvents();
int m_prevSelection;
void DisableEvents();
void EnableEvents();
GtkWidget* GetConnectWidget(); GtkWidget* GetConnectWidget();
wxCONTROL_ITEMCONTAINER_CLIENTDATAOBJECT_RECAST
static wxVisualAttributes static wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant variant = wxWINDOW_VARIANT_NORMAL); GetClassDefaultAttributes(wxWindowVariant variant = wxWINDOW_VARIANT_NORMAL);
virtual bool IsSorted() const { return HasFlag(wxCB_SORT); }
protected: protected:
// From wxWindowGTK: // From wxWindowGTK:
virtual GdkWindow *GTKGetWindow(wxArrayGdkWindows& windows) const; virtual GdkWindow *GTKGetWindow(wxArrayGdkWindows& windows) const;
// From wxItemContainer:
virtual int DoInsertItems(const wxArrayStringsAdapter& items,
unsigned int pos,
void **clientData, wxClientDataType type);
virtual void DoSetItemClientData(unsigned int n, void* clientData);
virtual void* DoGetItemClientData(unsigned int n) const;
virtual void DoClear();
virtual void DoDeleteOneItem(unsigned int n);
// From wxControl:
virtual wxSize DoGetBestSize() const;
// Widgets that use the style->base colour for the BG colour should // Widgets that use the style->base colour for the BG colour should
// override this and return true. // override this and return true.
virtual bool UseGTKStyleBase() const { return true; } virtual bool UseGTKStyleBase() const { return true; }
@@ -163,10 +138,6 @@ private:
DisableEvents(); DisableEvents();
} }
// this array is only used for controls with wxCB_SORT style, so only
// allocate it if it's needed (hence using pointer)
wxSortedArrayString *m_strings;
DECLARE_DYNAMIC_CLASS_NO_COPY(wxComboBox) DECLARE_DYNAMIC_CLASS_NO_COPY(wxComboBox)
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()
}; };

View File

@@ -272,7 +272,7 @@ void ChoiceWidgetsPage::CreateContent()
// right pane // right pane
wxSizer *sizerRight = new wxBoxSizer(wxVERTICAL); wxSizer *sizerRight = new wxBoxSizer(wxVERTICAL);
m_choice = new wxChoice(this, ChoicePage_Choice); m_choice = new wxChoice(this, ChoicePage_Choice);
sizerRight->Add(m_choice, 1, wxGROW | wxALL, 5); sizerRight->Add(m_choice, 0, wxALL | wxGROW, 5);
sizerRight->SetMinSize(150, 0); sizerRight->SetMinSize(150, 0);
m_sizerChoice = sizerRight; // save it to modify it later m_sizerChoice = sizerRight; // save it to modify it later
@@ -439,10 +439,7 @@ void ChoiceWidgetsPage::OnChoice(wxCommandEvent& event)
long sel = event.GetSelection(); long sel = event.GetSelection();
m_textDelete->SetValue(wxString::Format(_T("%ld"), sel)); m_textDelete->SetValue(wxString::Format(_T("%ld"), sel));
if (event.IsSelection()) wxLogMessage(_T("Choice item %ld selected"), sel);
wxLogMessage(_T("Choice item %ld selected"), sel);
else
wxLogMessage(_T("Choice item %ld deselected"), sel);
} }
void ChoiceWidgetsPage::OnCheckOrRadioBox(wxCommandEvent& WXUNUSED(event)) void ChoiceWidgetsPage::OnCheckOrRadioBox(wxCommandEvent& WXUNUSED(event))

View File

@@ -9,7 +9,7 @@
#include "wx/wxprec.h" #include "wx/wxprec.h"
#if wxUSE_CHOICE #if wxUSE_CHOICE || wxUSE_COMBOBOX
#include "wx/choice.h" #include "wx/choice.h"
@@ -17,53 +17,21 @@
#include "wx/arrstr.h" #include "wx/arrstr.h"
#endif #endif
// FIXME: We use GtkOptionMenu which has been deprecated since GTK+ 2.3.0 in
// favour of GtkComboBox.
// Later use GtkComboBox if GTK+ runtime version is new enough.
#include <gtk/gtkversion.h>
#if defined(GTK_DISABLE_DEPRECATED) && GTK_CHECK_VERSION(2,3,0)
#undef GTK_DISABLE_DEPRECATED
#endif
#include "wx/gtk/private.h" #include "wx/gtk/private.h"
//-----------------------------------------------------------------------------
// data
//-----------------------------------------------------------------------------
extern bool g_blockEventsOnDrag; // ----------------------------------------------------------------------------
// GTK callbacks
//----------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// "activate"
//-----------------------------------------------------------------------------
extern "C" { extern "C" {
static void gtk_choice_clicked_callback( GtkWidget *WXUNUSED(widget), wxChoice *choice )
static void
gtk_choice_changed_callback( GtkWidget *WXUNUSED(widget), wxChoice *choice )
{ {
if (!choice->m_hasVMT) return; choice->SendSelectionChangedEvent(wxEVT_COMMAND_CHOICE_SELECTED);
if (g_blockEventsOnDrag) return;
int selection = wxNOT_FOUND;
selection = gtk_option_menu_get_history( GTK_OPTION_MENU(choice->GetHandle()) );
choice->m_selection_hack = selection;
wxCommandEvent event(wxEVT_COMMAND_CHOICE_SELECTED, choice->GetId() );
int n = choice->GetSelection();
event.SetInt( n );
event.SetString( choice->GetStringSelection() );
event.SetEventObject(choice);
if ( choice->HasClientObjectData() )
event.SetClientObject( choice->GetClientObject(n) );
else if ( choice->HasClientUntypedData() )
event.SetClientData( choice->GetClientData(n) );
choice->HandleWindowEvent(event);
} }
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@@ -73,8 +41,8 @@ static void gtk_choice_clicked_callback( GtkWidget *WXUNUSED(widget), wxChoice *
IMPLEMENT_DYNAMIC_CLASS(wxChoice, wxControlWithItems) IMPLEMENT_DYNAMIC_CLASS(wxChoice, wxControlWithItems)
wxChoice::wxChoice() wxChoice::wxChoice()
: m_strings(NULL)
{ {
m_strings = (wxSortedArrayString *)NULL;
} }
bool wxChoice::Create( wxWindow *parent, wxWindowID id, bool wxChoice::Create( wxWindow *parent, wxWindowID id,
@@ -92,8 +60,11 @@ bool wxChoice::Create( wxWindow *parent, wxWindowID id,
bool wxChoice::Create( wxWindow *parent, wxWindowID id, bool wxChoice::Create( wxWindow *parent, wxWindowID id,
const wxPoint &pos, const wxSize &size, const wxPoint &pos, const wxSize &size,
int n, const wxString choices[], int n, const wxString choices[],
long style, const wxValidator& validator, const wxString &name ) long style, const wxValidator& validator,
const wxString &name )
{ {
m_strings = NULL;
if (!PreCreation( parent, pos, size ) || if (!PreCreation( parent, pos, size ) ||
!CreateBase( parent, id, pos, size, style, validator, name )) !CreateBase( parent, id, pos, size, style, validator, name ))
{ {
@@ -101,8 +72,6 @@ bool wxChoice::Create( wxWindow *parent, wxWindowID id,
return false; return false;
} }
m_widget = gtk_option_menu_new();
if ( IsSorted() ) if ( IsSorted() )
{ {
// if our m_strings != NULL, Append() will check for it and insert // if our m_strings != NULL, Append() will check for it and insert
@@ -110,73 +79,75 @@ bool wxChoice::Create( wxWindow *parent, wxWindowID id,
m_strings = new wxSortedArrayString; m_strings = new wxSortedArrayString;
} }
// If we have items, GTK will choose the first item by default m_widget = gtk_combo_box_new_text();
m_selection_hack = n > 0 ? 0 : wxNOT_FOUND;
GtkWidget *menu = gtk_menu_new(); Append(n, choices);
for (unsigned int i = 0; i < (unsigned int)n; i++)
{
GtkAddHelper(menu, i, choices[i]);
}
gtk_option_menu_set_menu( GTK_OPTION_MENU(m_widget), menu );
m_parent->DoAddChild( this ); m_parent->DoAddChild( this );
PostCreation(size); PostCreation(size);
SetInitialSize(size); // need this too because this is a wxControlWithItems
// workaround for bug in gtk_option_menu_set_history(), it causes g_signal_connect_after (m_widget, "changed",
// gtk_widget_size_allocate() to be called with the current G_CALLBACK (gtk_choice_changed_callback), this);
// widget->allocation values, which will be zero if a proper
// size_allocate has not occured yet
m_widget->allocation.width = m_width;
m_widget->allocation.height = m_height;
return true; return true;
} }
wxChoice::~wxChoice() wxChoice::~wxChoice()
{ {
Clear();
delete m_strings; delete m_strings;
} }
void wxChoice::SendSelectionChangedEvent(wxEventType evt_type)
{
if (!m_hasVMT)
return;
if (GetSelection() == -1)
return;
wxCommandEvent event( evt_type, GetId() );
int n = GetSelection();
event.SetInt( n );
event.SetString( GetStringSelection() );
event.SetEventObject( this );
InitCommandEventWithItems( event, n );
HandleWindowEvent( event );
}
int wxChoice::DoInsertItems(const wxArrayStringsAdapter & items, int wxChoice::DoInsertItems(const wxArrayStringsAdapter & items,
unsigned int pos, unsigned int pos,
void **clientData, wxClientDataType type) void **clientData, wxClientDataType type)
{ {
wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid choice control") ); wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid control") );
const unsigned int count = items.GetCount(); wxASSERT_MSG( !IsSorted() || (pos == GetCount()),
_T("In a sorted choice data could only be appended"));
GtkWidget *menu = gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget) ); const int count = items.GetCount();
for ( unsigned int i = 0; i < count; ++i, ++pos ) int n = wxNOT_FOUND;
GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
for ( int i = 0; i < count; ++i )
{ {
int n = GtkAddHelper(menu, pos, items[i]); n = pos + i;
if ( n == wxNOT_FOUND ) // If sorted, use this wxSortedArrayStrings to determine
return n; // the right insertion point
if(m_strings)
n = m_strings->Add(items[i]);
gtk_combo_box_insert_text( combobox, n, wxGTK_CONV( items[i] ) );
m_clientData.Insert( NULL, n );
AssignNewItemClientData(n, clientData, i, type); AssignNewItemClientData(n, clientData, i, type);
} }
// if the item to insert is at or before the selection, and the selection is valid InvalidateBestSize();
if (((int)pos <= m_selection_hack) && (m_selection_hack != wxNOT_FOUND))
{
// move the selection forward
m_selection_hack += count;
}
// We must set the selection so that it can be read back even if return n;
// the user has not modified it since GTK+ will then select the
// first item so well return 0.
if ((count > 0) && (m_selection_hack==wxNOT_FOUND))
m_selection_hack = 0;
return pos - 1;
} }
void wxChoice::DoSetItemClientData(unsigned int n, void* clientData) void wxChoice::DoSetItemClientData(unsigned int n, void* clientData)
@@ -191,337 +162,164 @@ void* wxChoice::DoGetItemClientData(unsigned int n) const
void wxChoice::DoClear() void wxChoice::DoClear()
{ {
wxCHECK_RET( m_widget != NULL, wxT("invalid choice") ); wxCHECK_RET( m_widget != NULL, wxT("invalid control") );
gtk_option_menu_remove_menu( GTK_OPTION_MENU(m_widget) ); DisableEvents();
GtkWidget *menu = gtk_menu_new();
gtk_option_menu_set_menu( GTK_OPTION_MENU(m_widget), menu ); const unsigned int count = GetCount();
for (unsigned int i = 0; i < count; i++)
gtk_combo_box_remove_text( GTK_COMBO_BOX(m_widget), 0 );
m_clientData.Clear(); m_clientData.Clear();
if ( m_strings ) if (m_strings)
m_strings->Clear(); m_strings->Clear();
// begin with no selection EnableEvents();
m_selection_hack = wxNOT_FOUND;
InvalidateBestSize();
} }
void wxChoice::DoDeleteOneItem(unsigned int n) void wxChoice::DoDeleteOneItem(unsigned int n)
{ {
wxCHECK_RET( m_widget != NULL, wxT("invalid choice") ); wxCHECK_RET( m_widget != NULL, wxT("invalid control") );
wxCHECK_RET( IsValid(n), _T("invalid index in wxChoice::Delete") ); wxCHECK_RET( IsValid(n), _T("invalid index in wxChoice::Delete") );
// if the item to delete is before the selection, and the selection is valid gtk_combo_box_remove_text( GTK_COMBO_BOX( m_widget ), n );
if (((int)n < m_selection_hack) && (m_selection_hack != wxNOT_FOUND)) m_clientData.RemoveAt( n );
{ if ( m_strings )
// move the selection back one m_strings->RemoveAt( n );
m_selection_hack--;
}
else if ((int)n == m_selection_hack)
{
// invalidate the selection
m_selection_hack = wxNOT_FOUND;
}
// VZ: apparently GTK+ doesn't have a built-in function to do it (not even InvalidateBestSize();
// in 2.0), hence this dumb implementation -- still better than nothing
const unsigned int count = GetCount();
wxArrayString items;
wxArrayPtrVoid itemsData;
items.Alloc(count - 1);
itemsData.Alloc(count - 1);
for ( unsigned i = 0; i < count; i++ )
{
if ( i != n )
{
items.Add(GetString(i));
itemsData.Add(m_clientData[i]);
}
}
wxChoice::DoClear();
if ( count > 1 )
{
void ** const data = &itemsData[0];
if ( HasClientObjectData() )
Append(items, wx_reinterpret_cast(wxClientData **, data));
else
Append(items, data);
}
//else: the control is now empty, nothing to append
} }
int wxChoice::FindString( const wxString &string, bool bCase ) const int wxChoice::FindString( const wxString &item, bool bCase ) const
{ {
wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid choice") ); wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid control") );
// If you read this code once and you think you understand GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
// it, then you are very wrong. Robert Roebling. GtkTreeModel* model = gtk_combo_box_get_model( combobox );
GtkTreeIter iter;
GtkMenuShell *menu_shell = GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget) ) ); gtk_tree_model_get_iter_first( model, &iter );
if (!gtk_list_store_iter_is_valid(GTK_LIST_STORE(model), &iter ))
return -1;
int count = 0; int count = 0;
GList *child = menu_shell->children; do
while (child)
{ {
GtkBin *bin = GTK_BIN( child->data ); GValue value = { 0, };
GtkLabel *label = (GtkLabel *) NULL; gtk_tree_model_get_value( model, &iter, 0, &value );
if (bin->child) wxString str = wxGTK_CONV_BACK( g_value_get_string( &value ) );
label = GTK_LABEL(bin->child); g_value_unset( &value );
if (!label)
label = GTK_LABEL(GTK_BIN(m_widget)->child);
wxASSERT_MSG( label != NULL , wxT("wxChoice: invalid label") ); if (item.IsSameAs( str, bCase ) )
wxString tmp( wxGTK_CONV_BACK( gtk_label_get_text( label) ) );
if (string.IsSameAs( tmp, bCase ))
return count; return count;
child = child->next;
count++; count++;
} }
while ( gtk_tree_model_iter_next(model, &iter) );
return wxNOT_FOUND; return wxNOT_FOUND;
} }
int wxChoice::GetSelection() const int wxChoice::GetSelection() const
{ {
wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid choice") ); return gtk_combo_box_get_active( GTK_COMBO_BOX( m_widget ) );
return m_selection_hack;
} }
void wxChoice::SetString(unsigned int n, const wxString& str) void wxChoice::SetString(unsigned int n, const wxString &text)
{ {
wxCHECK_RET( m_widget != NULL, wxT("invalid choice") ); wxCHECK_RET( m_widget != NULL, wxT("invalid control") );
GtkMenuShell *menu_shell = GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget) ) ); GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
unsigned int count = 0; wxCHECK_RET( IsValid(n), wxT("invalid index") );
GList *child = menu_shell->children;
while (child) GtkTreeModel *model = gtk_combo_box_get_model( combobox );
GtkTreeIter iter;
if (gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
{ {
GtkBin *bin = GTK_BIN( child->data ); GValue value = { 0, };
if (count == n) g_value_init( &value, G_TYPE_STRING );
{ g_value_set_string( &value, wxGTK_CONV( text ) );
GtkLabel *label = (GtkLabel *) NULL; gtk_list_store_set_value( GTK_LIST_STORE(model), &iter, 0, &value );
if (bin->child) g_value_unset( &value );
label = GTK_LABEL(bin->child);
if (!label)
label = GTK_LABEL(GTK_BIN(m_widget)->child);
wxASSERT_MSG( label != NULL , wxT("wxChoice: invalid label") );
gtk_label_set_text( label, wxGTK_CONV( str ) );
InvalidateBestSize();
return;
}
child = child->next;
count++;
} }
InvalidateBestSize();
} }
wxString wxChoice::GetString(unsigned int n) const wxString wxChoice::GetString(unsigned int n) const
{ {
wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid choice") ); wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid control") );
GtkMenuShell *menu_shell = GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget) ) ); wxString str;
unsigned int count = 0;
GList *child = menu_shell->children; GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
while (child) GtkTreeModel *model = gtk_combo_box_get_model( combobox );
GtkTreeIter iter;
if (gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
{ {
GtkBin *bin = GTK_BIN( child->data ); GValue value = { 0, };
if (count == n) gtk_tree_model_get_value( model, &iter, 0, &value );
{ wxString tmp = wxGTK_CONV_BACK( g_value_get_string( &value ) );
GtkLabel *label = (GtkLabel *) NULL; g_value_unset( &value );
if (bin->child) return tmp;
label = GTK_LABEL(bin->child);
if (!label)
label = GTK_LABEL(GTK_BIN(m_widget)->child);
wxASSERT_MSG( label != NULL , wxT("wxChoice: invalid label") );
return wxString( wxGTK_CONV_BACK( gtk_label_get_text( label) ) );
}
child = child->next;
count++;
} }
wxFAIL_MSG( wxT("wxChoice: invalid index in GetString()") ); return str;
return wxEmptyString;
} }
unsigned int wxChoice::GetCount() const unsigned int wxChoice::GetCount() const
{ {
wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid choice") ); wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid control") );
GtkMenuShell *menu_shell = GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget) ) ); GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
unsigned int count = 0; GtkTreeModel* model = gtk_combo_box_get_model( combobox );
GList *child = menu_shell->children; GtkTreeIter iter;
while (child) gtk_tree_model_get_iter_first( model, &iter );
{ if (!gtk_list_store_iter_is_valid(GTK_LIST_STORE(model), &iter ))
count++; return 0;
child = child->next; unsigned int ret = 1;
} while (gtk_tree_model_iter_next( model, &iter ))
return count; ret++;
return ret;
} }
void wxChoice::SetSelection( int n ) void wxChoice::SetSelection( int n )
{ {
wxCHECK_RET( m_widget != NULL, wxT("invalid choice") ); wxCHECK_RET( m_widget != NULL, wxT("invalid control") );
int tmp = n; DisableEvents();
gtk_option_menu_set_history( GTK_OPTION_MENU(m_widget), (gint)tmp );
// set the local selection variable manually GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
if ((n >= 0) && ((unsigned int)n < GetCount())) gtk_combo_box_set_active( combobox, n );
{
// a valid selection has been made EnableEvents();
m_selection_hack = n;
}
else if ((n == wxNOT_FOUND) || (GetCount() == 0))
{
// invalidates the selection if there are no items
// or if it is specifically set to wxNOT_FOUND
m_selection_hack = wxNOT_FOUND;
}
else
{
// this selects the first item by default if the selection is out of bounds
m_selection_hack = 0;
}
} }
void wxChoice::DoApplyWidgetStyle(GtkRcStyle *style) void wxChoice::DisableEvents()
{ {
GtkMenuShell *menu_shell = GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget) ) ); g_signal_handlers_block_by_func(m_widget,
(gpointer) gtk_choice_changed_callback, this);
gtk_widget_modify_style( m_widget, style );
gtk_widget_modify_style( GTK_WIDGET( menu_shell ), style );
GList *child = menu_shell->children;
while (child)
{
gtk_widget_modify_style( GTK_WIDGET( child->data ), style );
GtkBin *bin = GTK_BIN( child->data );
GtkWidget *label = (GtkWidget *) NULL;
if (bin->child)
label = bin->child;
if (!label)
label = GTK_BIN(m_widget)->child;
gtk_widget_modify_style( label, style );
child = child->next;
}
} }
int wxChoice::GtkAddHelper(GtkWidget *menu, unsigned int pos, const wxString& item) void wxChoice::EnableEvents()
{ {
wxCHECK_MSG(pos<=m_clientData.GetCount(), -1, wxT("invalid index")); g_signal_handlers_unblock_by_func(m_widget,
(gpointer) gtk_choice_changed_callback, this);
GtkWidget *menu_item = gtk_menu_item_new_with_label( wxGTK_CONV( item ) );
if ( m_strings )
{
// sorted control, need to insert at the correct index
pos = m_strings->Add(item);
}
// don't call wxChoice::GetCount() from here because it doesn't work
// if we're called from ctor (and GtkMenuShell is still NULL)
if (pos == m_clientData.GetCount())
gtk_menu_shell_append( GTK_MENU_SHELL(menu), menu_item );
else
gtk_menu_shell_insert( GTK_MENU_SHELL(menu), menu_item, pos );
m_clientData.Insert( NULL, pos );
if (GTK_WIDGET_REALIZED(m_widget))
{
gtk_widget_realize( menu_item );
gtk_widget_realize( GTK_BIN(menu_item)->child );
ApplyWidgetStyle();
}
// The best size of a wxChoice should probably
// be changed everytime the control has been
// changed, but at least after adding an item
// it has to change. Adapted from Matt Ownby.
InvalidateBestSize();
g_signal_connect_after (menu_item, "activate",
G_CALLBACK (gtk_choice_clicked_callback),
this);
gtk_widget_show( menu_item );
// return the index of the item in the control
return pos;
} }
wxSize wxChoice::DoGetBestSize() const
{
wxSize ret( wxControl::DoGetBestSize() );
// we know better our horizontal extent: it depends on the longest string
// we have
ret.x = 0;
if ( m_widget )
{
int width;
unsigned int count = GetCount();
for ( unsigned int n = 0; n < count; n++ )
{
GetTextExtent( GetString(n), &width, NULL, NULL, NULL );
if ( width > ret.x )
ret.x = width;
}
// add extra for the choice "=" button
// VZ: I don't know how to get the right value, it seems to be in
// GtkOptionMenuProps struct from gtkoptionmenu.c but we can't get
// to it - maybe we can use gtk_option_menu_size_request() for this
// somehow?
//
// This default value works only for the default GTK+ theme (i.e.
// no theme at all) (FIXME)
static const int widthChoiceIndicator = 35;
ret.x += widthChoiceIndicator;
}
// but not less than the minimal width
if ( ret.x < 80 )
ret.x = 80;
// If this request_size is called with no entries then
// the returned height is wrong. Give it a reasonable
// default value.
if (ret.y <= 18)
ret.y = 8 + GetCharHeight();
CacheBestSize(ret);
return ret;
}
GdkWindow *wxChoice::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const GdkWindow *wxChoice::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
{ {
return GTK_BUTTON(m_widget)->event_window; return m_widget->window;
} }
// static // static
wxVisualAttributes wxVisualAttributes
wxChoice::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant)) wxChoice::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
{ {
return GetDefaultAttributesFromGTKWidget(gtk_option_menu_new); return GetDefaultAttributesFromGTKWidget(gtk_combo_box_new);
} }
#endif // wxUSE_CHOICE #endif // wxUSE_CHOICE || wxUSE_COMBOBOX

View File

@@ -42,16 +42,7 @@ gtkcombobox_text_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *comb
static void static void
gtkcombobox_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo ) gtkcombobox_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo )
{ {
if (!combo->m_hasVMT) return; combo->SendSelectionChangedEvent(wxEVT_COMMAND_COMBOBOX_SELECTED);
if (combo->GetSelection() == -1)
return;
wxCommandEvent event( wxEVT_COMMAND_COMBOBOX_SELECTED, combo->GetId() );
event.SetInt( combo->GetSelection() );
event.SetString( combo->GetStringSelection() );
event.SetEventObject( combo );
combo->HandleWindowEvent( event );
} }
} }
@@ -59,9 +50,9 @@ gtkcombobox_changed_callback( GtkWidget *WXUNUSED(widget), wxComboBox *combo )
// wxComboBox // wxComboBox
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxComboBox,wxControl) IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxChoice)
BEGIN_EVENT_TABLE(wxComboBox, wxControl) BEGIN_EVENT_TABLE(wxComboBox, wxChoice)
EVT_CHAR(wxComboBox::OnChar) EVT_CHAR(wxComboBox::OnChar)
EVT_MENU(wxID_CUT, wxComboBox::OnCut) EVT_MENU(wxID_CUT, wxComboBox::OnCut)
@@ -101,8 +92,6 @@ bool wxComboBox::Create( wxWindow *parent, wxWindowID id, const wxString& value,
const wxString& name ) const wxString& name )
{ {
m_strings = NULL; m_strings = NULL;
m_ignoreNextUpdate = false;
m_prevSelection = 0;
if (!PreCreation( parent, pos, size ) || if (!PreCreation( parent, pos, size ) ||
!CreateBase( parent, id, pos, size, style, validator, name )) !CreateBase( parent, id, pos, size, style, validator, name ))
@@ -139,8 +128,6 @@ bool wxComboBox::Create( wxWindow *parent, wxWindowID id, const wxString& value,
PostCreation(size); PostCreation(size);
ConnectWidget( m_widget );
gtk_entry_set_text( entry, wxGTK_CONV(value) ); gtk_entry_set_text( entry, wxGTK_CONV(value) );
if (style & wxCB_READONLY) if (style & wxCB_READONLY)
@@ -152,7 +139,6 @@ bool wxComboBox::Create( wxWindow *parent, wxWindowID id, const wxString& value,
g_signal_connect_after (m_widget, "changed", g_signal_connect_after (m_widget, "changed",
G_CALLBACK (gtkcombobox_changed_callback), this); G_CALLBACK (gtkcombobox_changed_callback), this);
SetInitialSize(size); // need this too because this is a wxControlWithItems SetInitialSize(size); // need this too because this is a wxControlWithItems
return true; return true;
@@ -168,205 +154,6 @@ GtkEditable *wxComboBox::GetEditable() const
return GTK_EDITABLE( GTK_BIN(m_widget)->child ); return GTK_EDITABLE( GTK_BIN(m_widget)->child );
} }
wxComboBox::~wxComboBox()
{
Clear();
delete m_strings;
}
int wxComboBox::DoInsertItems(const wxArrayStringsAdapter & items,
unsigned int pos,
void **clientData, wxClientDataType type)
{
wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
wxASSERT_MSG( !IsSorted() || (pos == GetCount()),
_T("In a sorted combobox data could only be appended"));
const int count = items.GetCount();
int n = wxNOT_FOUND;
GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
for( int i = 0; i < count; ++i )
{
n = pos + i;
// If sorted, use this wxSortedArrayStrings to determine
// the right insertion point
if(m_strings)
n = m_strings->Add(items[i]);
gtk_combo_box_insert_text( combobox, n, wxGTK_CONV( items[i] ) );
m_clientData.Insert( NULL, n );
AssignNewItemClientData(n, clientData, i, type);
}
InvalidateBestSize();
return n;
}
void wxComboBox::DoSetItemClientData(unsigned int n, void* clientData)
{
m_clientData[n] = clientData;
}
void* wxComboBox::DoGetItemClientData(unsigned int n) const
{
return m_clientData[n];
}
void wxComboBox::DoClear()
{
wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
DisableEvents();
GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
const unsigned int count = GetCount();
for (unsigned int i = 0; i < count; i++)
gtk_combo_box_remove_text( combobox, 0 );
m_clientData.Clear();
if(m_strings)
m_strings->Clear();
EnableEvents();
InvalidateBestSize();
}
void wxComboBox::DoDeleteOneItem(unsigned int n)
{
wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
wxCHECK_RET( IsValid(n), wxT("invalid index") );
GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
gtk_combo_box_remove_text( combobox, n );
m_clientData.RemoveAt( n );
if(m_strings)
m_strings->RemoveAt( n );
InvalidateBestSize();
}
void wxComboBox::SetString(unsigned int n, const wxString &text)
{
wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
wxCHECK_RET( IsValid(n), wxT("invalid index") );
GtkTreeModel *model = gtk_combo_box_get_model( combobox );
GtkTreeIter iter;
if (gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
{
GValue value = { 0, };
g_value_init( &value, G_TYPE_STRING );
g_value_set_string( &value, wxGTK_CONV( text ) );
gtk_list_store_set_value( GTK_LIST_STORE(model), &iter, 0, &value );
g_value_unset( &value );
}
InvalidateBestSize();
}
int wxComboBox::FindString( const wxString &item, bool bCase ) const
{
wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid combobox") );
GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
GtkTreeModel* model = gtk_combo_box_get_model( combobox );
GtkTreeIter iter;
gtk_tree_model_get_iter_first( model, &iter );
if (!gtk_list_store_iter_is_valid(GTK_LIST_STORE(model), &iter ))
return -1;
int count = 0;
do
{
GValue value = { 0, };
gtk_tree_model_get_value( model, &iter, 0, &value );
wxString str = wxGTK_CONV_BACK( g_value_get_string( &value ) );
g_value_unset( &value );
if (item.IsSameAs( str, bCase ) )
return count;
count++;
}
while ( gtk_tree_model_iter_next(model, &iter) );
return wxNOT_FOUND;
}
int wxComboBox::GetSelection() const
{
GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
return gtk_combo_box_get_active( combobox );
}
int wxComboBox::GetCurrentSelection() const
{
wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid combobox") );
GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
return gtk_combo_box_get_active( combobox );
}
wxString wxComboBox::GetString(unsigned int n) const
{
wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid combobox") );
wxString str;
GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
GtkTreeModel *model = gtk_combo_box_get_model( combobox );
GtkTreeIter iter;
if (gtk_tree_model_iter_nth_child (model, &iter, NULL, n))
{
GValue value = { 0, };
gtk_tree_model_get_value( model, &iter, 0, &value );
wxString tmp = wxGTK_CONV_BACK( g_value_get_string( &value ) );
g_value_unset( &value );
return tmp;
}
return str;
}
unsigned int wxComboBox::GetCount() const
{
wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid combobox") );
GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
GtkTreeModel* model = gtk_combo_box_get_model( combobox );
GtkTreeIter iter;
gtk_tree_model_get_iter_first( model, &iter );
if (!gtk_list_store_iter_is_valid(GTK_LIST_STORE(model), &iter ))
return 0;
unsigned int ret = 1;
while (gtk_tree_model_iter_next( model, &iter ))
ret++;
return ret;
}
void wxComboBox::SetSelection( int n )
{
wxCHECK_RET( m_widget != NULL, wxT("invalid combobox") );
DisableEvents();
GtkComboBox* combobox = GTK_COMBO_BOX( m_widget );
gtk_combo_box_set_active( combobox, n );
EnableEvents();
}
void wxComboBox::OnChar( wxKeyEvent &event ) void wxComboBox::OnChar( wxKeyEvent &event )
{ {
switch ( event.GetKeyCode() ) switch ( event.GetKeyCode() )
@@ -423,34 +210,6 @@ GdkWindow *wxComboBox::GTKGetWindow(wxArrayGdkWindows& windows) const
return GetEntry()->text_area; return GetEntry()->text_area;
} }
wxSize wxComboBox::DoGetBestSize() const
{
// strangely, this returns a width of 188 pixels from GTK+ (?)
wxSize ret( wxControl::DoGetBestSize() );
// we know better our horizontal extent: it depends on the longest string
// in the combobox
if ( m_widget )
{
ret.x = 60; // start with something "sensible"
int width;
unsigned int count = GetCount();
for ( unsigned int n = 0; n < count; n++ )
{
GetTextExtent(GetString(n), &width, NULL, NULL, NULL );
if ( width + 40 > ret.x ) // 40 for drop down arrow and space around text
ret.x = width + 40;
}
}
// empty combobox should have some reasonable default size too
if ((GetCount() == 0) && (ret.x < 80))
ret.x = 80;
CacheBestSize(ret);
return ret;
}
// static // static
wxVisualAttributes wxVisualAttributes
wxComboBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant)) wxComboBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))