1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/gtk1/choice.cpp 
   4 // Author:      Robert Roebling 
   6 // Copyright:   (c) 1998 Robert Roebling 
   7 // Licence:     wxWindows licence 
   8 ///////////////////////////////////////////////////////////////////////////// 
  10 #include "wx/wxprec.h" 
  14 #include "wx/choice.h" 
  15 #include "wx/arrstr.h" 
  17 #include "wx/gtk1/private.h" 
  19 //----------------------------------------------------------------------------- 
  21 //----------------------------------------------------------------------------- 
  23 extern void wxapp_install_idle_handler(); 
  26 //----------------------------------------------------------------------------- 
  28 //----------------------------------------------------------------------------- 
  30 extern bool   g_blockEventsOnDrag
; 
  32 //----------------------------------------------------------------------------- 
  34 //----------------------------------------------------------------------------- 
  37 static void gtk_choice_clicked_callback( GtkWidget 
*WXUNUSED(widget
), wxChoice 
*choice 
) 
  40       wxapp_install_idle_handler(); 
  42     if (!choice
->m_hasVMT
) return; 
  44     if (g_blockEventsOnDrag
) return; 
  46     int selection 
= wxNOT_FOUND
; 
  48     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(choice
->GetHandle()) ) ); 
  51     GList 
*child 
= menu_shell
->children
; 
  54         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
  64     choice
->m_selection_hack 
= selection
; 
  66     wxCommandEvent 
event(wxEVT_COMMAND_CHOICE_SELECTED
, choice
->GetId() ); 
  67     int n 
= choice
->GetSelection(); 
  70     event
.SetString( choice
->GetStringSelection() ); 
  71     event
.SetEventObject(choice
); 
  73     if ( choice
->HasClientObjectData() ) 
  74         event
.SetClientObject( choice
->GetClientObject(n
) ); 
  75     else if ( choice
->HasClientUntypedData() ) 
  76         event
.SetClientData( choice
->GetClientData(n
) ); 
  78     choice
->GetEventHandler()->ProcessEvent(event
); 
  82 //----------------------------------------------------------------------------- 
  84 //----------------------------------------------------------------------------- 
  86 IMPLEMENT_DYNAMIC_CLASS(wxChoice
,wxControl
) 
  90     m_strings 
= (wxSortedArrayString 
*)NULL
; 
  93 bool wxChoice::Create( wxWindow 
*parent
, wxWindowID id
, 
  94                        const wxPoint 
&pos
, const wxSize 
&size
, 
  95                        const wxArrayString
& choices
, 
  96                        long style
, const wxValidator
& validator
, 
  97                        const wxString 
&name 
) 
  99     wxCArrayString 
chs(choices
); 
 101     return Create( parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(), 
 102                    style
, validator
, name 
); 
 105 bool wxChoice::Create( wxWindow 
*parent
, wxWindowID id
, 
 106                        const wxPoint 
&pos
, const wxSize 
&size
, 
 107                        int n
, const wxString choices
[], 
 108                        long style
, const wxValidator
& validator
, const wxString 
&name 
) 
 111 #if (GTK_MINOR_VERSION > 0) 
 112     m_acceptsFocus 
= true; 
 115     if (!PreCreation( parent
, pos
, size 
) || 
 116         !CreateBase( parent
, id
, pos
, size
, style
, validator
, name 
)) 
 118         wxFAIL_MSG( wxT("wxChoice creation failed") ); 
 122     m_widget 
= gtk_option_menu_new(); 
 124     if ( style 
& wxCB_SORT 
) 
 126         // if our m_strings != NULL, DoAppend() will check for it and insert 
 127         // items in the correct order 
 128         m_strings 
= new wxSortedArrayString
; 
 131     // begin with no selection 
 132     m_selection_hack 
= wxNOT_FOUND
; 
 134     GtkWidget 
*menu 
= gtk_menu_new(); 
 136     for (unsigned int i 
= 0; i 
< (unsigned int)n
; i
++) 
 138         GtkAddHelper(menu
, i
, choices
[i
]); 
 141     gtk_option_menu_set_menu( GTK_OPTION_MENU(m_widget
), menu 
); 
 143     m_parent
->DoAddChild( this ); 
 146     SetBestSize(size
); // need this too because this is a wxControlWithItems 
 151 wxChoice::~wxChoice() 
 158 int wxChoice::DoAppend( const wxString 
&item 
) 
 160     wxCHECK_MSG( m_widget 
!= NULL
, -1, wxT("invalid choice control") ); 
 162     GtkWidget 
*menu 
= gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ); 
 164     return GtkAddHelper(menu
, GetCount(), item
); 
 167 int wxChoice::DoInsert( const wxString 
&item
, unsigned int pos 
) 
 169     wxCHECK_MSG( m_widget 
!= NULL
, -1, wxT("invalid choice control") ); 
 170     wxCHECK_MSG( IsValidInsert(pos
), -1, wxT("invalid index")); 
 172     if (pos 
== GetCount()) 
 173         return DoAppend(item
); 
 175     GtkWidget 
*menu 
= gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ); 
 177     // if the item to insert is at or before the selection, and the selection is valid 
 178     if (((int)pos 
<= m_selection_hack
) && (m_selection_hack 
!= wxNOT_FOUND
)) 
 180         // move the selection forward one 
 184     return GtkAddHelper(menu
, pos
, item
); 
 187 void wxChoice::DoSetItemClientData(unsigned int n
, void* clientData
) 
 189     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice control") ); 
 191     wxList::compatibility_iterator node 
= m_clientList
.Item( n 
); 
 192     wxCHECK_RET( node
, wxT("invalid index in wxChoice::DoSetItemClientData") ); 
 194     node
->SetData( (wxObject
*) clientData 
); 
 197 void* wxChoice::DoGetItemClientData(unsigned int n
) const 
 199     wxCHECK_MSG( m_widget 
!= NULL
, NULL
, wxT("invalid choice control") ); 
 201     wxList::compatibility_iterator node 
= m_clientList
.Item( n 
); 
 202     wxCHECK_MSG( node
, NULL
, wxT("invalid index in wxChoice::DoGetItemClientData") ); 
 204     return node
->GetData(); 
 207 void wxChoice::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
) 
 209     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice control") ); 
 211     wxList::compatibility_iterator node 
= m_clientList
.Item( n 
); 
 212     wxCHECK_RET( node
, wxT("invalid index in wxChoice::DoSetItemClientObject") ); 
 214     // wxItemContainer already deletes data for us 
 216     node
->SetData( (wxObject
*) clientData 
); 
 219 wxClientData
* wxChoice::DoGetItemClientObject(unsigned int n
) const 
 221     wxCHECK_MSG( m_widget 
!= NULL
, (wxClientData
*) NULL
, wxT("invalid choice control") ); 
 223     wxList::compatibility_iterator node 
= m_clientList
.Item( n 
); 
 224     wxCHECK_MSG( node
, (wxClientData 
*)NULL
, 
 225                  wxT("invalid index in wxChoice::DoGetItemClientObject") ); 
 227     return (wxClientData
*) node
->GetData(); 
 230 void wxChoice::Clear() 
 232     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice") ); 
 234     gtk_option_menu_remove_menu( GTK_OPTION_MENU(m_widget
) ); 
 235     GtkWidget 
*menu 
= gtk_menu_new(); 
 236     gtk_option_menu_set_menu( GTK_OPTION_MENU(m_widget
), menu 
); 
 238     if ( HasClientObjectData() ) 
 240         // destroy the data (due to Robert's idea of using wxList<wxObject> 
 241         // and not wxList<wxClientData> we can't just say 
 242         // m_clientList.DeleteContents(true) - this would crash! 
 243         wxList::compatibility_iterator node 
= m_clientList
.GetFirst(); 
 246             delete (wxClientData 
*)node
->GetData(); 
 247             node 
= node
->GetNext(); 
 250     m_clientList
.Clear(); 
 255     // begin with no selection 
 256     m_selection_hack 
= wxNOT_FOUND
; 
 259 void wxChoice::Delete(unsigned int n
) 
 261     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice") ); 
 263     // VZ: apparently GTK+ doesn't have a built-in function to do it (not even 
 264     //     in 2.0), hence this dumb implementation -- still better than nothing 
 266     unsigned int count 
= GetCount(); 
 268     wxCHECK_RET( IsValid(n
), _T("invalid index in wxChoice::Delete") ); 
 270     // if the item to delete is before the selection, and the selection is valid 
 271     if (((int)n 
< m_selection_hack
) && (m_selection_hack 
!= wxNOT_FOUND
)) 
 273         // move the selection back one 
 276     else if ((int)n 
== m_selection_hack
) 
 278         // invalidate the selection 
 279         m_selection_hack 
= wxNOT_FOUND
; 
 282     const bool hasClientData 
= m_clientDataItemsType 
!= wxClientData_None
; 
 283     const bool hasObjectData 
= m_clientDataItemsType 
== wxClientData_Object
; 
 285     wxList::compatibility_iterator node 
= m_clientList
.GetFirst(); 
 288     wxArrayPtrVoid itemsData
; 
 290     for ( i 
= 0; i 
< count
; i
++ ) 
 294             items
.Add(GetString(i
)); 
 297                 // also save the client data 
 298                 itemsData
.Add(node
->GetData()); 
 301         else // need to delete the client object too 
 305                 delete (wxClientData 
*)node
->GetData(); 
 311             node 
= node
->GetNext(); 
 317         // prevent Clear() from destroying all client data 
 318         m_clientDataItemsType 
= wxClientData_None
; 
 323     for ( i 
= 0; i 
< count 
- 1; i
++ ) 
 328             SetClientObject(i
, (wxClientData 
*)itemsData
[i
]); 
 329         else if ( hasClientData 
) 
 330             SetClientData(i
, itemsData
[i
]); 
 334 int wxChoice::FindString( const wxString 
&string
, bool bCase 
) const 
 336     wxCHECK_MSG( m_widget 
!= NULL
, wxNOT_FOUND
, wxT("invalid choice") ); 
 338     // If you read this code once and you think you understand 
 339     // it, then you are very wrong. Robert Roebling. 
 341     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) ); 
 343     GList 
*child 
= menu_shell
->children
; 
 346         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
 347         GtkLabel 
*label 
= (GtkLabel 
*) NULL
; 
 349             label 
= GTK_LABEL(bin
->child
); 
 351             label 
= GTK_LABEL( BUTTON_CHILD(m_widget
) ); 
 353         wxASSERT_MSG( label 
!= NULL 
, wxT("wxChoice: invalid label") ); 
 355          wxString 
tmp( label
->label 
); 
 357          if (string
.IsSameAs( tmp
, bCase 
)) 
 367 int wxChoice::GetSelection() const 
 369     wxCHECK_MSG( m_widget 
!= NULL
, wxNOT_FOUND
, wxT("invalid choice") ); 
 371     return m_selection_hack
; 
 375 void wxChoice::SetString(unsigned int n
, const wxString
& str 
) 
 377     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice") ); 
 379     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) ); 
 380     unsigned int count 
= 0; 
 381     GList 
*child 
= menu_shell
->children
; 
 384         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
 387             GtkLabel 
*label 
= (GtkLabel 
*) NULL
; 
 389                 label 
= GTK_LABEL(bin
->child
); 
 391                 label 
= GTK_LABEL( BUTTON_CHILD(m_widget
) ); 
 393             wxASSERT_MSG( label 
!= NULL 
, wxT("wxChoice: invalid label") ); 
 395             gtk_label_set_text( label
, wxGTK_CONV( str 
) ); 
 404 wxString 
wxChoice::GetString(unsigned int n
) const 
 406     wxCHECK_MSG( m_widget 
!= NULL
, wxEmptyString
, wxT("invalid choice") ); 
 408     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) ); 
 409     unsigned int count 
= 0; 
 410     GList 
*child 
= menu_shell
->children
; 
 413         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
 416             GtkLabel 
*label 
= (GtkLabel 
*) NULL
; 
 418                 label 
= GTK_LABEL(bin
->child
); 
 420                 label 
= GTK_LABEL( BUTTON_CHILD(m_widget
) ); 
 422             wxASSERT_MSG( label 
!= NULL 
, wxT("wxChoice: invalid label") ); 
 424             return wxString( label
->label 
); 
 430     wxFAIL_MSG( wxT("wxChoice: invalid index in GetString()") ); 
 432     return wxEmptyString
; 
 435 unsigned int wxChoice::GetCount() const 
 437     wxCHECK_MSG( m_widget 
!= NULL
, 0, wxT("invalid choice") ); 
 439     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) ); 
 440     unsigned int count 
= 0; 
 441     GList 
*child 
= menu_shell
->children
; 
 450 void wxChoice::SetSelection( int n 
) 
 452     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice") ); 
 455     gtk_option_menu_set_history( GTK_OPTION_MENU(m_widget
), (gint
)tmp 
); 
 457     // set the local selection variable manually 
 458     if (IsValid((unsigned int)n
)) 
 460         // a valid selection has been made 
 461         m_selection_hack 
= n
; 
 463     else if ((n 
== wxNOT_FOUND
) || (GetCount() == 0)) 
 465         // invalidates the selection if there are no items 
 466         // or if it is specifically set to wxNOT_FOUND 
 467         m_selection_hack 
= wxNOT_FOUND
; 
 471         // this selects the first item by default if the selection is out of bounds 
 472         m_selection_hack 
= 0; 
 476 void wxChoice::DoApplyWidgetStyle(GtkRcStyle 
*style
) 
 478     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) ); 
 480     gtk_widget_modify_style( m_widget
, style 
); 
 481     gtk_widget_modify_style( GTK_WIDGET( menu_shell 
), style 
); 
 483     GList 
*child 
= menu_shell
->children
; 
 486         gtk_widget_modify_style( GTK_WIDGET( child
->data 
), style 
); 
 488         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
 489         GtkWidget 
*label 
= (GtkWidget 
*) NULL
; 
 493             label 
= BUTTON_CHILD(m_widget
); 
 495         gtk_widget_modify_style( label
, style 
); 
 501 int wxChoice::GtkAddHelper(GtkWidget 
*menu
, unsigned int pos
, const wxString
& item
) 
 503     wxCHECK_MSG(pos
<=m_clientList
.GetCount(), -1, wxT("invalid index")); 
 505     GtkWidget 
*menu_item 
= gtk_menu_item_new_with_label( wxGTK_CONV( item 
) ); 
 510         // sorted control, need to insert at the correct index 
 511         index 
= m_strings
->Add(item
); 
 513         gtk_menu_insert( GTK_MENU(menu
), menu_item
, index 
); 
 517             m_clientList
.Insert( m_clientList
.Item(index 
- 1), 
 522             m_clientList
.Insert( (wxObject
*) NULL 
); 
 527         // don't call wxChoice::GetCount() from here because it doesn't work 
 528         // if we're called from ctor (and GtkMenuShell is still NULL) 
 530         // normal control, just append 
 531         if (pos 
== m_clientList
.GetCount()) 
 533             gtk_menu_append( GTK_MENU(menu
), menu_item 
); 
 534             m_clientList
.Append( (wxObject
*) NULL 
); 
 535             index 
= m_clientList
.GetCount() - 1; 
 539             gtk_menu_insert( GTK_MENU(menu
), menu_item
, pos 
); 
 540             m_clientList
.Insert( pos
, (wxObject
*) NULL 
); 
 545     if (GTK_WIDGET_REALIZED(m_widget
)) 
 547         gtk_widget_realize( menu_item 
); 
 548         gtk_widget_realize( GTK_BIN(menu_item
)->child 
); 
 553     // The best size of a wxChoice should probably 
 554     // be changed everytime the control has been 
 555     // changed, but at least after adding an item 
 556     // it has to change. Adapted from Matt Ownby. 
 557     InvalidateBestSize(); 
 559     gtk_signal_connect_after( GTK_OBJECT( menu_item 
), "activate", 
 560       GTK_SIGNAL_FUNC(gtk_choice_clicked_callback
), (gpointer
*)this ); 
 562     gtk_widget_show( menu_item 
); 
 564     // return the index of the item in the control 
 568 wxSize 
wxChoice::DoGetBestSize() const 
 570     wxSize 
ret( wxControl::DoGetBestSize() ); 
 572     // we know better our horizontal extent: it depends on the longest string 
 578         unsigned int count 
= GetCount(); 
 579         for ( unsigned int n 
= 0; n 
< count
; n
++ ) 
 581             GetTextExtent(GetString(n
), &width
, NULL
, NULL
, NULL 
); 
 586         // add extra for the choice "=" button 
 588         // VZ: I don't know how to get the right value, it seems to be in 
 589         //     GtkOptionMenuProps struct from gtkoptionmenu.c but we can't get 
 590         //     to it - maybe we can use gtk_option_menu_size_request() for this 
 593         //     This default value works only for the default GTK+ theme (i.e. 
 594         //     no theme at all) (FIXME) 
 595         static const int widthChoiceIndicator 
= 35; 
 596         ret
.x 
+= widthChoiceIndicator
; 
 599     // but not less than the minimal width 
 603     // If this request_size is called with no entries then 
 604     // the returned height is wrong. Give it a reasonable 
 607         ret
.y 
= 8 + GetCharHeight(); 
 613 bool wxChoice::IsOwnGtkWindow( GdkWindow 
*window 
) 
 615     return (window 
== m_widget
->window
); 
 620 wxChoice::GetClassDefaultAttributes(wxWindowVariant 
WXUNUSED(variant
)) 
 622     return GetDefaultAttributesFromGTKWidget(gtk_option_menu_new
); 
 626 #endif // wxUSE_CHOICE