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" 
  17     #include "wx/arrstr.h" 
  20 #include "wx/gtk1/private.h" 
  22 //----------------------------------------------------------------------------- 
  24 //----------------------------------------------------------------------------- 
  26 extern void wxapp_install_idle_handler(); 
  29 //----------------------------------------------------------------------------- 
  31 //----------------------------------------------------------------------------- 
  33 extern bool   g_blockEventsOnDrag
; 
  35 //----------------------------------------------------------------------------- 
  37 //----------------------------------------------------------------------------- 
  40 static void gtk_choice_clicked_callback( GtkWidget 
*WXUNUSED(widget
), wxChoice 
*choice 
) 
  43       wxapp_install_idle_handler(); 
  45     if (!choice
->m_hasVMT
) return; 
  47     if (g_blockEventsOnDrag
) return; 
  49     int selection 
= wxNOT_FOUND
; 
  51     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(choice
->GetHandle()) ) ); 
  54     GList 
*child 
= menu_shell
->children
; 
  57         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
  67     choice
->m_selection_hack 
= selection
; 
  69     wxCommandEvent 
event(wxEVT_COMMAND_CHOICE_SELECTED
, choice
->GetId() ); 
  70     int n 
= choice
->GetSelection(); 
  73     event
.SetString( choice
->GetStringSelection() ); 
  74     event
.SetEventObject(choice
); 
  76     if ( choice
->HasClientObjectData() ) 
  77         event
.SetClientObject( choice
->GetClientObject(n
) ); 
  78     else if ( choice
->HasClientUntypedData() ) 
  79         event
.SetClientData( choice
->GetClientData(n
) ); 
  81     choice
->HandleWindowEvent(event
); 
  85 //----------------------------------------------------------------------------- 
  87 //----------------------------------------------------------------------------- 
  94 bool wxChoice::Create( wxWindow 
*parent
, wxWindowID id
, 
  95                        const wxPoint 
&pos
, const wxSize 
&size
, 
  96                        const wxArrayString
& choices
, 
  97                        long style
, const wxValidator
& validator
, 
  98                        const wxString 
&name 
) 
 100     wxCArrayString 
chs(choices
); 
 102     return Create( parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(), 
 103                    style
, validator
, name 
); 
 106 bool wxChoice::Create( wxWindow 
*parent
, wxWindowID id
, 
 107                        const wxPoint 
&pos
, const wxSize 
&size
, 
 108                        int n
, const wxString choices
[], 
 109                        long style
, const wxValidator
& validator
, const wxString 
&name 
) 
 112 #if (GTK_MINOR_VERSION > 0) 
 113     m_acceptsFocus 
= true; 
 116     if (!PreCreation( parent
, pos
, size 
) || 
 117         !CreateBase( parent
, id
, pos
, size
, style
, validator
, name 
)) 
 119         wxFAIL_MSG( wxT("wxChoice creation failed") ); 
 123     m_widget 
= gtk_option_menu_new(); 
 125     if ( style 
& wxCB_SORT 
) 
 127         // if our m_strings != NULL, Append() will check for it and insert 
 128         // items in the correct order 
 129         m_strings 
= new wxSortedArrayString
; 
 132     // begin with no selection 
 133     m_selection_hack 
= wxNOT_FOUND
; 
 135     GtkWidget 
*menu 
= gtk_menu_new(); 
 137     for (unsigned int i 
= 0; i 
< (unsigned int)n
; i
++) 
 139         GtkAddHelper(menu
, i
, choices
[i
]); 
 142     gtk_option_menu_set_menu( GTK_OPTION_MENU(m_widget
), menu 
); 
 144     m_parent
->DoAddChild( this ); 
 147     SetInitialSize(size
); // need this too because this is a wxControlWithItems 
 152 wxChoice::~wxChoice() 
 159 int wxChoice::DoInsertItems(const wxArrayStringsAdapter 
& items
, 
 161                             void **clientData
, wxClientDataType type
) 
 163     wxCHECK_MSG( m_widget 
!= NULL
, -1, wxT("invalid choice control") ); 
 165     const unsigned int count 
= items
.GetCount(); 
 167     GtkWidget 
*menu 
= gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ); 
 169     for ( unsigned int i 
= 0; i 
< count
; ++i
, ++pos 
) 
 171         int n 
= GtkAddHelper(menu
, pos
, items
[i
]); 
 172         if ( n 
== wxNOT_FOUND 
) 
 175         AssignNewItemClientData(n
, clientData
, i
, type
); 
 178     // if the item to insert is at or before the selection, and the selection is valid 
 179     if (((int)pos 
<= m_selection_hack
) && (m_selection_hack 
!= wxNOT_FOUND
)) 
 181         // move the selection forward 
 182         m_selection_hack 
+= count
; 
 188 void wxChoice::DoSetItemClientData(unsigned int n
, void* clientData
) 
 190     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice control") ); 
 192     wxList::compatibility_iterator node 
= m_clientList
.Item( n 
); 
 193     wxCHECK_RET( node
, wxT("invalid index in wxChoice::DoSetItemClientData") ); 
 195     node
->SetData( (wxObject
*) clientData 
); 
 198 void* wxChoice::DoGetItemClientData(unsigned int n
) const 
 200     wxCHECK_MSG( m_widget 
!= NULL
, NULL
, wxT("invalid choice control") ); 
 202     wxList::compatibility_iterator node 
= m_clientList
.Item( n 
); 
 203     wxCHECK_MSG( node
, NULL
, wxT("invalid index in wxChoice::DoGetItemClientData") ); 
 205     return node
->GetData(); 
 208 void wxChoice::DoClear() 
 210     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice") ); 
 212     gtk_option_menu_remove_menu( GTK_OPTION_MENU(m_widget
) ); 
 213     GtkWidget 
*menu 
= gtk_menu_new(); 
 214     gtk_option_menu_set_menu( GTK_OPTION_MENU(m_widget
), menu 
); 
 216     m_clientList
.Clear(); 
 221     // begin with no selection 
 222     m_selection_hack 
= wxNOT_FOUND
; 
 225 void wxChoice::DoDeleteOneItem(unsigned int n
) 
 227     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice") ); 
 229     wxCHECK_RET( IsValid(n
), wxT("invalid index in wxChoice::Delete") ); 
 231     // if the item to delete is before the selection, and the selection is valid 
 232     if (((int)n 
< m_selection_hack
) && (m_selection_hack 
!= wxNOT_FOUND
)) 
 234         // move the selection back one 
 237     else if ((int)n 
== m_selection_hack
) 
 239         // invalidate the selection 
 240         m_selection_hack 
= wxNOT_FOUND
; 
 243     // VZ: apparently GTK+ doesn't have a built-in function to do it (not even 
 244     //     in 2.0), hence this dumb implementation -- still better than nothing 
 245     const unsigned int count 
= GetCount(); 
 247     wxList::compatibility_iterator node 
= m_clientList
.GetFirst(); 
 250     wxArrayPtrVoid itemsData
; 
 252     for ( unsigned i 
= 0; i 
< count
; i
++, node 
= node
->GetNext() ) 
 256             items
.Add(GetString(i
)); 
 257             itemsData
.Add(node
->GetData()); 
 263     void ** const data 
= &itemsData
[0]; 
 264     if ( HasClientObjectData() ) 
 265         Append(items
, reinterpret_cast<wxClientData 
**>(data
)); 
 270 int wxChoice::FindString( const wxString 
&string
, bool bCase 
) const 
 272     wxCHECK_MSG( m_widget 
!= NULL
, wxNOT_FOUND
, wxT("invalid choice") ); 
 274     // If you read this code once and you think you understand 
 275     // it, then you are very wrong. Robert Roebling. 
 277     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) ); 
 279     GList 
*child 
= menu_shell
->children
; 
 282         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
 283         GtkLabel 
*label 
= NULL
; 
 285             label 
= GTK_LABEL(bin
->child
); 
 287             label 
= GTK_LABEL( BUTTON_CHILD(m_widget
) ); 
 289         wxASSERT_MSG( label 
!= NULL 
, wxT("wxChoice: invalid label") ); 
 291          wxString 
tmp( label
->label 
); 
 293          if (string
.IsSameAs( tmp
, bCase 
)) 
 303 int wxChoice::GetSelection() const 
 305     wxCHECK_MSG( m_widget 
!= NULL
, wxNOT_FOUND
, wxT("invalid choice") ); 
 307     return m_selection_hack
; 
 311 void wxChoice::SetString(unsigned int n
, const wxString
& str 
) 
 313     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice") ); 
 315     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) ); 
 316     unsigned int count 
= 0; 
 317     GList 
*child 
= menu_shell
->children
; 
 320         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
 323             GtkLabel 
*label 
= NULL
; 
 325                 label 
= GTK_LABEL(bin
->child
); 
 327                 label 
= GTK_LABEL( BUTTON_CHILD(m_widget
) ); 
 329             wxASSERT_MSG( label 
!= NULL 
, wxT("wxChoice: invalid label") ); 
 331             gtk_label_set_text( label
, wxGTK_CONV( str 
) ); 
 340 wxString 
wxChoice::GetString(unsigned int n
) const 
 342     wxCHECK_MSG( m_widget 
!= NULL
, wxEmptyString
, wxT("invalid choice") ); 
 344     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) ); 
 345     unsigned int count 
= 0; 
 346     GList 
*child 
= menu_shell
->children
; 
 349         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
 352             GtkLabel 
*label 
= NULL
; 
 354                 label 
= GTK_LABEL(bin
->child
); 
 356                 label 
= GTK_LABEL( BUTTON_CHILD(m_widget
) ); 
 358             wxASSERT_MSG( label 
!= NULL 
, wxT("wxChoice: invalid label") ); 
 360             return wxString( label
->label 
); 
 366     wxFAIL_MSG( wxT("wxChoice: invalid index in GetString()") ); 
 368     return wxEmptyString
; 
 371 unsigned int wxChoice::GetCount() const 
 373     wxCHECK_MSG( m_widget 
!= NULL
, 0, wxT("invalid choice") ); 
 375     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) ); 
 376     unsigned int count 
= 0; 
 377     GList 
*child 
= menu_shell
->children
; 
 386 void wxChoice::SetSelection( int n 
) 
 388     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid choice") ); 
 391     gtk_option_menu_set_history( GTK_OPTION_MENU(m_widget
), (gint
)tmp 
); 
 393     // set the local selection variable manually 
 394     if (IsValid((unsigned int)n
)) 
 396         // a valid selection has been made 
 397         m_selection_hack 
= n
; 
 399     else if ((n 
== wxNOT_FOUND
) || (GetCount() == 0)) 
 401         // invalidates the selection if there are no items 
 402         // or if it is specifically set to wxNOT_FOUND 
 403         m_selection_hack 
= wxNOT_FOUND
; 
 407         // this selects the first item by default if the selection is out of bounds 
 408         m_selection_hack 
= 0; 
 412 void wxChoice::DoApplyWidgetStyle(GtkRcStyle 
*style
) 
 414     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) ); 
 416     gtk_widget_modify_style( m_widget
, style 
); 
 417     gtk_widget_modify_style( GTK_WIDGET( menu_shell 
), style 
); 
 419     GList 
*child 
= menu_shell
->children
; 
 422         gtk_widget_modify_style( GTK_WIDGET( child
->data 
), style 
); 
 424         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
 425         GtkWidget 
*label 
= NULL
; 
 429             label 
= BUTTON_CHILD(m_widget
); 
 431         gtk_widget_modify_style( label
, style 
); 
 437 int wxChoice::GtkAddHelper(GtkWidget 
*menu
, unsigned int pos
, const wxString
& item
) 
 439     wxCHECK_MSG(pos
<=m_clientList
.GetCount(), -1, wxT("invalid index")); 
 441     GtkWidget 
*menu_item 
= gtk_menu_item_new_with_label( wxGTK_CONV( item 
) ); 
 446         // sorted control, need to insert at the correct index 
 447         index 
= m_strings
->Add(item
); 
 449         gtk_menu_insert( GTK_MENU(menu
), menu_item
, index 
); 
 453             m_clientList
.Insert( m_clientList
.Item(index 
- 1), 
 458             m_clientList
.Insert( NULL 
); 
 463         // don't call wxChoice::GetCount() from here because it doesn't work 
 464         // if we're called from ctor (and GtkMenuShell is still NULL) 
 466         // normal control, just append 
 467         if (pos 
== m_clientList
.GetCount()) 
 469             gtk_menu_append( GTK_MENU(menu
), menu_item 
); 
 470             m_clientList
.Append( NULL 
); 
 471             index 
= m_clientList
.GetCount() - 1; 
 475             gtk_menu_insert( GTK_MENU(menu
), menu_item
, pos 
); 
 476             m_clientList
.Insert( pos
, NULL 
); 
 481     if (GTK_WIDGET_REALIZED(m_widget
)) 
 483         gtk_widget_realize( menu_item 
); 
 484         gtk_widget_realize( GTK_BIN(menu_item
)->child 
); 
 489     // The best size of a wxChoice should probably 
 490     // be changed everytime the control has been 
 491     // changed, but at least after adding an item 
 492     // it has to change. Adapted from Matt Ownby. 
 493     InvalidateBestSize(); 
 495     gtk_signal_connect_after( GTK_OBJECT( menu_item 
), "activate", 
 496       GTK_SIGNAL_FUNC(gtk_choice_clicked_callback
), (gpointer
*)this ); 
 498     gtk_widget_show( menu_item 
); 
 500     // return the index of the item in the control 
 504 wxSize 
wxChoice::DoGetBestSize() const 
 506     wxSize 
ret( wxControl::DoGetBestSize() ); 
 508     // we know better our horizontal extent: it depends on the longest string 
 514         unsigned int count 
= GetCount(); 
 515         for ( unsigned int n 
= 0; n 
< count
; n
++ ) 
 517             GetTextExtent(GetString(n
), &width
, NULL
, NULL
, NULL 
); 
 522         // add extra for the choice "=" button 
 524         // VZ: I don't know how to get the right value, it seems to be in 
 525         //     GtkOptionMenuProps struct from gtkoptionmenu.c but we can't get 
 526         //     to it - maybe we can use gtk_option_menu_size_request() for this 
 529         //     This default value works only for the default GTK+ theme (i.e. 
 530         //     no theme at all) (FIXME) 
 531         static const int widthChoiceIndicator 
= 35; 
 532         ret
.x 
+= widthChoiceIndicator
; 
 535     // but not less than the minimal width 
 539     // If this request_size is called with no entries then 
 540     // the returned height is wrong. Give it a reasonable 
 543         ret
.y 
= 8 + GetCharHeight(); 
 549 bool wxChoice::IsOwnGtkWindow( GdkWindow 
*window 
) 
 551     return (window 
== m_widget
->window
); 
 556 wxChoice::GetClassDefaultAttributes(wxWindowVariant 
WXUNUSED(variant
)) 
 558     return GetDefaultAttributesFromGTKWidget(gtk_option_menu_new
); 
 562 #endif // wxUSE_CHOICE