1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk1/choice.cpp
4 // Author: Robert Roebling
5 // Copyright: (c) 1998 Robert Roebling
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
13 #include "wx/choice.h"
16 #include "wx/arrstr.h"
19 #include "wx/gtk1/private.h"
21 //-----------------------------------------------------------------------------
23 //-----------------------------------------------------------------------------
25 extern void wxapp_install_idle_handler();
28 //-----------------------------------------------------------------------------
30 //-----------------------------------------------------------------------------
32 extern bool g_blockEventsOnDrag
;
34 //-----------------------------------------------------------------------------
36 //-----------------------------------------------------------------------------
39 static void gtk_choice_clicked_callback( GtkWidget
*WXUNUSED(widget
), wxChoice
*choice
)
42 wxapp_install_idle_handler();
44 if (!choice
->m_hasVMT
) return;
46 if (g_blockEventsOnDrag
) return;
48 int selection
= wxNOT_FOUND
;
50 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(choice
->GetHandle()) ) );
53 GList
*child
= menu_shell
->children
;
56 GtkBin
*bin
= GTK_BIN( child
->data
);
66 choice
->m_selection_hack
= selection
;
68 wxCommandEvent
event(wxEVT_CHOICE
, choice
->GetId() );
69 int n
= choice
->GetSelection();
72 event
.SetString( choice
->GetStringSelection() );
73 event
.SetEventObject(choice
);
75 if ( choice
->HasClientObjectData() )
76 event
.SetClientObject( choice
->GetClientObject(n
) );
77 else if ( choice
->HasClientUntypedData() )
78 event
.SetClientData( choice
->GetClientData(n
) );
80 choice
->HandleWindowEvent(event
);
84 //-----------------------------------------------------------------------------
86 //-----------------------------------------------------------------------------
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, Append() 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 SetInitialSize(size
); // need this too because this is a wxControlWithItems
151 wxChoice::~wxChoice()
158 int wxChoice::DoInsertItems(const wxArrayStringsAdapter
& items
,
160 void **clientData
, wxClientDataType type
)
162 wxCHECK_MSG( m_widget
!= NULL
, -1, wxT("invalid choice control") );
164 const unsigned int count
= items
.GetCount();
166 GtkWidget
*menu
= gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) );
168 for ( unsigned int i
= 0; i
< count
; ++i
, ++pos
)
170 int n
= GtkAddHelper(menu
, pos
, items
[i
]);
171 if ( n
== wxNOT_FOUND
)
174 AssignNewItemClientData(n
, clientData
, i
, type
);
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
181 m_selection_hack
+= count
;
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::DoClear()
209 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid choice") );
211 gtk_option_menu_remove_menu( GTK_OPTION_MENU(m_widget
) );
212 GtkWidget
*menu
= gtk_menu_new();
213 gtk_option_menu_set_menu( GTK_OPTION_MENU(m_widget
), menu
);
215 m_clientList
.Clear();
220 // begin with no selection
221 m_selection_hack
= wxNOT_FOUND
;
224 void wxChoice::DoDeleteOneItem(unsigned int n
)
226 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid choice") );
228 wxCHECK_RET( IsValid(n
), wxT("invalid index in wxChoice::Delete") );
230 // if the item to delete is before the selection, and the selection is valid
231 if (((int)n
< m_selection_hack
) && (m_selection_hack
!= wxNOT_FOUND
))
233 // move the selection back one
236 else if ((int)n
== m_selection_hack
)
238 // invalidate the selection
239 m_selection_hack
= wxNOT_FOUND
;
242 // VZ: apparently GTK+ doesn't have a built-in function to do it (not even
243 // in 2.0), hence this dumb implementation -- still better than nothing
244 const unsigned int count
= GetCount();
246 wxList::compatibility_iterator node
= m_clientList
.GetFirst();
249 wxArrayPtrVoid itemsData
;
251 for ( unsigned i
= 0; i
< count
; i
++, node
= node
->GetNext() )
255 items
.Add(GetString(i
));
256 itemsData
.Add(node
->GetData());
262 void ** const data
= &itemsData
[0];
263 if ( HasClientObjectData() )
264 Append(items
, reinterpret_cast<wxClientData
**>(data
));
269 int wxChoice::FindString( const wxString
&string
, bool bCase
) const
271 wxCHECK_MSG( m_widget
!= NULL
, wxNOT_FOUND
, wxT("invalid choice") );
273 // If you read this code once and you think you understand
274 // it, then you are very wrong. Robert Roebling.
276 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) );
278 GList
*child
= menu_shell
->children
;
281 GtkBin
*bin
= GTK_BIN( child
->data
);
282 GtkLabel
*label
= NULL
;
284 label
= GTK_LABEL(bin
->child
);
286 label
= GTK_LABEL( BUTTON_CHILD(m_widget
) );
288 wxASSERT_MSG( label
!= NULL
, wxT("wxChoice: invalid label") );
290 wxString
tmp( label
->label
);
292 if (string
.IsSameAs( tmp
, bCase
))
302 int wxChoice::GetSelection() const
304 wxCHECK_MSG( m_widget
!= NULL
, wxNOT_FOUND
, wxT("invalid choice") );
306 return m_selection_hack
;
310 void wxChoice::SetString(unsigned int n
, const wxString
& str
)
312 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid choice") );
314 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) );
315 unsigned int count
= 0;
316 GList
*child
= menu_shell
->children
;
319 GtkBin
*bin
= GTK_BIN( child
->data
);
322 GtkLabel
*label
= NULL
;
324 label
= GTK_LABEL(bin
->child
);
326 label
= GTK_LABEL( BUTTON_CHILD(m_widget
) );
328 wxASSERT_MSG( label
!= NULL
, wxT("wxChoice: invalid label") );
330 gtk_label_set_text( label
, wxGTK_CONV( str
) );
339 wxString
wxChoice::GetString(unsigned int n
) const
341 wxCHECK_MSG( m_widget
!= NULL
, wxEmptyString
, wxT("invalid choice") );
343 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) );
344 unsigned int count
= 0;
345 GList
*child
= menu_shell
->children
;
348 GtkBin
*bin
= GTK_BIN( child
->data
);
351 GtkLabel
*label
= NULL
;
353 label
= GTK_LABEL(bin
->child
);
355 label
= GTK_LABEL( BUTTON_CHILD(m_widget
) );
357 wxASSERT_MSG( label
!= NULL
, wxT("wxChoice: invalid label") );
359 return wxString( label
->label
);
365 wxFAIL_MSG( wxT("wxChoice: invalid index in GetString()") );
367 return wxEmptyString
;
370 unsigned int wxChoice::GetCount() const
372 wxCHECK_MSG( m_widget
!= NULL
, 0, wxT("invalid choice") );
374 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) );
375 unsigned int count
= 0;
376 GList
*child
= menu_shell
->children
;
385 void wxChoice::SetSelection( int n
)
387 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid choice") );
390 gtk_option_menu_set_history( GTK_OPTION_MENU(m_widget
), (gint
)tmp
);
392 // set the local selection variable manually
393 if (IsValid((unsigned int)n
))
395 // a valid selection has been made
396 m_selection_hack
= n
;
398 else if ((n
== wxNOT_FOUND
) || (GetCount() == 0))
400 // invalidates the selection if there are no items
401 // or if it is specifically set to wxNOT_FOUND
402 m_selection_hack
= wxNOT_FOUND
;
406 // this selects the first item by default if the selection is out of bounds
407 m_selection_hack
= 0;
411 void wxChoice::DoApplyWidgetStyle(GtkRcStyle
*style
)
413 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL( gtk_option_menu_get_menu( GTK_OPTION_MENU(m_widget
) ) );
415 gtk_widget_modify_style( m_widget
, style
);
416 gtk_widget_modify_style( GTK_WIDGET( menu_shell
), style
);
418 GList
*child
= menu_shell
->children
;
421 gtk_widget_modify_style( GTK_WIDGET( child
->data
), style
);
423 GtkBin
*bin
= GTK_BIN( child
->data
);
424 GtkWidget
*label
= NULL
;
428 label
= BUTTON_CHILD(m_widget
);
430 gtk_widget_modify_style( label
, style
);
436 int wxChoice::GtkAddHelper(GtkWidget
*menu
, unsigned int pos
, const wxString
& item
)
438 wxCHECK_MSG(pos
<=m_clientList
.GetCount(), -1, wxT("invalid index"));
440 GtkWidget
*menu_item
= gtk_menu_item_new_with_label( wxGTK_CONV( item
) );
445 // sorted control, need to insert at the correct index
446 index
= m_strings
->Add(item
);
448 gtk_menu_insert( GTK_MENU(menu
), menu_item
, index
);
452 m_clientList
.Insert( m_clientList
.Item(index
- 1),
457 m_clientList
.Insert( NULL
);
462 // don't call wxChoice::GetCount() from here because it doesn't work
463 // if we're called from ctor (and GtkMenuShell is still NULL)
465 // normal control, just append
466 if (pos
== m_clientList
.GetCount())
468 gtk_menu_append( GTK_MENU(menu
), menu_item
);
469 m_clientList
.Append( NULL
);
470 index
= m_clientList
.GetCount() - 1;
474 gtk_menu_insert( GTK_MENU(menu
), menu_item
, pos
);
475 m_clientList
.Insert( pos
, NULL
);
480 if (GTK_WIDGET_REALIZED(m_widget
))
482 gtk_widget_realize( menu_item
);
483 gtk_widget_realize( GTK_BIN(menu_item
)->child
);
488 // The best size of a wxChoice should probably
489 // be changed everytime the control has been
490 // changed, but at least after adding an item
491 // it has to change. Adapted from Matt Ownby.
492 InvalidateBestSize();
494 gtk_signal_connect_after( GTK_OBJECT( menu_item
), "activate",
495 GTK_SIGNAL_FUNC(gtk_choice_clicked_callback
), (gpointer
*)this );
497 gtk_widget_show( menu_item
);
499 // return the index of the item in the control
503 wxSize
wxChoice::DoGetBestSize() const
505 wxSize
ret( wxControl::DoGetBestSize() );
507 // we know better our horizontal extent: it depends on the longest string
513 unsigned int count
= GetCount();
514 for ( unsigned int n
= 0; n
< count
; n
++ )
516 GetTextExtent(GetString(n
), &width
, NULL
, NULL
, NULL
);
521 // add extra for the choice "=" button
523 // VZ: I don't know how to get the right value, it seems to be in
524 // GtkOptionMenuProps struct from gtkoptionmenu.c but we can't get
525 // to it - maybe we can use gtk_option_menu_size_request() for this
528 // This default value works only for the default GTK+ theme (i.e.
529 // no theme at all) (FIXME)
530 static const int widthChoiceIndicator
= 35;
531 ret
.x
+= widthChoiceIndicator
;
534 // but not less than the minimal width
538 // If this request_size is called with no entries then
539 // the returned height is wrong. Give it a reasonable
542 ret
.y
= 8 + GetCharHeight();
548 bool wxChoice::IsOwnGtkWindow( GdkWindow
*window
)
550 return (window
== m_widget
->window
);
555 wxChoice::GetClassDefaultAttributes(wxWindowVariant
WXUNUSED(variant
))
557 return GetDefaultAttributesFromGTKWidget(gtk_option_menu_new
);
561 #endif // wxUSE_CHOICE