1 ///////////////////////////////////////////////////////////////////////////// 
   4 // Author:      Robert Roebling 
   6 // Copyright:   (c) 1998 Robert Roebling 
   7 // Licence:     wxWindows licence 
   8 ///////////////////////////////////////////////////////////////////////////// 
  12 #pragma implementation "choice.h" 
  19 #include "wx/choice.h" 
  20 #include "wx/arrstr.h" 
  22 #include "wx/gtk/private.h" 
  24 //----------------------------------------------------------------------------- 
  26 //----------------------------------------------------------------------------- 
  28 extern void wxapp_install_idle_handler(); 
  31 //----------------------------------------------------------------------------- 
  33 //----------------------------------------------------------------------------- 
  35 extern bool   g_blockEventsOnDrag
; 
  37 //----------------------------------------------------------------------------- 
  39 //----------------------------------------------------------------------------- 
  41 static void gtk_choice_clicked_callback( GtkWidget 
*WXUNUSED(widget
), wxChoice 
*choice 
) 
  44       wxapp_install_idle_handler(); 
  46     if (!choice
->m_hasVMT
) return; 
  48     if (g_blockEventsOnDrag
) return; 
  50     wxCommandEvent 
event(wxEVT_COMMAND_CHOICE_SELECTED
, choice
->GetId() ); 
  51     int n 
= choice
->GetSelection(); 
  54     event
.SetString( choice
->GetStringSelection() ); 
  55     event
.SetEventObject(choice
); 
  57     if ( choice
->HasClientObjectData() ) 
  58         event
.SetClientObject( choice
->GetClientObject(n
) ); 
  59     else if ( choice
->HasClientUntypedData() ) 
  60         event
.SetClientData( choice
->GetClientData(n
) ); 
  62     choice
->GetEventHandler()->ProcessEvent(event
); 
  65 //----------------------------------------------------------------------------- 
  67 //----------------------------------------------------------------------------- 
  69 IMPLEMENT_DYNAMIC_CLASS(wxChoice
,wxControl
) 
  73     m_strings 
= (wxSortedArrayString 
*)NULL
; 
  76 bool wxChoice::Create( wxWindow 
*parent
, wxWindowID id
, 
  77                        const wxPoint 
&pos
, const wxSize 
&size
, 
  78                        int n
, const wxString choices
[], 
  79                        long style
, const wxValidator
& validator
, const wxString 
&name 
) 
  82 #if (GTK_MINOR_VERSION > 0) 
  83     m_acceptsFocus 
= TRUE
; 
  86     if (!PreCreation( parent
, pos
, size 
) || 
  87         !CreateBase( parent
, id
, pos
, size
, style
, validator
, name 
)) 
  89         wxFAIL_MSG( wxT("wxChoice creation failed") ); 
  93     m_widget 
= gtk_option_menu_new(); 
  95     if ( style 
& wxCB_SORT 
) 
  97         // if our m_strings != NULL, DoAppend() will check for it and insert 
  98         // items in the correct order 
  99         m_strings 
= new wxSortedArrayString
; 
 102     GtkWidget 
*menu 
= gtk_menu_new(); 
 104     for (int i 
= 0; i 
< n
; i
++) 
 106         GtkAddHelper(menu
, i
, choices
[i
]); 
 109     gtk_option_menu_set_menu( GTK_OPTION_MENU(m_widget
), menu 
); 
 111     m_parent
->DoAddChild( this ); 
 115     SetFont( parent
->GetFont() ); 
 119     SetBackgroundColour( parent
->GetBackgroundColour() ); 
 120     SetForegroundColour( parent
->GetForegroundColour() ); 
 127 wxChoice::~wxChoice() 
 134 int wxChoice::DoAppend( const wxString 
&item 
) 
 136     wxCHECK_MSG( m_widget 
!= NULL
, -1, wxT("invalid choice control") ); 
 138     GtkWidget 
*menu 
= gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ); 
 140     return GtkAddHelper(menu
, GetCount(), item
); 
 143 int wxChoice::DoInsert( const wxString 
&item
, int pos 
) 
 145     wxCHECK_MSG( m_widget 
!= NULL
, -1, wxT("invalid choice control") ); 
 146     wxCHECK_MSG( (pos
>=0) && (pos
<=GetCount()), -1, wxT("invalid index")); 
 148     if (pos 
== GetCount()) 
 149         return DoAppend(item
); 
 151     GtkWidget 
*menu 
= gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ); 
 153     return GtkAddHelper(menu
, pos
, item
); 
 156 void wxChoice::DoSetItemClientData( int n
, void* clientData 
) 
 158     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice control") ); 
 160     wxList::compatibility_iterator node 
= m_clientList
.Item( n 
); 
 161     wxCHECK_RET( node
, wxT("invalid index in wxChoice::DoSetItemClientData") ); 
 163     node
->SetData( (wxObject
*) clientData 
); 
 166 void* wxChoice::DoGetItemClientData( int n 
) const 
 168     wxCHECK_MSG( m_widget 
!= NULL
, NULL
, wxT("invalid choice control") ); 
 170     wxList::compatibility_iterator node 
= m_clientList
.Item( n 
); 
 171     wxCHECK_MSG( node
, NULL
, wxT("invalid index in wxChoice::DoGetItemClientData") ); 
 173     return node
->GetData(); 
 176 void wxChoice::DoSetItemClientObject( int n
, wxClientData
* clientData 
) 
 178     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice control") ); 
 180     wxList::compatibility_iterator node 
= m_clientList
.Item( n 
); 
 181     wxCHECK_RET( node
, wxT("invalid index in wxChoice::DoSetItemClientObject") ); 
 183     // wxItemContainer already deletes data for us 
 185     node
->SetData( (wxObject
*) clientData 
); 
 188 wxClientData
* wxChoice::DoGetItemClientObject( int n 
) const 
 190     wxCHECK_MSG( m_widget 
!= NULL
, (wxClientData
*) NULL
, wxT("invalid choice control") ); 
 192     wxList::compatibility_iterator node 
= m_clientList
.Item( n 
); 
 193     wxCHECK_MSG( node
, (wxClientData 
*)NULL
, 
 194                  wxT("invalid index in wxChoice::DoGetItemClientObject") ); 
 196     return (wxClientData
*) node
->GetData(); 
 199 void wxChoice::Clear() 
 201     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice") ); 
 203     gtk_option_menu_remove_menu( GTK_OPTION_MENU(m_widget
) ); 
 204     GtkWidget 
*menu 
= gtk_menu_new(); 
 205     gtk_option_menu_set_menu( GTK_OPTION_MENU(m_widget
), menu 
); 
 207     if ( HasClientObjectData() ) 
 209         // destroy the data (due to Robert's idea of using wxList<wxObject> 
 210         // and not wxList<wxClientData> we can't just say 
 211         // m_clientList.DeleteContents(TRUE) - this would crash! 
 212         wxList::compatibility_iterator node 
= m_clientList
.GetFirst(); 
 215             delete (wxClientData 
*)node
->GetData(); 
 216             node 
= node
->GetNext(); 
 219     m_clientList
.Clear(); 
 225 void wxChoice::Delete( int n 
) 
 227     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice") ); 
 229     // VZ: apparently GTK+ doesn't have a built-in function to do it (not even 
 230     //     in 2.0), hence this dump implementation - still better than nothing 
 234     wxCHECK_RET( n 
>= 0 && n 
< count
, _T("invalid index in wxChoice::Delete") ); 
 238     for ( i 
= 0; i 
< count
; i
++ ) 
 241             items
.Add(GetString(i
)); 
 246     for ( i 
= 0; i 
< count 
- 1; i
++ ) 
 252 int wxChoice::FindString( const wxString 
&string 
) const 
 254     wxCHECK_MSG( m_widget 
!= NULL
, -1, wxT("invalid choice") ); 
 256     // If you read this code once and you think you understand 
 257     // it, then you are very wrong. Robert Roebling. 
 259     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) ); 
 261     GList 
*child 
= menu_shell
->children
; 
 264         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
 265         GtkLabel 
*label 
= (GtkLabel 
*) NULL
; 
 267             label 
= GTK_LABEL(bin
->child
); 
 269             label 
= GTK_LABEL( BUTTON_CHILD(m_widget
) ); 
 271         wxASSERT_MSG( label 
!= NULL 
, wxT("wxChoice: invalid label") ); 
 274          wxString 
tmp( wxGTK_CONV_BACK( gtk_label_get_text( label
) ) ); 
 276          wxString 
tmp( label
->label 
); 
 288 int wxChoice::GetSelection() const 
 290     wxCHECK_MSG( m_widget 
!= NULL
, -1, wxT("invalid choice") ); 
 294     return gtk_option_menu_get_history( GTK_OPTION_MENU(m_widget
) ); 
 297     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) ); 
 300     GList 
*child 
= menu_shell
->children
; 
 303         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
 304         if (!bin
->child
) return count
; 
 313 void wxChoice::SetString( int WXUNUSED(n
), const wxString
& WXUNUSED(string
) ) 
 315     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice") ); 
 317     wxFAIL_MSG(wxT("not implemented")); 
 320 wxString 
wxChoice::GetString( int n 
) const 
 322     wxCHECK_MSG( m_widget 
!= NULL
, wxT(""), wxT("invalid choice") ); 
 324     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) ); 
 326     GList 
*child 
= menu_shell
->children
; 
 329         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
 332             GtkLabel 
*label 
= (GtkLabel 
*) NULL
; 
 334                 label 
= GTK_LABEL(bin
->child
); 
 336                 label 
= GTK_LABEL( BUTTON_CHILD(m_widget
) ); 
 338             wxASSERT_MSG( label 
!= NULL 
, wxT("wxChoice: invalid label") ); 
 341             return wxString( wxGTK_CONV_BACK( gtk_label_get_text( label
) ) ); 
 343             return wxString( label
->label 
); 
 350     wxFAIL_MSG( wxT("wxChoice: invalid index in GetString()") ); 
 355 int wxChoice::GetCount() const 
 357     wxCHECK_MSG( m_widget 
!= NULL
, 0, wxT("invalid choice") ); 
 359     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) ); 
 361     GList 
*child 
= menu_shell
->children
; 
 370 void wxChoice::SetSelection( int n 
) 
 372     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice") ); 
 375     gtk_option_menu_set_history( GTK_OPTION_MENU(m_widget
), (gint
)tmp 
); 
 378 void wxChoice::ApplyWidgetStyle() 
 382     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) ); 
 384     gtk_widget_set_style( m_widget
, m_widgetStyle 
); 
 385     gtk_widget_set_style( GTK_WIDGET( menu_shell 
), m_widgetStyle 
); 
 387     GList 
*child 
= menu_shell
->children
; 
 390         gtk_widget_set_style( GTK_WIDGET( child
->data 
), m_widgetStyle 
); 
 392         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
 393         GtkWidget 
*label 
= (GtkWidget 
*) NULL
; 
 397             label 
= BUTTON_CHILD(m_widget
); 
 399         gtk_widget_set_style( label
, m_widgetStyle 
); 
 405 int wxChoice::GtkAddHelper(GtkWidget 
*menu
, int pos
, const wxString
& item
) 
 407     wxCHECK_MSG((pos
>=0) && (pos
<=(int)m_clientList
.GetCount()), -1, wxT("invalid index")); 
 409     GtkWidget 
*menu_item 
= gtk_menu_item_new_with_label( wxGTK_CONV( item 
) ); 
 414         // sorted control, need to insert at the correct index 
 415         index 
= m_strings
->Add(item
); 
 417         gtk_menu_insert( GTK_MENU(menu
), menu_item
, index 
); 
 421             m_clientList
.Insert( m_clientList
.Item(index 
- 1), 
 426             m_clientList
.Insert( (wxObject
*) NULL 
); 
 431         // don't call wxChoice::GetCount() from here because it doesn't work 
 432         // if we're called from ctor (and GtkMenuShell is still NULL) 
 434         // normal control, just append 
 435         if (pos 
== (int)m_clientList
.GetCount()) 
 437         gtk_menu_append( GTK_MENU(menu
), menu_item 
); 
 438         m_clientList
.Append( (wxObject
*) NULL 
); 
 439         index 
= m_clientList
.GetCount() - 1; 
 443             gtk_menu_insert( GTK_MENU(menu
), menu_item
, pos 
); 
 444             m_clientList
.Insert( pos
, (wxObject
*) NULL 
); 
 449     if (GTK_WIDGET_REALIZED(m_widget
)) 
 451         gtk_widget_realize( menu_item 
); 
 452         gtk_widget_realize( GTK_BIN(menu_item
)->child 
); 
 454         if (m_widgetStyle
) ApplyWidgetStyle(); 
 457     gtk_signal_connect( GTK_OBJECT( menu_item 
), "activate", 
 458       GTK_SIGNAL_FUNC(gtk_choice_clicked_callback
), (gpointer
*)this ); 
 460     gtk_widget_show( menu_item 
); 
 462     // return the index of the item in the control 
 466 wxSize 
wxChoice::DoGetBestSize() const 
 468     wxSize 
ret( wxControl::DoGetBestSize() ); 
 470     // we know better our horizontal extent: it depends on the longest string 
 476         size_t count 
= GetCount(); 
 477         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 479             GetTextExtent( GetString(n
), &width
, NULL
, NULL
, NULL
, &m_font 
); 
 484         // add extra for the choice "=" button 
 486         // VZ: I don't know how to get the right value, it seems to be in 
 487         //     GtkOptionMenuProps struct from gtkoptionmenu.c but we can't get 
 488         //     to it - maybe we can use gtk_option_menu_size_request() for this 
 491         //     This default value works only for the default GTK+ theme (i.e. 
 492         //     no theme at all) (FIXME) 
 493         static const int widthChoiceIndicator 
= 35; 
 494         ret
.x 
+= widthChoiceIndicator
; 
 497     // but not less than the minimal width 
 501     ret
.y 
= 16 + GetCharHeight(); 
 506 bool wxChoice::IsOwnGtkWindow( GdkWindow 
*window 
) 
 509     return GTK_BUTTON(m_widget
)->event_window
; 
 511     return (window 
== m_widget
->window
); 
 516 #endif // wxUSE_CHOICE