1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/listbox.cpp
4 // Author: Robert Roebling
5 // Modified By: Ryan Norton (GtkTreeView implementation)
7 // Copyright: (c) 1998 Robert Roebling
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
18 #include "wx/listbox.h"
19 #include "wx/dynarray.h"
20 #include "wx/arrstr.h"
23 #include "wx/checklst.h"
24 #include "wx/settings.h"
26 #include "wx/gtk/private.h"
27 #include "wx/gtk/treeentry_gtk.h"
30 #include "wx/tooltip.h"
35 #include <gdk/gdkkeysyms.h>
37 //-----------------------------------------------------------------------------
39 //-----------------------------------------------------------------------------
41 extern bool g_blockEventsOnDrag
;
42 extern bool g_blockEventsOnScroll
;
43 extern wxCursor g_globalCursor
;
46 //-----------------------------------------------------------------------------
48 //-----------------------------------------------------------------------------
50 extern void wxapp_install_idle_handler();
53 //-----------------------------------------------------------------------------
54 // Macro to tell which row the strings are in (1 if native checklist, 0 if not)
55 //-----------------------------------------------------------------------------
57 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
58 # define WXLISTBOX_DATACOLUMN_ARG(x) (x->m_hasCheckBoxes ? 1 : 0)
60 # define WXLISTBOX_DATACOLUMN_ARG(x) (0)
61 #endif // wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
63 #define WXLISTBOX_DATACOLUMN WXLISTBOX_DATACOLUMN_ARG(this)
65 //-----------------------------------------------------------------------------
67 //-----------------------------------------------------------------------------
71 gtk_listbox_row_activated_callback(GtkTreeView
*treeview
,
73 GtkTreeViewColumn
*col
,
76 if (g_isIdle
) wxapp_install_idle_handler();
78 if (g_blockEventsOnDrag
) return;
79 if (g_blockEventsOnScroll
) return;
81 if (!listbox
->m_hasVMT
) return;
84 //1) This is triggered by either a double-click or a space press
85 //2) We handle both here because
86 //2a) in the case of a space/keypress we can't really know
87 // which item was pressed on because we can't get coords
89 //2b) It seems more correct
91 int sel
= gtk_tree_path_get_indices(path
)[0];
93 if(!listbox
->m_spacePressed
)
95 //Assume it was double-click
96 wxCommandEvent
event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
, listbox
->GetId() );
97 event
.SetEventObject( listbox
);
99 if(listbox
->IsSelected(sel
))
101 GtkTreeEntry
* entry
= listbox
->GtkGetEntry(sel
);
106 event
.SetString(wxConvUTF8
.cMB2WX(gtk_tree_entry_get_label(entry
)));
108 if ( listbox
->HasClientObjectData() )
109 event
.SetClientObject(
110 (wxClientData
*) gtk_tree_entry_get_userdata(entry
) );
111 else if ( listbox
->HasClientUntypedData() )
112 event
.SetClientData( gtk_tree_entry_get_userdata(entry
) );
113 g_object_unref(G_OBJECT(entry
));
117 wxLogSysError(wxT("Internal error - could not get entry for double-click"));
124 listbox
->GetEventHandler()->ProcessEvent( event
);
128 listbox
->m_spacePressed
= false; //don't block selection behaviour anymore
130 //Space was pressed - toggle the appropriate checkbox and the selection
131 #if wxUSE_CHECKLISTBOX //Do it for both native and non-native
132 if (listbox
->m_hasCheckBoxes
)
134 wxCheckListBox
*clb
= (wxCheckListBox
*)listbox
;
136 clb
->Check( sel
, !clb
->IsChecked(sel
) );
138 wxCommandEvent
new_event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, listbox
->GetId() );
139 new_event
.SetEventObject( listbox
);
140 new_event
.SetInt( sel
);
141 listbox
->GetEventHandler()->ProcessEvent( new_event
);
143 #endif // wxUSE_CHECKLISTBOX
145 if( (((listbox
->GetWindowStyleFlag() & wxLB_MULTIPLE
) != 0) ||
146 ((listbox
->GetWindowStyleFlag() & wxLB_EXTENDED
) != 0)) )
148 //toggle the selection + send event
149 listbox
->GtkSetSelection(sel
, !listbox
->IsSelected( sel
), FALSE
);
155 //-----------------------------------------------------------------------------
156 // "button_press_event"
157 //-----------------------------------------------------------------------------
161 gtk_listbox_button_press_callback( GtkWidget
*widget
,
162 GdkEventButton
*gdk_event
,
165 if (g_isIdle
) wxapp_install_idle_handler();
167 if (g_blockEventsOnDrag
) return FALSE
;
168 if (g_blockEventsOnScroll
) return FALSE
;
170 if (!listbox
->m_hasVMT
) return FALSE
;
172 //Just to be on the safe side - it should be unset in the activate callback
173 //but we don't want any obscure bugs if it doesn't get called somehow...
174 listbox
->m_spacePressed
= false;
176 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
177 if ((listbox
->m_hasCheckBoxes
) && (gdk_event
->x
< 15) && (gdk_event
->type
!= GDK_2BUTTON_PRESS
))
180 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget
),
181 (gint
)gdk_event
->x
, (gint
)gdk_event
->y
,
182 &path
, NULL
, NULL
, NULL
);
183 int sel
= gtk_tree_path_get_indices(path
)[0];
184 gtk_tree_path_free(path
);
186 wxCheckListBox
*clb
= (wxCheckListBox
*)listbox
;
188 clb
->Check( sel
, !clb
->IsChecked(sel
) );
190 wxCommandEvent
event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, listbox
->GetId() );
191 event
.SetEventObject( listbox
);
193 listbox
->GetEventHandler()->ProcessEvent( event
);
195 #endif // wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
201 //-----------------------------------------------------------------------------
203 //-----------------------------------------------------------------------------
207 gtk_listbox_key_press_callback( GtkWidget
*widget
,
208 GdkEventKey
*gdk_event
,
211 if (g_isIdle
) wxapp_install_idle_handler();
213 if (g_blockEventsOnDrag
) return FALSE
;
218 if ((gdk_event
->keyval
== GDK_Tab
) || (gdk_event
->keyval
== GDK_ISO_Left_Tab
))
220 wxNavigationKeyEvent new_event
;
221 /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */
222 new_event
.SetDirection( (gdk_event
->keyval
== GDK_Tab
) );
223 /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
224 new_event
.SetWindowChange( (gdk_event
->state
& GDK_CONTROL_MASK
) );
225 new_event
.SetCurrentFocus( listbox
);
226 ret
= listbox
->GetEventHandler()->ProcessEvent( new_event
);
229 if ((gdk_event
->keyval
== GDK_Return
) && (!ret
))
231 // eat return in all modes (RN:WHY?)
235 // Check or uncheck item with SPACE
236 if (gdk_event
->keyval
== ' ')
238 //In the keyevent we don't know the index of the item
239 //and the activated event gets called anyway...
241 //Also, activating with the space causes the treeview to
242 //unselect all the items and then select the item in question
243 //wx's behaviour is to just toggle the item's selection state
244 //and leave the others alone
245 listbox
->m_spacePressed
= true;
250 g_signal_stop_emission_by_name (widget
, "key_press_event");
258 //-----------------------------------------------------------------------------
259 // "select" and "deselect"
260 //-----------------------------------------------------------------------------
263 static gboolean
gtk_listitem_select_cb( GtkTreeSelection
* selection
,
266 gboolean is_selected
,
269 if (g_isIdle
) wxapp_install_idle_handler();
271 if (!listbox
->m_hasVMT
) return TRUE
;
272 if (g_blockEventsOnDrag
) return TRUE
;
274 if (listbox
->m_spacePressed
) return FALSE
; //see keyevent callback
275 if (listbox
->m_blockEvent
) return TRUE
;
277 // NB: wxdocs explicitly say that this event only gets sent when
278 // something is actually selected, plus the controls example
279 // assumes so and passes -1 to the dogetclientdata funcs if not
281 // OK, so basically we need to do a bit of a run-around here as
282 // 1) is_selected says whether the item(s?) are CURRENTLY selected -
283 // i.e. if is_selected is FALSE then the item is going to be
284 // selected right now!
285 // 2) However, since it is not already selected and the user
286 // will expect it to be we need to manually select it and
287 // return FALSE telling GTK we handled the selection
288 if (is_selected
) return TRUE
;
290 int nIndex
= gtk_tree_path_get_indices(path
)[0];
291 GtkTreeEntry
* entry
= listbox
->GtkGetEntry(nIndex
);
295 //Now, as mentioned above, we manually select the row that is/was going
296 //to be selected anyway by GTK
297 listbox
->m_blockEvent
= TRUE
; //if we don't block events we will lock the
298 //app due to recursion!!
300 GtkTreeSelection
* selection
=
301 gtk_tree_view_get_selection(listbox
->m_treeview
);
303 gtk_tree_model_get_iter(GTK_TREE_MODEL(listbox
->m_liststore
), &iter
, path
);
304 gtk_tree_selection_select_iter(selection
, &iter
);
306 listbox
->m_blockEvent
= FALSE
;
308 //Finally, send the wx event
309 wxCommandEvent
event(wxEVT_COMMAND_LISTBOX_SELECTED
, listbox
->GetId() );
310 event
.SetEventObject( listbox
);
312 // indicate whether this is a selection or a deselection
313 event
.SetExtraLong( 1 );
315 event
.SetInt(nIndex
);
316 event
.SetString(wxConvUTF8
.cMB2WX(gtk_tree_entry_get_label(entry
)));
318 if ( listbox
->HasClientObjectData() )
319 event
.SetClientObject(
320 (wxClientData
*) gtk_tree_entry_get_userdata(entry
)
322 else if ( listbox
->HasClientUntypedData() )
323 event
.SetClientData( gtk_tree_entry_get_userdata(entry
) );
325 listbox
->GetEventHandler()->ProcessEvent( event
);
327 g_object_unref(G_OBJECT(entry
));
328 return FALSE
; //We handled it/did it manually
331 return TRUE
; //allow selection to change
335 //-----------------------------------------------------------------------------
336 // GtkTreeEntry destruction (to destroy client data)
337 //-----------------------------------------------------------------------------
340 static void gtk_tree_entry_destroy_cb(GtkTreeEntry
* entry
,
343 if(listbox
->HasClientObjectData())
345 gpointer userdata
= gtk_tree_entry_get_userdata(entry
);
347 delete (wxClientData
*)userdata
;
352 //-----------------------------------------------------------------------------
353 // Sorting callback (standard CmpNoCase return value)
354 //-----------------------------------------------------------------------------
357 static gint
gtk_listbox_sort_callback(GtkTreeModel
*model
,
363 GtkTreeEntry
* entry2
;
365 gtk_tree_model_get(GTK_TREE_MODEL(listbox
->m_liststore
),
367 WXLISTBOX_DATACOLUMN_ARG(listbox
),
369 gtk_tree_model_get(GTK_TREE_MODEL(listbox
->m_liststore
),
371 WXLISTBOX_DATACOLUMN_ARG(listbox
),
373 wxCHECK_MSG(entry
, 0, wxT("Could not get entry"));
374 wxCHECK_MSG(entry2
, 0, wxT("Could not get entry2"));
376 //We compare collate keys here instead of calling g_utf8_collate
377 //as it is rather slow (and even the docs reccommend this)
378 int ret
= strcasecmp(gtk_tree_entry_get_collate_key(entry
),
379 gtk_tree_entry_get_collate_key(entry2
));
381 g_object_unref(G_OBJECT(entry
));
382 g_object_unref(G_OBJECT(entry2
));
388 //-----------------------------------------------------------------------------
389 // Searching callback (TRUE == not equal, FALSE == equal)
390 //-----------------------------------------------------------------------------
393 static gboolean
gtk_listbox_searchequal_callback(GtkTreeModel
* model
,
401 gtk_tree_model_get(GTK_TREE_MODEL(listbox
->m_liststore
),
403 WXLISTBOX_DATACOLUMN_ARG(listbox
),
405 wxCHECK_MSG(entry
, 0, wxT("Could not get entry"));
406 gchar
* keycollatekey
= g_utf8_collate_key(key
, -1);
408 int ret
= strcasecmp(keycollatekey
,
409 gtk_tree_entry_get_collate_key(entry
));
411 g_free(keycollatekey
);
412 g_object_unref(G_OBJECT(entry
));
418 //-----------------------------------------------------------------------------
420 //-----------------------------------------------------------------------------
422 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
)
424 // ----------------------------------------------------------------------------
426 // ----------------------------------------------------------------------------
428 wxListBox::wxListBox()
430 m_treeview
= (GtkTreeView
*) NULL
;
431 #if wxUSE_CHECKLISTBOX
432 m_hasCheckBoxes
= FALSE
;
433 #endif // wxUSE_CHECKLISTBOX
434 m_spacePressed
= false;
437 bool wxListBox::Create( wxWindow
*parent
, wxWindowID id
,
438 const wxPoint
&pos
, const wxSize
&size
,
439 const wxArrayString
& choices
,
440 long style
, const wxValidator
& validator
,
441 const wxString
&name
)
443 wxCArrayString
chs(choices
);
445 return Create( parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
446 style
, validator
, name
);
449 bool wxListBox::Create( wxWindow
*parent
, wxWindowID id
,
450 const wxPoint
&pos
, const wxSize
&size
,
451 int n
, const wxString choices
[],
452 long style
, const wxValidator
& validator
,
453 const wxString
&name
)
456 m_acceptsFocus
= TRUE
;
457 m_blockEvent
= FALSE
;
459 if (!PreCreation( parent
, pos
, size
) ||
460 !CreateBase( parent
, id
, pos
, size
, style
, validator
, name
))
462 wxFAIL_MSG( wxT("wxListBox creation failed") );
466 m_widget
= gtk_scrolled_window_new( (GtkAdjustment
*) NULL
, (GtkAdjustment
*) NULL
);
467 if (style
& wxLB_ALWAYS_SB
)
469 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget
),
470 GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
);
474 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget
),
475 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
478 m_treeview
= GTK_TREE_VIEW( gtk_tree_view_new( ) );
480 //wxListBox doesn't have a header :)
481 //NB: If enabled SetFirstItem doesn't work correctly
482 gtk_tree_view_set_headers_visible(m_treeview
, FALSE
);
484 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
486 ((wxCheckListBox
*)this)->DoCreateCheckList();
487 #endif // wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
489 // Create the data column
490 gtk_tree_view_insert_column_with_attributes(m_treeview
, -1, "",
491 gtk_cell_renderer_text_new(),
493 WXLISTBOX_DATACOLUMN
, NULL
);
495 // Now create+set the model (GtkListStore) - first argument # of columns
496 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
498 m_liststore
= gtk_list_store_new(2, G_TYPE_BOOLEAN
,
499 GTK_TYPE_TREE_ENTRY
);
502 m_liststore
= gtk_list_store_new(1, GTK_TYPE_TREE_ENTRY
);
504 gtk_tree_view_set_model(m_treeview
, GTK_TREE_MODEL(m_liststore
));
506 g_object_unref(G_OBJECT(m_liststore
)); //free on treeview destruction
508 // Disable the pop-up textctrl that enables searching - note that
509 // the docs specify that even if this disabled (which we are doing)
510 // the user can still have it through the start-interactive-search
511 // key binding...either way we want to provide a searchequal callback
512 // NB: If this is enabled a doubleclick event (activate) gets sent
513 // on a successful search
514 gtk_tree_view_set_search_column(m_treeview
, WXLISTBOX_DATACOLUMN
);
515 gtk_tree_view_set_search_equal_func(m_treeview
,
516 (GtkTreeViewSearchEqualFunc
) gtk_listbox_searchequal_callback
,
520 gtk_tree_view_set_enable_search(m_treeview
, FALSE
);
523 GtkTreeSelection
* selection
= gtk_tree_view_get_selection( m_treeview
);
524 gtk_tree_selection_set_select_function(selection
,
525 (GtkTreeSelectionFunc
)gtk_listitem_select_cb
,
526 this, NULL
); //NULL == destroycb
528 GtkSelectionMode mode
;
529 if (style
& wxLB_MULTIPLE
)
531 mode
= GTK_SELECTION_MULTIPLE
;
533 else if (style
& wxLB_EXTENDED
)
535 mode
= GTK_SELECTION_EXTENDED
;
539 // if style was 0 set single mode
540 m_windowStyle
|= wxLB_SINGLE
;
541 mode
= GTK_SELECTION_SINGLE
;
544 gtk_tree_selection_set_mode( selection
, mode
);
546 //Handle sortable stuff
547 if(style
& wxLB_SORT
)
549 //Setup sorting in ascending (wx) order
550 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(m_liststore
),
551 WXLISTBOX_DATACOLUMN
,
554 //Set the sort callback
555 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(m_liststore
),
556 WXLISTBOX_DATACOLUMN
,
557 (GtkTreeIterCompareFunc
) gtk_listbox_sort_callback
,
559 NULL
//"destroy notifier"
564 gtk_container_add (GTK_CONTAINER (m_widget
), GTK_WIDGET(m_treeview
) );
566 gtk_widget_show( GTK_WIDGET(m_treeview
) );
568 wxListBox::DoInsertItems(wxArrayString(n
, choices
), 0); // insert initial items
570 //treeview-specific events
571 g_signal_connect(m_treeview
, "row-activated",
572 G_CALLBACK(gtk_listbox_row_activated_callback
), this);
575 g_signal_connect (m_treeview
, "button_press_event",
576 G_CALLBACK (gtk_listbox_button_press_callback
),
578 g_signal_connect (m_treeview
, "key_press_event",
579 G_CALLBACK (gtk_listbox_key_press_callback
),
582 m_parent
->DoAddChild( this );
585 SetBestSize(size
); // need this too because this is a wxControlWithItems
590 wxListBox::~wxListBox()
597 // ----------------------------------------------------------------------------
599 // ----------------------------------------------------------------------------
601 void wxListBox::GtkInsertItems(const wxArrayString
& items
,
602 void** clientData
, int pos
)
604 wxCHECK_RET( m_treeview
!= NULL
, wxT("invalid listbox") );
606 InvalidateBestSize();
608 // Create and set column ids and GValues
610 size_t nNum
= items
.GetCount();
611 int nCurCount
= wxListBox::GetCount();
612 wxASSERT_MSG(pos
<= nCurCount
, wxT("Invalid index passed to wxListBox"));
614 GtkTreeIter
* pIter
= NULL
; // append by default
616 if (pos
!= nCurCount
)
618 gboolean res
= gtk_tree_model_iter_nth_child(
619 GTK_TREE_MODEL(m_liststore
),
620 &iter
, NULL
, //NULL = parent = get first
624 wxLogSysError(wxT("internal wxListBox error in insertion"));
631 for (size_t i
= 0; i
< nNum
; ++i
)
633 wxString label
= items
[i
];
635 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
638 label
.Prepend(wxCHECKLBOX_STRING
);
640 #endif // wxUSE_CHECKLISTBOX
643 GtkTreeEntry
* entry
= gtk_tree_entry_new();
644 gtk_tree_entry_set_label(entry
, wxConvUTF8
.cWX2MB(label
));
645 gtk_tree_entry_set_destroy_func(entry
,
646 (GtkTreeEntryDestroy
)gtk_tree_entry_destroy_cb
,
650 gtk_tree_entry_set_userdata(entry
, clientData
[i
]);
653 gtk_list_store_insert_before(m_liststore
, &itercur
, pIter
);
655 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
658 gtk_list_store_set(m_liststore
, &itercur
,
659 0, FALSE
, //FALSE == not toggled
664 gtk_list_store_set(m_liststore
, &itercur
,
667 g_object_unref(G_OBJECT(entry
)); //liststore always refs :)
671 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
)
673 GtkInsertItems(items
, NULL
, pos
);
676 int wxListBox::DoAppend( const wxString
& item
)
678 // Call DoInsertItems
679 int nWhere
= wxListBox::GetCount();
680 wxArrayString aItems
;
682 wxListBox::DoInsertItems(aItems
, nWhere
);
686 void wxListBox::DoSetItems( const wxArrayString
& items
,
690 GtkInsertItems(items
, clientData
, 0);
693 // ----------------------------------------------------------------------------
695 // ----------------------------------------------------------------------------
697 void wxListBox::Clear()
699 wxCHECK_RET( m_treeview
!= NULL
, wxT("invalid listbox") );
701 InvalidateBestSize();
703 gtk_list_store_clear( m_liststore
); /* well, THAT was easy :) */
706 void wxListBox::Delete( int n
)
708 wxCHECK_RET( m_treeview
!= NULL
, wxT("invalid listbox") );
710 InvalidateBestSize();
713 gboolean res
= gtk_tree_model_iter_nth_child(
714 GTK_TREE_MODEL(m_liststore
),
715 &iter
, NULL
, //NULL = parent = get first
719 wxCHECK_RET( res
, wxT("wrong listbox index") );
721 //this returns false if iter is invalid (i.e. deleting item
722 //at end) but since we don't use iter, well... :)
723 gtk_list_store_remove(m_liststore
, &iter
);
726 // ----------------------------------------------------------------------------
727 // get GtkTreeEntry from position (note: you need to g_unref it if valid)
728 // ----------------------------------------------------------------------------
730 struct _GtkTreeEntry
* wxListBox::GtkGetEntry(int n
) const
733 gboolean res
= gtk_tree_model_iter_nth_child(
734 GTK_TREE_MODEL(m_liststore
),
735 &iter
, NULL
, //NULL = parent = get first
740 wxLogDebug(wxT("gtk_tree_model_iter_nth_child failed\n")
741 wxT("Passed in value was:[%i] List size:[%i]"),
742 n
, wxListBox::GetCount() );
747 GtkTreeEntry
* entry
= NULL
;
748 gtk_tree_model_get(GTK_TREE_MODEL(m_liststore
), &iter
,
749 WXLISTBOX_DATACOLUMN
, &entry
, -1);
754 // ----------------------------------------------------------------------------
756 // ----------------------------------------------------------------------------
758 void* wxListBox::DoGetItemClientData( int n
) const
760 wxCHECK_MSG( n
>= 0 && n
< wxListBox::GetCount(), NULL
,
761 wxT("Invalid index passed to GetItemClientData") );
763 GtkTreeEntry
* entry
= GtkGetEntry(n
);
764 wxCHECK_MSG(entry
, NULL
, wxT("could not get entry"));
766 void* userdata
= gtk_tree_entry_get_userdata( entry
);
767 g_object_unref(G_OBJECT(entry
));
771 wxClientData
* wxListBox::DoGetItemClientObject( int n
) const
773 return (wxClientData
*) wxListBox::DoGetItemClientData(n
);
776 void wxListBox::DoSetItemClientData( int n
, void* clientData
)
778 wxCHECK_RET( n
>= 0 && n
< wxListBox::GetCount(),
779 wxT("Invalid index passed to SetItemClientData") );
781 GtkTreeEntry
* entry
= GtkGetEntry(n
);
782 wxCHECK_RET(entry
, wxT("could not get entry"));
784 gtk_tree_entry_set_userdata( entry
, clientData
);
785 g_object_unref(G_OBJECT(entry
));
788 void wxListBox::DoSetItemClientObject( int n
, wxClientData
* clientData
)
790 // wxItemContainer already deletes data for us
791 wxListBox::DoSetItemClientData(n
, (void*) clientData
);
794 // ----------------------------------------------------------------------------
795 // string list access
796 // ----------------------------------------------------------------------------
798 void wxListBox::SetString( int n
, const wxString
&string
)
800 wxCHECK_RET( m_treeview
!= NULL
, wxT("invalid listbox") );
802 GtkTreeEntry
* entry
= GtkGetEntry(n
);
803 wxCHECK_RET( entry
, wxT("wrong listbox index") );
805 wxString label
= string
;
807 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
809 label
.Prepend(wxCHECKLBOX_STRING
);
810 #endif // wxUSE_CHECKLISTBOX
812 // RN: This may look wierd but the problem is that the TreeView
813 // doesn't resort or update when changed above and there is no real
814 // notification function...
815 void* userdata
= gtk_tree_entry_get_userdata(entry
);
816 gtk_tree_entry_set_userdata(entry
, NULL
); //don't delete on destroy
817 g_object_unref(G_OBJECT(entry
));
819 bool bWasSelected
= wxListBox::IsSelected(n
);
820 wxListBox::Delete(n
);
822 wxArrayString aItems
;
824 GtkInsertItems(aItems
, &userdata
, n
);
826 wxListBox::GtkSetSelection(n
, TRUE
, TRUE
);
829 wxString
wxListBox::GetString( int n
) const
831 wxCHECK_MSG( m_treeview
!= NULL
, wxEmptyString
, wxT("invalid listbox") );
833 GtkTreeEntry
* entry
= GtkGetEntry(n
);
834 wxCHECK_MSG( entry
, wxEmptyString
, wxT("wrong listbox index") );
836 wxString label
= wxGTK_CONV_BACK( gtk_tree_entry_get_label(entry
) );
838 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
839 // checklistboxes have "[±] " prepended to their lables, remove it
841 // NB: 4 below is the length of wxCHECKLBOX_STRING from wx/gtk/checklst.h
842 if ( m_hasCheckBoxes
)
844 #endif // wxUSE_CHECKLISTBOX
846 g_object_unref(G_OBJECT(entry
));
850 int wxListBox::GetCount() const
852 wxCHECK_MSG( m_treeview
!= NULL
, -1, wxT("invalid listbox") );
854 return gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_liststore
), NULL
);
857 int wxListBox::FindString( const wxString
&item
, bool bCase
) const
859 wxCHECK_MSG( m_treeview
!= NULL
, wxNOT_FOUND
, wxT("invalid listbox") );
861 //Sort of hackish - maybe there is a faster way
862 int nCount
= wxListBox::GetCount();
864 for(int i
= 0; i
< nCount
; ++i
)
866 if( item
.IsSameAs( wxListBox::GetString(i
), bCase
) )
871 // it's not an error if the string is not found -> no wxCHECK
875 // ----------------------------------------------------------------------------
877 // ----------------------------------------------------------------------------
879 int wxListBox::GetSelection() const
881 wxCHECK_MSG( m_treeview
!= NULL
, -1, wxT("invalid listbox"));
882 wxCHECK_MSG( HasFlag(wxLB_SINGLE
), -1,
883 wxT("must be single selection listbox"));
886 GtkTreeSelection
* selection
= gtk_tree_view_get_selection(m_treeview
);
888 // only works on single-sel
889 if (!gtk_tree_selection_get_selected(selection
, NULL
, &iter
))
893 gtk_tree_model_get_path(GTK_TREE_MODEL(m_liststore
), &iter
);
895 int sel
= gtk_tree_path_get_indices(path
)[0];
897 gtk_tree_path_free(path
);
902 int wxListBox::GetSelections( wxArrayInt
& aSelections
) const
904 wxCHECK_MSG( m_treeview
!= NULL
, -1, wxT("invalid listbox") );
910 GtkTreeSelection
* selection
= gtk_tree_view_get_selection(m_treeview
);
912 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(m_liststore
), &iter
))
913 { //gtk_tree_selection_get_selected_rows is GTK 2.2+ so iter instead
916 if (gtk_tree_selection_iter_is_selected(selection
, &iter
))
920 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(m_liststore
), &iter
));
923 return aSelections
.GetCount();
926 bool wxListBox::IsSelected( int n
) const
928 wxCHECK_MSG( m_treeview
!= NULL
, FALSE
, wxT("invalid listbox") );
930 GtkTreeSelection
* selection
= gtk_tree_view_get_selection(m_treeview
);
933 gboolean res
= gtk_tree_model_iter_nth_child(
934 GTK_TREE_MODEL(m_liststore
),
935 &iter
, NULL
, //NULL = parent = get first
938 wxCHECK_MSG( res
, FALSE
, wxT("Invalid index") );
940 return gtk_tree_selection_iter_is_selected(selection
, &iter
);
943 void wxListBox::DoSetSelection( int n
, bool select
)
945 return GtkSetSelection(n
, select
, TRUE
); //docs say no events here
948 void wxListBox::GtkSetSelection(int n
, const bool select
, const bool blockEvent
)
950 wxCHECK_RET( m_treeview
!= NULL
, wxT("invalid listbox") );
952 GtkTreeSelection
* selection
= gtk_tree_view_get_selection(m_treeview
);
955 gboolean res
= gtk_tree_model_iter_nth_child(
956 GTK_TREE_MODEL(m_liststore
),
957 &iter
, NULL
, //NULL = parent = get first
960 wxCHECK_RET( res
, wxT("Invalid index") );
962 m_blockEvent
= blockEvent
;
965 gtk_tree_selection_select_iter(selection
, &iter
);
967 gtk_tree_selection_unselect_iter(selection
, &iter
);
969 m_blockEvent
= FALSE
;
972 void wxListBox::DoSetFirstItem( int n
)
974 wxCHECK_RET( m_treeview
, wxT("invalid listbox") );
975 wxCHECK_RET( n
>= 0 && n
< wxListBox::GetCount(), wxT("invalid index"));
977 //RN: I have no idea why this line is needed...
978 if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (m_treeview
))
981 // terribly efficient (RN:???)
982 // RN: Note that evidently the vadjustment property "vadjustment" from
983 // GtkTreeView is different from the "gtk-vadjustment"...
984 // (i.e. gtk_tree_view_get_vadjustment)
985 const gchar
*vadjustment_key
= "gtk-vadjustment";
986 guint vadjustment_key_id
= g_quark_from_static_string (vadjustment_key
);
988 GtkAdjustment
*adjustment
=
989 (GtkAdjustment
*) g_object_get_qdata(G_OBJECT (m_treeview
), vadjustment_key_id
);
990 wxCHECK_RET( adjustment
, wxT("invalid listbox code") );
992 // Get the greater of the item heights from each column
993 gint cellheight
= 0, cellheightcur
;
994 GList
* columnlist
= gtk_tree_view_get_columns(m_treeview
);
995 GList
* curlist
= columnlist
;
999 gtk_tree_view_column_cell_get_size(
1000 GTK_TREE_VIEW_COLUMN(curlist
->data
),
1001 NULL
, NULL
, NULL
, NULL
,
1004 cellheight
= cellheightcur
> cellheight
?
1005 cellheightcur
: cellheight
;
1007 curlist
= curlist
->next
;
1010 g_list_free(columnlist
);
1012 float y
= (float) (cellheight
* n
);
1013 if (y
> adjustment
->upper
- adjustment
->page_size
)
1014 y
= adjustment
->upper
- adjustment
->page_size
;
1015 gtk_adjustment_set_value( adjustment
, y
);
1019 // ----------------------------------------------------------------------------
1021 // ----------------------------------------------------------------------------
1024 void wxListBox::ApplyToolTip( GtkTooltips
*tips
, const wxChar
*tip
)
1026 // RN: Is this needed anymore?
1027 gtk_tooltips_set_tip( tips
, GTK_WIDGET( m_treeview
), wxGTK_CONV(tip
), (gchar
*) NULL
);
1029 #endif // wxUSE_TOOLTIPS
1031 GtkWidget
*wxListBox::GetConnectWidget()
1033 // return GTK_WIDGET(m_treeview);
1037 bool wxListBox::IsOwnGtkWindow( GdkWindow
*window
)
1039 return (window
== gtk_tree_view_get_bin_window(m_treeview
));
1042 void wxListBox::DoApplyWidgetStyle(GtkRcStyle
*style
)
1044 if (m_hasBgCol
&& m_backgroundColour
.Ok())
1046 GdkWindow
*window
= gtk_tree_view_get_bin_window(m_treeview
);
1049 m_backgroundColour
.CalcPixel( gdk_window_get_colormap( window
) );
1050 gdk_window_set_background( window
, m_backgroundColour
.GetColor() );
1051 gdk_window_clear( window
);
1055 gtk_widget_modify_style( GTK_WIDGET(m_treeview
), style
);
1058 void wxListBox::OnInternalIdle()
1060 //RN: Is this needed anymore?
1061 wxCursor cursor
= m_cursor
;
1062 if (g_globalCursor
.Ok()) cursor
= g_globalCursor
;
1064 if (GTK_WIDGET(m_treeview
)->window
&& cursor
.Ok())
1066 /* I now set the cursor the anew in every OnInternalIdle call
1067 as setting the cursor in a parent window also effects the
1068 windows above so that checking for the current cursor is
1070 GdkWindow
*window
= gtk_tree_view_get_bin_window(m_treeview
);
1071 gdk_window_set_cursor( window
, cursor
.GetCursor() );
1074 if (wxUpdateUIEvent::CanUpdate(this))
1075 UpdateWindowUI(wxUPDATE_UI_FROMIDLE
);
1078 wxSize
wxListBox::DoGetBestSize() const
1080 wxCHECK_MSG(m_treeview
, wxDefaultSize
, wxT("invalid tree view"));
1082 // Start with a minimum size that's not too small
1084 GetTextExtent( wxT("X"), &cx
, &cy
);
1085 int lbWidth
= 3 * cx
;
1088 // Get the visible area of the tree view (limit to the 10th item
1089 // so that it isn't too big)
1090 int count
= GetCount();
1095 // Find the widest line
1096 for(int i
= 0; i
< count
; i
++) {
1097 wxString
str(GetString(i
));
1098 GetTextExtent(str
, &wLine
, NULL
);
1099 lbWidth
= wxMax(lbWidth
, wLine
);
1104 // And just a bit more for the checkbox if present and then some
1105 // (these are rough guesses)
1106 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
1107 if ( m_hasCheckBoxes
)
1110 cy
= cy
> 25 ? cy
: 25; // rough height of checkbox
1114 // don't make the listbox too tall (limit height to around 10 items) but don't
1115 // make it too small neither
1116 lbHeight
= (cy
+4) * wxMin(wxMax(count
, 3), 10);
1119 // Add room for the scrollbar
1120 lbWidth
+= wxSystemSettings::GetMetric(wxSYS_VSCROLL_X
);
1122 wxSize
best(lbWidth
, lbHeight
);
1123 CacheBestSize(best
);
1129 wxListBox::GetClassDefaultAttributes(wxWindowVariant
WXUNUSED(variant
))
1131 return GetDefaultAttributesFromGTKWidget(gtk_tree_view_new
, true);
1134 #endif // wxUSE_LISTBOX