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"
16 #include "wx/listbox.h"
19 #include "wx/dynarray.h"
23 #include "wx/settings.h"
24 #include "wx/checklst.h"
25 #include "wx/arrstr.h"
28 #include "wx/gtk/private.h"
29 #include "wx/gtk/treeentry_gtk.h"
32 #include "wx/tooltip.h"
37 #include <gdk/gdkkeysyms.h>
39 //-----------------------------------------------------------------------------
41 //-----------------------------------------------------------------------------
43 extern bool g_blockEventsOnDrag
;
44 extern bool g_blockEventsOnScroll
;
45 extern wxCursor g_globalCursor
;
49 //-----------------------------------------------------------------------------
50 // Macro to tell which row the strings are in (1 if native checklist, 0 if not)
51 //-----------------------------------------------------------------------------
53 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
54 # define WXLISTBOX_DATACOLUMN_ARG(x) (x->m_hasCheckBoxes ? 1 : 0)
56 # define WXLISTBOX_DATACOLUMN_ARG(x) (0)
57 #endif // wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
59 #define WXLISTBOX_DATACOLUMN WXLISTBOX_DATACOLUMN_ARG(this)
61 //-----------------------------------------------------------------------------
63 //-----------------------------------------------------------------------------
67 gtk_listbox_row_activated_callback(GtkTreeView
*treeview
,
69 GtkTreeViewColumn
*col
,
72 if (g_isIdle
) wxapp_install_idle_handler();
74 if (g_blockEventsOnDrag
) return;
75 if (g_blockEventsOnScroll
) return;
77 if (!listbox
->m_hasVMT
) return;
80 //1) This is triggered by either a double-click or a space press
81 //2) We handle both here because
82 //2a) in the case of a space/keypress we can't really know
83 // which item was pressed on because we can't get coords
85 //2b) It seems more correct
87 int sel
= gtk_tree_path_get_indices(path
)[0];
89 if(!listbox
->m_spacePressed
)
91 //Assume it was double-click
92 wxCommandEvent
event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
, listbox
->GetId() );
93 event
.SetEventObject( listbox
);
95 if(listbox
->IsSelected(sel
))
97 GtkTreeEntry
* entry
= listbox
->GtkGetEntry(sel
);
102 event
.SetString(wxConvUTF8
.cMB2WX(gtk_tree_entry_get_label(entry
)));
104 if ( listbox
->HasClientObjectData() )
105 event
.SetClientObject(
106 (wxClientData
*) gtk_tree_entry_get_userdata(entry
) );
107 else if ( listbox
->HasClientUntypedData() )
108 event
.SetClientData( gtk_tree_entry_get_userdata(entry
) );
109 g_object_unref (entry
);
113 wxLogSysError(wxT("Internal error - could not get entry for double-click"));
120 listbox
->GetEventHandler()->ProcessEvent( event
);
124 listbox
->m_spacePressed
= false; //don't block selection behaviour anymore
126 //Space was pressed - toggle the appropriate checkbox and the selection
127 #if wxUSE_CHECKLISTBOX //Do it for both native and non-native
128 if (listbox
->m_hasCheckBoxes
)
130 wxCheckListBox
*clb
= (wxCheckListBox
*)listbox
;
132 clb
->Check( sel
, !clb
->IsChecked(sel
) );
134 wxCommandEvent
new_event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, listbox
->GetId() );
135 new_event
.SetEventObject( listbox
);
136 new_event
.SetInt( sel
);
137 listbox
->GetEventHandler()->ProcessEvent( new_event
);
139 #endif // wxUSE_CHECKLISTBOX
141 if( (((listbox
->GetWindowStyleFlag() & wxLB_MULTIPLE
) != 0) ||
142 ((listbox
->GetWindowStyleFlag() & wxLB_EXTENDED
) != 0)) )
144 //toggle the selection + send event
145 listbox
->GtkSetSelection(sel
, !listbox
->IsSelected( sel
), false);
151 //-----------------------------------------------------------------------------
152 // "button_press_event"
153 //-----------------------------------------------------------------------------
157 gtk_listbox_button_press_callback( GtkWidget
*widget
,
158 GdkEventButton
*gdk_event
,
161 if (g_isIdle
) wxapp_install_idle_handler();
163 if (g_blockEventsOnDrag
) return FALSE
;
164 if (g_blockEventsOnScroll
) return FALSE
;
166 if (!listbox
->m_hasVMT
) return FALSE
;
168 //Just to be on the safe side - it should be unset in the activate callback
169 //but we don't want any obscure bugs if it doesn't get called somehow...
170 listbox
->m_spacePressed
= false;
172 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
173 if ((listbox
->m_hasCheckBoxes
) && (gdk_event
->x
< 15) && (gdk_event
->type
!= GDK_2BUTTON_PRESS
))
176 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget
),
177 (gint
)gdk_event
->x
, (gint
)gdk_event
->y
,
178 &path
, NULL
, NULL
, NULL
);
179 int sel
= gtk_tree_path_get_indices(path
)[0];
180 gtk_tree_path_free(path
);
182 wxCheckListBox
*clb
= (wxCheckListBox
*)listbox
;
184 clb
->Check( sel
, !clb
->IsChecked(sel
) );
186 wxCommandEvent
event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, listbox
->GetId() );
187 event
.SetEventObject( listbox
);
189 listbox
->GetEventHandler()->ProcessEvent( event
);
191 #endif // wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
197 //-----------------------------------------------------------------------------
199 //-----------------------------------------------------------------------------
203 gtk_listbox_key_press_callback( GtkWidget
*widget
,
204 GdkEventKey
*gdk_event
,
207 if (g_isIdle
) wxapp_install_idle_handler();
209 if (g_blockEventsOnDrag
) return FALSE
;
214 if ((gdk_event
->keyval
== GDK_Tab
) || (gdk_event
->keyval
== GDK_ISO_Left_Tab
))
216 wxNavigationKeyEvent new_event
;
217 /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */
218 new_event
.SetDirection( (gdk_event
->keyval
== GDK_Tab
) );
219 /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
220 new_event
.SetWindowChange( (gdk_event
->state
& GDK_CONTROL_MASK
) );
221 new_event
.SetCurrentFocus( listbox
);
222 ret
= listbox
->GetEventHandler()->ProcessEvent( new_event
);
225 if ((gdk_event
->keyval
== GDK_Return
) && (!ret
))
227 // eat return in all modes (RN:WHY?)
231 // Check or uncheck item with SPACE
232 if (gdk_event
->keyval
== ' ')
234 //In the keyevent we don't know the index of the item
235 //and the activated event gets called anyway...
237 //Also, activating with the space causes the treeview to
238 //unselect all the items and then select the item in question
239 //wx's behaviour is to just toggle the item's selection state
240 //and leave the others alone
241 listbox
->m_spacePressed
= true;
246 g_signal_stop_emission_by_name (widget
, "key_press_event");
254 //-----------------------------------------------------------------------------
255 // "select" and "deselect"
256 //-----------------------------------------------------------------------------
259 static gboolean
gtk_listitem_select_cb( GtkTreeSelection
* selection
,
262 gboolean is_selected
,
265 if (g_isIdle
) wxapp_install_idle_handler();
267 if (!listbox
->m_hasVMT
) return TRUE
;
268 if (g_blockEventsOnDrag
) return TRUE
;
270 if (listbox
->m_spacePressed
) return FALSE
; //see keyevent callback
271 if (listbox
->m_blockEvent
) return TRUE
;
273 // NB: wxdocs explicitly say that this event only gets sent when
274 // something is actually selected, plus the controls example
275 // assumes so and passes -1 to the dogetclientdata funcs if not
277 // OK, so basically we need to do a bit of a run-around here as
278 // 1) is_selected says whether the item(s?) are CURRENTLY selected -
279 // i.e. if is_selected is FALSE then the item is going to be
280 // selected right now!
281 // 2) However, since it is not already selected and the user
282 // will expect it to be we need to manually select it and
283 // return FALSE telling GTK we handled the selection
284 if (is_selected
) return TRUE
;
286 int nIndex
= gtk_tree_path_get_indices(path
)[0];
287 GtkTreeEntry
* entry
= listbox
->GtkGetEntry(nIndex
);
291 //Now, as mentioned above, we manually select the row that is/was going
292 //to be selected anyway by GTK
293 listbox
->m_blockEvent
= true; //if we don't block events we will lock the
294 //app due to recursion!!
296 GtkTreeSelection
* selection
=
297 gtk_tree_view_get_selection(listbox
->m_treeview
);
299 gtk_tree_model_get_iter(GTK_TREE_MODEL(listbox
->m_liststore
), &iter
, path
);
300 gtk_tree_selection_select_iter(selection
, &iter
);
302 listbox
->m_blockEvent
= false;
304 //Finally, send the wx event
305 wxCommandEvent
event(wxEVT_COMMAND_LISTBOX_SELECTED
, listbox
->GetId() );
306 event
.SetEventObject( listbox
);
308 // indicate whether this is a selection or a deselection
309 event
.SetExtraLong( 1 );
311 event
.SetInt(nIndex
);
312 event
.SetString(wxConvUTF8
.cMB2WX(gtk_tree_entry_get_label(entry
)));
314 if ( listbox
->HasClientObjectData() )
315 event
.SetClientObject(
316 (wxClientData
*) gtk_tree_entry_get_userdata(entry
)
318 else if ( listbox
->HasClientUntypedData() )
319 event
.SetClientData( gtk_tree_entry_get_userdata(entry
) );
321 listbox
->GetEventHandler()->ProcessEvent( event
);
323 g_object_unref (entry
);
324 return FALSE
; //We handled it/did it manually
327 return TRUE
; //allow selection to change
331 //-----------------------------------------------------------------------------
332 // GtkTreeEntry destruction (to destroy client data)
333 //-----------------------------------------------------------------------------
336 static void gtk_tree_entry_destroy_cb(GtkTreeEntry
* entry
,
339 if(listbox
->HasClientObjectData())
341 gpointer userdata
= gtk_tree_entry_get_userdata(entry
);
343 delete (wxClientData
*)userdata
;
348 //-----------------------------------------------------------------------------
349 // Sorting callback (standard CmpNoCase return value)
350 //-----------------------------------------------------------------------------
353 static gint
gtk_listbox_sort_callback(GtkTreeModel
*model
,
359 GtkTreeEntry
* entry2
;
361 gtk_tree_model_get(GTK_TREE_MODEL(listbox
->m_liststore
),
363 WXLISTBOX_DATACOLUMN_ARG(listbox
),
365 gtk_tree_model_get(GTK_TREE_MODEL(listbox
->m_liststore
),
367 WXLISTBOX_DATACOLUMN_ARG(listbox
),
369 wxCHECK_MSG(entry
, 0, wxT("Could not get entry"));
370 wxCHECK_MSG(entry2
, 0, wxT("Could not get entry2"));
372 //We compare collate keys here instead of calling g_utf8_collate
373 //as it is rather slow (and even the docs reccommend this)
374 int ret
= strcasecmp(gtk_tree_entry_get_collate_key(entry
),
375 gtk_tree_entry_get_collate_key(entry2
));
377 g_object_unref (entry
);
378 g_object_unref (entry2
);
384 //-----------------------------------------------------------------------------
385 // Searching callback (TRUE == not equal, FALSE == equal)
386 //-----------------------------------------------------------------------------
389 static gboolean
gtk_listbox_searchequal_callback(GtkTreeModel
* model
,
397 gtk_tree_model_get(GTK_TREE_MODEL(listbox
->m_liststore
),
399 WXLISTBOX_DATACOLUMN_ARG(listbox
),
401 wxCHECK_MSG(entry
, 0, wxT("Could not get entry"));
402 gchar
* keycollatekey
= g_utf8_collate_key(key
, -1);
404 int ret
= strcasecmp(keycollatekey
,
405 gtk_tree_entry_get_collate_key(entry
));
407 g_free(keycollatekey
);
408 g_object_unref (entry
);
414 //-----------------------------------------------------------------------------
416 //-----------------------------------------------------------------------------
418 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
)
420 // ----------------------------------------------------------------------------
422 // ----------------------------------------------------------------------------
424 void wxListBox::Init()
426 m_treeview
= (GtkTreeView
*) NULL
;
427 #if wxUSE_CHECKLISTBOX
428 m_hasCheckBoxes
= false;
429 #endif // wxUSE_CHECKLISTBOX
430 m_spacePressed
= false;
433 bool wxListBox::Create( wxWindow
*parent
, wxWindowID id
,
434 const wxPoint
&pos
, const wxSize
&size
,
435 const wxArrayString
& choices
,
436 long style
, const wxValidator
& validator
,
437 const wxString
&name
)
439 wxCArrayString
chs(choices
);
441 return Create( parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
442 style
, validator
, name
);
445 bool wxListBox::Create( wxWindow
*parent
, wxWindowID id
,
446 const wxPoint
&pos
, const wxSize
&size
,
447 int n
, const wxString choices
[],
448 long style
, const wxValidator
& validator
,
449 const wxString
&name
)
452 m_acceptsFocus
= true;
453 m_blockEvent
= false;
455 if (!PreCreation( parent
, pos
, size
) ||
456 !CreateBase( parent
, id
, pos
, size
, style
, validator
, name
))
458 wxFAIL_MSG( wxT("wxListBox creation failed") );
462 m_widget
= gtk_scrolled_window_new( (GtkAdjustment
*) NULL
, (GtkAdjustment
*) NULL
);
463 if (style
& wxLB_ALWAYS_SB
)
465 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget
),
466 GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
);
470 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget
),
471 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
475 GtkScrolledWindowSetBorder(m_widget
, style
);
477 m_treeview
= GTK_TREE_VIEW( gtk_tree_view_new( ) );
479 //wxListBox doesn't have a header :)
480 //NB: If enabled SetFirstItem doesn't work correctly
481 gtk_tree_view_set_headers_visible(m_treeview
, FALSE
);
483 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
485 ((wxCheckListBox
*)this)->DoCreateCheckList();
486 #endif // wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
488 // Create the data column
489 gtk_tree_view_insert_column_with_attributes(m_treeview
, -1, "",
490 gtk_cell_renderer_text_new(),
492 WXLISTBOX_DATACOLUMN
, NULL
);
494 // Now create+set the model (GtkListStore) - first argument # of columns
495 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
497 m_liststore
= gtk_list_store_new(2, G_TYPE_BOOLEAN
,
498 GTK_TYPE_TREE_ENTRY
);
501 m_liststore
= gtk_list_store_new(1, GTK_TYPE_TREE_ENTRY
);
503 gtk_tree_view_set_model(m_treeview
, GTK_TREE_MODEL(m_liststore
));
505 g_object_unref (m_liststore
); //free on treeview destruction
507 // Disable the pop-up textctrl that enables searching - note that
508 // the docs specify that even if this disabled (which we are doing)
509 // the user can still have it through the start-interactive-search
510 // key binding...either way we want to provide a searchequal callback
511 // NB: If this is enabled a doubleclick event (activate) gets sent
512 // on a successful search
513 gtk_tree_view_set_search_column(m_treeview
, WXLISTBOX_DATACOLUMN
);
514 gtk_tree_view_set_search_equal_func(m_treeview
,
515 (GtkTreeViewSearchEqualFunc
) gtk_listbox_searchequal_callback
,
519 gtk_tree_view_set_enable_search(m_treeview
, FALSE
);
522 GtkTreeSelection
* selection
= gtk_tree_view_get_selection( m_treeview
);
523 gtk_tree_selection_set_select_function(selection
,
524 (GtkTreeSelectionFunc
)gtk_listitem_select_cb
,
525 this, NULL
); //NULL == destroycb
527 GtkSelectionMode mode
;
528 if (style
& wxLB_MULTIPLE
)
530 mode
= GTK_SELECTION_MULTIPLE
;
532 else if (style
& wxLB_EXTENDED
)
534 mode
= GTK_SELECTION_EXTENDED
;
538 // if style was 0 set single mode
539 m_windowStyle
|= wxLB_SINGLE
;
540 mode
= GTK_SELECTION_SINGLE
;
543 gtk_tree_selection_set_mode( selection
, mode
);
545 //Handle sortable stuff
546 if(style
& wxLB_SORT
)
548 //Setup sorting in ascending (wx) order
549 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(m_liststore
),
550 WXLISTBOX_DATACOLUMN
,
553 //Set the sort callback
554 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(m_liststore
),
555 WXLISTBOX_DATACOLUMN
,
556 (GtkTreeIterCompareFunc
) gtk_listbox_sort_callback
,
558 NULL
//"destroy notifier"
563 gtk_container_add (GTK_CONTAINER (m_widget
), GTK_WIDGET(m_treeview
) );
565 gtk_widget_show( GTK_WIDGET(m_treeview
) );
567 wxListBox::DoInsertItems(wxArrayString(n
, choices
), 0); // insert initial items
569 //treeview-specific events
570 g_signal_connect(m_treeview
, "row-activated",
571 G_CALLBACK(gtk_listbox_row_activated_callback
), this);
574 g_signal_connect (m_treeview
, "button_press_event",
575 G_CALLBACK (gtk_listbox_button_press_callback
),
577 g_signal_connect (m_treeview
, "key_press_event",
578 G_CALLBACK (gtk_listbox_key_press_callback
),
581 m_parent
->DoAddChild( this );
584 SetBestSize(size
); // need this too because this is a wxControlWithItems
589 wxListBox::~wxListBox()
596 // ----------------------------------------------------------------------------
598 // ----------------------------------------------------------------------------
600 void wxListBox::GtkInsertItems(const wxArrayString
& items
,
601 void** clientData
, unsigned int pos
)
603 wxCHECK_RET( m_treeview
!= NULL
, wxT("invalid listbox") );
605 InvalidateBestSize();
607 // Create and set column ids and GValues
609 unsigned int nNum
= items
.GetCount();
610 unsigned int nCurCount
= wxListBox::GetCount();
611 wxASSERT_MSG(pos
<= nCurCount
, wxT("Invalid index passed to wxListBox"));
613 GtkTreeIter
* pIter
= NULL
; // append by default
615 if (pos
!= nCurCount
)
617 gboolean res
= gtk_tree_model_iter_nth_child(
618 GTK_TREE_MODEL(m_liststore
),
619 &iter
, NULL
, //NULL = parent = get first
623 wxLogSysError(wxT("internal wxListBox error in insertion"));
630 for (unsigned int i
= 0; i
< nNum
; ++i
)
632 wxString label
= items
[i
];
634 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
637 label
.Prepend(wxCHECKLBOX_STRING
);
639 #endif // wxUSE_CHECKLISTBOX
642 GtkTreeEntry
* entry
= gtk_tree_entry_new();
643 gtk_tree_entry_set_label(entry
, wxConvUTF8
.cWX2MB(label
));
644 gtk_tree_entry_set_destroy_func(entry
,
645 (GtkTreeEntryDestroy
)gtk_tree_entry_destroy_cb
,
649 gtk_tree_entry_set_userdata(entry
, clientData
[i
]);
652 gtk_list_store_insert_before(m_liststore
, &itercur
, pIter
);
654 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
657 gtk_list_store_set(m_liststore
, &itercur
,
658 0, FALSE
, //FALSE == not toggled
663 gtk_list_store_set(m_liststore
, &itercur
,
666 g_object_unref (entry
); //liststore always refs :)
670 void wxListBox::DoInsertItems(const wxArrayString
& items
, unsigned int pos
)
672 wxCHECK_RET( IsValidInsert(pos
), wxT("invalid index in wxListBox::InsertItems") );
674 GtkInsertItems(items
, NULL
, pos
);
677 int wxListBox::DoAppend( const wxString
& item
)
679 // Call DoInsertItems
680 unsigned int nWhere
= wxListBox::GetCount();
681 wxArrayString aItems
;
683 wxListBox::DoInsertItems(aItems
, nWhere
);
687 void wxListBox::DoSetItems( const wxArrayString
& items
,
691 GtkInsertItems(items
, clientData
, 0);
694 // ----------------------------------------------------------------------------
696 // ----------------------------------------------------------------------------
698 void wxListBox::Clear()
700 wxCHECK_RET( m_treeview
!= NULL
, wxT("invalid listbox") );
702 InvalidateBestSize();
704 gtk_list_store_clear( m_liststore
); /* well, THAT was easy :) */
707 void wxListBox::Delete(unsigned int n
)
709 wxCHECK_RET( m_treeview
!= NULL
, wxT("invalid listbox") );
711 InvalidateBestSize();
714 gboolean res
= gtk_tree_model_iter_nth_child(
715 GTK_TREE_MODEL(m_liststore
),
716 &iter
, NULL
, //NULL = parent = get first
720 wxCHECK_RET( res
, wxT("wrong listbox index") );
722 //this returns false if iter is invalid (i.e. deleting item
723 //at end) but since we don't use iter, well... :)
724 gtk_list_store_remove(m_liststore
, &iter
);
727 // ----------------------------------------------------------------------------
728 // get GtkTreeEntry from position (note: you need to g_unref it if valid)
729 // ----------------------------------------------------------------------------
731 struct _GtkTreeEntry
* wxListBox::GtkGetEntry(int n
) const
734 gboolean res
= gtk_tree_model_iter_nth_child(
735 GTK_TREE_MODEL(m_liststore
),
736 &iter
, NULL
, //NULL = parent = get first
741 wxLogDebug(wxT("gtk_tree_model_iter_nth_child failed\n")
742 wxT("Passed in value was:[%i] List size:[%u]"),
743 n
, wxListBox::GetCount() );
748 GtkTreeEntry
* entry
= NULL
;
749 gtk_tree_model_get(GTK_TREE_MODEL(m_liststore
), &iter
,
750 WXLISTBOX_DATACOLUMN
, &entry
, -1);
755 // ----------------------------------------------------------------------------
757 // ----------------------------------------------------------------------------
759 void* wxListBox::DoGetItemClientData(unsigned int n
) const
761 wxCHECK_MSG( IsValid(n
), NULL
,
762 wxT("Invalid index passed to GetItemClientData") );
764 GtkTreeEntry
* entry
= GtkGetEntry(n
);
765 wxCHECK_MSG(entry
, NULL
, wxT("could not get entry"));
767 void* userdata
= gtk_tree_entry_get_userdata( entry
);
768 g_object_unref (entry
);
772 wxClientData
* wxListBox::DoGetItemClientObject(unsigned int n
) const
774 return (wxClientData
*) wxListBox::DoGetItemClientData(n
);
777 void wxListBox::DoSetItemClientData(unsigned int n
, void* clientData
)
779 wxCHECK_RET( IsValid(n
),
780 wxT("Invalid index passed to SetItemClientData") );
782 GtkTreeEntry
* entry
= GtkGetEntry(n
);
783 wxCHECK_RET(entry
, wxT("could not get entry"));
785 gtk_tree_entry_set_userdata( entry
, clientData
);
786 g_object_unref (entry
);
789 void wxListBox::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
)
791 // wxItemContainer already deletes data for us
792 wxListBox::DoSetItemClientData(n
, (void*) clientData
);
795 // ----------------------------------------------------------------------------
796 // string list access
797 // ----------------------------------------------------------------------------
799 void wxListBox::SetString(unsigned int n
, const wxString
&string
)
801 wxCHECK_RET( IsValid(n
), wxT("invalid index in wxListBox::SetString") );
802 wxCHECK_RET( m_treeview
!= NULL
, wxT("invalid listbox") );
804 GtkTreeEntry
* entry
= GtkGetEntry(n
);
805 wxCHECK_RET( entry
, wxT("wrong listbox index") );
807 wxString label
= string
;
809 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
811 label
.Prepend(wxCHECKLBOX_STRING
);
812 #endif // wxUSE_CHECKLISTBOX
814 // RN: This may look wierd but the problem is that the TreeView
815 // doesn't resort or update when changed above and there is no real
816 // notification function...
817 void* userdata
= gtk_tree_entry_get_userdata(entry
);
818 gtk_tree_entry_set_userdata(entry
, NULL
); //don't delete on destroy
819 g_object_unref (entry
);
821 bool bWasSelected
= wxListBox::IsSelected(n
);
822 wxListBox::Delete(n
);
824 wxArrayString aItems
;
826 GtkInsertItems(aItems
, &userdata
, n
);
828 wxListBox::GtkSetSelection(n
, true, true);
831 wxString
wxListBox::GetString(unsigned int n
) const
833 wxCHECK_MSG( m_treeview
!= NULL
, wxEmptyString
, wxT("invalid listbox") );
835 GtkTreeEntry
* entry
= GtkGetEntry(n
);
836 wxCHECK_MSG( entry
, wxEmptyString
, wxT("wrong listbox index") );
838 wxString label
= wxGTK_CONV_BACK( gtk_tree_entry_get_label(entry
) );
840 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
841 // checklistboxes have "[±] " prepended to their lables, remove it
843 // NB: 4 below is the length of wxCHECKLBOX_STRING from wx/gtk/checklst.h
844 if ( m_hasCheckBoxes
)
846 #endif // wxUSE_CHECKLISTBOX
848 g_object_unref (entry
);
852 unsigned int wxListBox::GetCount() const
854 wxCHECK_MSG( m_treeview
!= NULL
, 0, wxT("invalid listbox") );
856 return (unsigned int)gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_liststore
), NULL
);
859 int wxListBox::FindString( const wxString
&item
, bool bCase
) const
861 wxCHECK_MSG( m_treeview
!= NULL
, wxNOT_FOUND
, wxT("invalid listbox") );
863 //Sort of hackish - maybe there is a faster way
864 unsigned int nCount
= wxListBox::GetCount();
866 for(unsigned int i
= 0; i
< nCount
; ++i
)
868 if( item
.IsSameAs( wxListBox::GetString(i
), bCase
) )
873 // it's not an error if the string is not found -> no wxCHECK
877 // ----------------------------------------------------------------------------
879 // ----------------------------------------------------------------------------
881 int wxListBox::GetSelection() const
883 wxCHECK_MSG( m_treeview
!= NULL
, wxNOT_FOUND
, wxT("invalid listbox"));
884 wxCHECK_MSG( HasFlag(wxLB_SINGLE
), wxNOT_FOUND
,
885 wxT("must be single selection listbox"));
888 GtkTreeSelection
* selection
= gtk_tree_view_get_selection(m_treeview
);
890 // only works on single-sel
891 if (!gtk_tree_selection_get_selected(selection
, NULL
, &iter
))
895 gtk_tree_model_get_path(GTK_TREE_MODEL(m_liststore
), &iter
);
897 int sel
= gtk_tree_path_get_indices(path
)[0];
899 gtk_tree_path_free(path
);
904 int wxListBox::GetSelections( wxArrayInt
& aSelections
) const
906 wxCHECK_MSG( m_treeview
!= NULL
, wxNOT_FOUND
, wxT("invalid listbox") );
912 GtkTreeSelection
* selection
= gtk_tree_view_get_selection(m_treeview
);
914 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(m_liststore
), &iter
))
915 { //gtk_tree_selection_get_selected_rows is GTK 2.2+ so iter instead
918 if (gtk_tree_selection_iter_is_selected(selection
, &iter
))
922 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(m_liststore
), &iter
));
925 return aSelections
.GetCount();
928 bool wxListBox::IsSelected( int n
) const
930 wxCHECK_MSG( m_treeview
!= NULL
, false, wxT("invalid listbox") );
932 GtkTreeSelection
* selection
= gtk_tree_view_get_selection(m_treeview
);
935 gboolean res
= gtk_tree_model_iter_nth_child(
936 GTK_TREE_MODEL(m_liststore
),
937 &iter
, NULL
, //NULL = parent = get first
940 wxCHECK_MSG( res
, false, wxT("Invalid index") );
942 return gtk_tree_selection_iter_is_selected(selection
, &iter
);
945 void wxListBox::DoSetSelection( int n
, bool select
)
947 // passing -1 to SetSelection() is documented to deselect all items
948 if ( n
== wxNOT_FOUND
)
950 // ... and not generate any events in the process
954 wxCHECK_RET( IsValid(n
), wxT("invalid index in wxListBox::SetSelection") );
956 // don't generate the selection event
957 GtkSetSelection(n
, select
, true);
960 void wxListBox::GtkDeselectAll()
962 wxCHECK_RET( m_treeview
!= NULL
, wxT("invalid listbox") );
964 GtkTreeSelection
* selection
= gtk_tree_view_get_selection(m_treeview
);
968 gtk_tree_selection_unselect_all(selection
);
970 m_blockEvent
= false;
973 void wxListBox::GtkSetSelection(int n
, const bool select
, const bool blockEvent
)
975 wxCHECK_RET( m_treeview
!= NULL
, wxT("invalid listbox") );
977 GtkTreeSelection
* selection
= gtk_tree_view_get_selection(m_treeview
);
980 gboolean res
= gtk_tree_model_iter_nth_child(
981 GTK_TREE_MODEL(m_liststore
),
982 &iter
, NULL
, //NULL = parent = get first
985 wxCHECK_RET( res
, wxT("Invalid index") );
987 m_blockEvent
= blockEvent
;
990 gtk_tree_selection_select_iter(selection
, &iter
);
992 gtk_tree_selection_unselect_iter(selection
, &iter
);
994 m_blockEvent
= false;
997 void wxListBox::DoSetFirstItem( int n
)
999 wxCHECK_RET( m_treeview
, wxT("invalid listbox") );
1000 wxCHECK_RET( IsValid(n
), wxT("invalid index"));
1002 //RN: I have no idea why this line is needed...
1003 if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (m_treeview
))
1007 gtk_tree_model_iter_nth_child(
1008 GTK_TREE_MODEL(m_liststore
),
1010 NULL
, //NULL = parent = get first
1014 GtkTreePath
* path
= gtk_tree_model_get_path(
1015 GTK_TREE_MODEL(m_liststore
), &iter
);
1017 // Scroll to the desired cell (0.0 == topleft alignment)
1018 gtk_tree_view_scroll_to_cell(m_treeview
, path
, NULL
,
1021 gtk_tree_path_free(path
);
1024 // ----------------------------------------------------------------------------
1026 // ----------------------------------------------------------------------------
1028 int wxListBox::DoListHitTest(const wxPoint
& point
) const
1030 // gtk_tree_view_get_path_at_pos() also gets items that are not visible and
1031 // we only want visible items we need to check for it manually here
1032 if ( !GetClientRect().Inside(point
) )
1035 // need to translate from master window since it is in client coords
1037 gdk_window_get_geometry(gtk_tree_view_get_bin_window(m_treeview
),
1038 &binx
, &biny
, NULL
, NULL
, NULL
);
1041 if ( !gtk_tree_view_get_path_at_pos
1047 NULL
, // [out] column (always 0 here)
1048 NULL
, // [out] x-coord relative to the cell (not interested)
1049 NULL
// [out] y-coord relative to the cell
1055 int index
= gtk_tree_path_get_indices(path
)[0];
1056 gtk_tree_path_free(path
);
1061 // ----------------------------------------------------------------------------
1063 // ----------------------------------------------------------------------------
1066 void wxListBox::ApplyToolTip( GtkTooltips
*tips
, const wxChar
*tip
)
1068 // RN: Is this needed anymore?
1069 gtk_tooltips_set_tip( tips
, GTK_WIDGET( m_treeview
), wxGTK_CONV(tip
), (gchar
*) NULL
);
1071 #endif // wxUSE_TOOLTIPS
1073 GtkWidget
*wxListBox::GetConnectWidget()
1075 // the correct widget for listbox events (such as mouse clicks for example)
1076 // is m_treeview, not the parent scrolled window
1077 return GTK_WIDGET(m_treeview
);
1080 bool wxListBox::IsOwnGtkWindow( GdkWindow
*window
)
1082 return (window
== gtk_tree_view_get_bin_window(m_treeview
));
1085 void wxListBox::DoApplyWidgetStyle(GtkRcStyle
*style
)
1087 if (m_hasBgCol
&& m_backgroundColour
.Ok())
1089 GdkWindow
*window
= gtk_tree_view_get_bin_window(m_treeview
);
1092 m_backgroundColour
.CalcPixel( gdk_drawable_get_colormap( window
) );
1093 gdk_window_set_background( window
, m_backgroundColour
.GetColor() );
1094 gdk_window_clear( window
);
1098 gtk_widget_modify_style( GTK_WIDGET(m_treeview
), style
);
1101 void wxListBox::OnInternalIdle()
1103 // Check if we have to show window now
1104 if (GtkShowFromOnIdle()) return;
1106 wxCursor cursor
= m_cursor
;
1107 if (g_globalCursor
.Ok()) cursor
= g_globalCursor
;
1109 if (GTK_WIDGET(m_treeview
)->window
&& cursor
.Ok())
1111 /* I now set the cursor the anew in every OnInternalIdle call
1112 as setting the cursor in a parent window also effects the
1113 windows above so that checking for the current cursor is
1115 GdkWindow
*window
= gtk_tree_view_get_bin_window(m_treeview
);
1116 gdk_window_set_cursor( window
, cursor
.GetCursor() );
1119 if (wxUpdateUIEvent::CanUpdate(this))
1120 UpdateWindowUI(wxUPDATE_UI_FROMIDLE
);
1123 wxSize
wxListBox::DoGetBestSize() const
1125 wxCHECK_MSG(m_treeview
, wxDefaultSize
, wxT("invalid tree view"));
1127 // Start with a minimum size that's not too small
1129 GetTextExtent( wxT("X"), &cx
, &cy
);
1130 int lbWidth
= 3 * cx
;
1133 // Get the visible area of the tree view (limit to the 10th item
1134 // so that it isn't too big)
1135 unsigned int count
= GetCount();
1140 // Find the widest line
1141 for(unsigned int i
= 0; i
< count
; i
++) {
1142 wxString
str(GetString(i
));
1143 GetTextExtent(str
, &wLine
, NULL
);
1144 lbWidth
= wxMax(lbWidth
, wLine
);
1149 // And just a bit more for the checkbox if present and then some
1150 // (these are rough guesses)
1151 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
1152 if ( m_hasCheckBoxes
)
1155 cy
= cy
> 25 ? cy
: 25; // rough height of checkbox
1159 // don't make the listbox too tall (limit height to around 10 items) but don't
1160 // make it too small neither
1161 lbHeight
= (cy
+4) * wxMin(wxMax(count
, 3), 10);
1164 // Add room for the scrollbar
1165 lbWidth
+= wxSystemSettings::GetMetric(wxSYS_VSCROLL_X
);
1167 wxSize
best(lbWidth
, lbHeight
);
1168 CacheBestSize(best
);
1174 wxListBox::GetClassDefaultAttributes(wxWindowVariant
WXUNUSED(variant
))
1176 return GetDefaultAttributesFromGTKWidget(gtk_tree_view_new
, true);
1179 #endif // wxUSE_LISTBOX