Patch #1446110: use of uninitialized variables
[wxWidgets.git] / src / gtk / listbox.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/listbox.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Modified By: Ryan Norton (GtkTreeView implementation)
6 // Id: $Id$
7 // Copyright: (c) 1998 Robert Roebling
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #include "wx/defs.h"
15
16 #if wxUSE_LISTBOX
17
18 #include "wx/listbox.h"
19 #include "wx/dynarray.h"
20 #include "wx/arrstr.h"
21 #include "wx/utils.h"
22 #include "wx/intl.h"
23 #include "wx/checklst.h"
24 #include "wx/settings.h"
25 #include "wx/log.h"
26 #include "wx/gtk/private.h"
27 #include "wx/gtk/treeentry_gtk.h"
28
29 #if wxUSE_TOOLTIPS
30 #include "wx/tooltip.h"
31 #endif
32
33 #include <gdk/gdk.h>
34 #include <gtk/gtk.h>
35 #include <gdk/gdkkeysyms.h>
36
37 //-----------------------------------------------------------------------------
38 // data
39 //-----------------------------------------------------------------------------
40
41 extern bool g_blockEventsOnDrag;
42 extern bool g_blockEventsOnScroll;
43 extern wxCursor g_globalCursor;
44
45
46 //-----------------------------------------------------------------------------
47 // idle system
48 //-----------------------------------------------------------------------------
49
50 extern void wxapp_install_idle_handler();
51 extern bool g_isIdle;
52
53 //-----------------------------------------------------------------------------
54 // Macro to tell which row the strings are in (1 if native checklist, 0 if not)
55 //-----------------------------------------------------------------------------
56
57 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
58 # define WXLISTBOX_DATACOLUMN_ARG(x) (x->m_hasCheckBoxes ? 1 : 0)
59 #else
60 # define WXLISTBOX_DATACOLUMN_ARG(x) (0)
61 #endif // wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
62
63 #define WXLISTBOX_DATACOLUMN WXLISTBOX_DATACOLUMN_ARG(this)
64
65 //-----------------------------------------------------------------------------
66 // "row-activated"
67 //-----------------------------------------------------------------------------
68
69 extern "C" {
70 static void
71 gtk_listbox_row_activated_callback(GtkTreeView *treeview,
72 GtkTreePath *path,
73 GtkTreeViewColumn *col,
74 wxListBox *listbox)
75 {
76 if (g_isIdle) wxapp_install_idle_handler();
77
78 if (g_blockEventsOnDrag) return;
79 if (g_blockEventsOnScroll) return;
80
81 if (!listbox->m_hasVMT) return;
82
83 //Notes:
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
88 // from a keyevent
89 //2b) It seems more correct
90
91 int sel = gtk_tree_path_get_indices(path)[0];
92
93 if(!listbox->m_spacePressed)
94 {
95 //Assume it was double-click
96 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, listbox->GetId() );
97 event.SetEventObject( listbox );
98
99 if(listbox->IsSelected(sel))
100 {
101 GtkTreeEntry* entry = listbox->GtkGetEntry(sel);
102
103 if(entry)
104 {
105 event.SetInt(sel);
106 event.SetString(wxConvUTF8.cMB2WX(gtk_tree_entry_get_label(entry)));
107
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));
114 }
115 else
116 {
117 wxLogSysError(wxT("Internal error - could not get entry for double-click"));
118 event.SetInt(-1);
119 }
120 }
121 else
122 event.SetInt(-1);
123
124 listbox->GetEventHandler()->ProcessEvent( event );
125 }
126 else
127 {
128 listbox->m_spacePressed = false; //don't block selection behaviour anymore
129
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)
133 {
134 wxCheckListBox *clb = (wxCheckListBox *)listbox;
135
136 clb->Check( sel, !clb->IsChecked(sel) );
137
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 );
142 }
143 #endif // wxUSE_CHECKLISTBOX
144
145 if( (((listbox->GetWindowStyleFlag() & wxLB_MULTIPLE) != 0) ||
146 ((listbox->GetWindowStyleFlag() & wxLB_EXTENDED) != 0)) )
147 {
148 //toggle the selection + send event
149 listbox->GtkSetSelection(sel, !listbox->IsSelected( sel ), FALSE);
150 }
151 }
152 }
153 }
154
155 //-----------------------------------------------------------------------------
156 // "button_press_event"
157 //-----------------------------------------------------------------------------
158
159 extern "C" {
160 static gint
161 gtk_listbox_button_press_callback( GtkWidget *widget,
162 GdkEventButton *gdk_event,
163 wxListBox *listbox )
164 {
165 if (g_isIdle) wxapp_install_idle_handler();
166
167 if (g_blockEventsOnDrag) return FALSE;
168 if (g_blockEventsOnScroll) return FALSE;
169
170 if (!listbox->m_hasVMT) return FALSE;
171
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;
175
176 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
177 if ((listbox->m_hasCheckBoxes) && (gdk_event->x < 15) && (gdk_event->type != GDK_2BUTTON_PRESS))
178 {
179 GtkTreePath* path;
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);
185
186 wxCheckListBox *clb = (wxCheckListBox *)listbox;
187
188 clb->Check( sel, !clb->IsChecked(sel) );
189
190 wxCommandEvent event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, listbox->GetId() );
191 event.SetEventObject( listbox );
192 event.SetInt( sel );
193 listbox->GetEventHandler()->ProcessEvent( event );
194 }
195 #endif // wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
196
197 return FALSE;
198 }
199 }
200
201 //-----------------------------------------------------------------------------
202 // "key_press_event"
203 //-----------------------------------------------------------------------------
204
205 extern "C" {
206 static gint
207 gtk_listbox_key_press_callback( GtkWidget *widget,
208 GdkEventKey *gdk_event,
209 wxListBox *listbox )
210 {
211 if (g_isIdle) wxapp_install_idle_handler();
212
213 if (g_blockEventsOnDrag) return FALSE;
214
215
216 bool ret = FALSE;
217
218 if ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab))
219 {
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 );
227 }
228
229 if ((gdk_event->keyval == GDK_Return) && (!ret))
230 {
231 // eat return in all modes (RN:WHY?)
232 ret = TRUE;
233 }
234
235 // Check or uncheck item with SPACE
236 if (gdk_event->keyval == ' ')
237 {
238 //In the keyevent we don't know the index of the item
239 //and the activated event gets called anyway...
240 //
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;
246 }
247
248 if (ret)
249 {
250 g_signal_stop_emission_by_name (widget, "key_press_event");
251 return TRUE;
252 }
253
254 return FALSE;
255 }
256 }
257
258 //-----------------------------------------------------------------------------
259 // "select" and "deselect"
260 //-----------------------------------------------------------------------------
261
262 extern "C" {
263 static gboolean gtk_listitem_select_cb( GtkTreeSelection* selection,
264 GtkTreeModel* model,
265 GtkTreePath* path,
266 gboolean is_selected,
267 wxListBox *listbox )
268 {
269 if (g_isIdle) wxapp_install_idle_handler();
270
271 if (!listbox->m_hasVMT) return TRUE;
272 if (g_blockEventsOnDrag) return TRUE;
273
274 if (listbox->m_spacePressed) return FALSE; //see keyevent callback
275 if (listbox->m_blockEvent) return TRUE;
276
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
280
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;
289
290 int nIndex = gtk_tree_path_get_indices(path)[0];
291 GtkTreeEntry* entry = listbox->GtkGetEntry(nIndex);
292
293 if(entry)
294 {
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!!
299
300 GtkTreeSelection* selection =
301 gtk_tree_view_get_selection(listbox->m_treeview);
302 GtkTreeIter iter;
303 gtk_tree_model_get_iter(GTK_TREE_MODEL(listbox->m_liststore), &iter, path);
304 gtk_tree_selection_select_iter(selection, &iter);
305
306 listbox->m_blockEvent = FALSE;
307
308 //Finally, send the wx event
309 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, listbox->GetId() );
310 event.SetEventObject( listbox );
311
312 // indicate whether this is a selection or a deselection
313 event.SetExtraLong( 1 );
314
315 event.SetInt(nIndex);
316 event.SetString(wxConvUTF8.cMB2WX(gtk_tree_entry_get_label(entry)));
317
318 if ( listbox->HasClientObjectData() )
319 event.SetClientObject(
320 (wxClientData*) gtk_tree_entry_get_userdata(entry)
321 );
322 else if ( listbox->HasClientUntypedData() )
323 event.SetClientData( gtk_tree_entry_get_userdata(entry) );
324
325 listbox->GetEventHandler()->ProcessEvent( event );
326
327 g_object_unref(G_OBJECT(entry));
328 return FALSE; //We handled it/did it manually
329 }
330
331 return TRUE; //allow selection to change
332 }
333 }
334
335 //-----------------------------------------------------------------------------
336 // GtkTreeEntry destruction (to destroy client data)
337 //-----------------------------------------------------------------------------
338
339 extern "C" {
340 static void gtk_tree_entry_destroy_cb(GtkTreeEntry* entry,
341 wxListBox* listbox)
342 {
343 if(listbox->HasClientObjectData())
344 {
345 gpointer userdata = gtk_tree_entry_get_userdata(entry);
346 if(userdata)
347 delete (wxClientData *)userdata;
348 }
349 }
350 }
351
352 //-----------------------------------------------------------------------------
353 // Sorting callback (standard CmpNoCase return value)
354 //-----------------------------------------------------------------------------
355
356 extern "C" {
357 static gint gtk_listbox_sort_callback(GtkTreeModel *model,
358 GtkTreeIter *a,
359 GtkTreeIter *b,
360 wxListBox *listbox)
361 {
362 GtkTreeEntry* entry;
363 GtkTreeEntry* entry2;
364
365 gtk_tree_model_get(GTK_TREE_MODEL(listbox->m_liststore),
366 a,
367 WXLISTBOX_DATACOLUMN_ARG(listbox),
368 &entry, -1);
369 gtk_tree_model_get(GTK_TREE_MODEL(listbox->m_liststore),
370 b,
371 WXLISTBOX_DATACOLUMN_ARG(listbox),
372 &entry2, -1);
373 wxCHECK_MSG(entry, 0, wxT("Could not get entry"));
374 wxCHECK_MSG(entry2, 0, wxT("Could not get entry2"));
375
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));
380
381 g_object_unref(G_OBJECT(entry));
382 g_object_unref(G_OBJECT(entry2));
383
384 return ret;
385 }
386 }
387
388 //-----------------------------------------------------------------------------
389 // Searching callback (TRUE == not equal, FALSE == equal)
390 //-----------------------------------------------------------------------------
391
392 extern "C" {
393 static gboolean gtk_listbox_searchequal_callback(GtkTreeModel* model,
394 gint column,
395 const gchar* key,
396 GtkTreeIter* iter,
397 wxListBox* listbox)
398 {
399 GtkTreeEntry* entry;
400
401 gtk_tree_model_get(GTK_TREE_MODEL(listbox->m_liststore),
402 iter,
403 WXLISTBOX_DATACOLUMN_ARG(listbox),
404 &entry, -1);
405 wxCHECK_MSG(entry, 0, wxT("Could not get entry"));
406 gchar* keycollatekey = g_utf8_collate_key(key, -1);
407
408 int ret = strcasecmp(keycollatekey,
409 gtk_tree_entry_get_collate_key(entry));
410
411 g_free(keycollatekey);
412 g_object_unref(G_OBJECT(entry));
413
414 return ret != 0;
415 }
416 }
417
418 //-----------------------------------------------------------------------------
419 // wxListBox
420 //-----------------------------------------------------------------------------
421
422 IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControl)
423
424 // ----------------------------------------------------------------------------
425 // construction
426 // ----------------------------------------------------------------------------
427
428 wxListBox::wxListBox()
429 {
430 m_treeview = (GtkTreeView*) NULL;
431 #if wxUSE_CHECKLISTBOX
432 m_hasCheckBoxes = FALSE;
433 #endif // wxUSE_CHECKLISTBOX
434 m_spacePressed = false;
435 }
436
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 )
442 {
443 wxCArrayString chs(choices);
444
445 return Create( parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
446 style, validator, name );
447 }
448
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 )
454 {
455 m_needParent = TRUE;
456 m_acceptsFocus = TRUE;
457 m_blockEvent = FALSE;
458
459 if (!PreCreation( parent, pos, size ) ||
460 !CreateBase( parent, id, pos, size, style, validator, name ))
461 {
462 wxFAIL_MSG( wxT("wxListBox creation failed") );
463 return FALSE;
464 }
465
466 m_widget = gtk_scrolled_window_new( (GtkAdjustment*) NULL, (GtkAdjustment*) NULL );
467 if (style & wxLB_ALWAYS_SB)
468 {
469 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
470 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
471 }
472 else
473 {
474 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
475 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
476 }
477
478 m_treeview = GTK_TREE_VIEW( gtk_tree_view_new( ) );
479
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);
483
484 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
485 if(m_hasCheckBoxes)
486 ((wxCheckListBox*)this)->DoCreateCheckList();
487 #endif // wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
488
489 // Create the data column
490 gtk_tree_view_insert_column_with_attributes(m_treeview, -1, "",
491 gtk_cell_renderer_text_new(),
492 "text",
493 WXLISTBOX_DATACOLUMN, NULL);
494
495 // Now create+set the model (GtkListStore) - first argument # of columns
496 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
497 if(m_hasCheckBoxes)
498 m_liststore = gtk_list_store_new(2, G_TYPE_BOOLEAN,
499 GTK_TYPE_TREE_ENTRY);
500 else
501 #endif
502 m_liststore = gtk_list_store_new(1, GTK_TYPE_TREE_ENTRY);
503
504 gtk_tree_view_set_model(m_treeview, GTK_TREE_MODEL(m_liststore));
505
506 g_object_unref(G_OBJECT(m_liststore)); //free on treeview destruction
507
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,
517 this,
518 NULL);
519
520 gtk_tree_view_set_enable_search(m_treeview, FALSE);
521
522
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
527
528 GtkSelectionMode mode;
529 if (style & wxLB_MULTIPLE)
530 {
531 mode = GTK_SELECTION_MULTIPLE;
532 }
533 else if (style & wxLB_EXTENDED)
534 {
535 mode = GTK_SELECTION_EXTENDED;
536 }
537 else
538 {
539 // if style was 0 set single mode
540 m_windowStyle |= wxLB_SINGLE;
541 mode = GTK_SELECTION_SINGLE;
542 }
543
544 gtk_tree_selection_set_mode( selection, mode );
545
546 //Handle sortable stuff
547 if(style & wxLB_SORT)
548 {
549 //Setup sorting in ascending (wx) order
550 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(m_liststore),
551 WXLISTBOX_DATACOLUMN,
552 GTK_SORT_ASCENDING);
553
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,
558 this, //userdata
559 NULL //"destroy notifier"
560 );
561 }
562
563
564 gtk_container_add (GTK_CONTAINER (m_widget), GTK_WIDGET(m_treeview) );
565
566 gtk_widget_show( GTK_WIDGET(m_treeview) );
567
568 wxListBox::DoInsertItems(wxArrayString(n, choices), 0); // insert initial items
569
570 //treeview-specific events
571 g_signal_connect(m_treeview, "row-activated",
572 G_CALLBACK(gtk_listbox_row_activated_callback), this);
573
574 // other events
575 g_signal_connect (m_treeview, "button_press_event",
576 G_CALLBACK (gtk_listbox_button_press_callback),
577 this);
578 g_signal_connect (m_treeview, "key_press_event",
579 G_CALLBACK (gtk_listbox_key_press_callback),
580 this);
581
582 m_parent->DoAddChild( this );
583
584 PostCreation(size);
585 SetBestSize(size); // need this too because this is a wxControlWithItems
586
587 return TRUE;
588 }
589
590 wxListBox::~wxListBox()
591 {
592 m_hasVMT = FALSE;
593
594 Clear();
595 }
596
597 // ----------------------------------------------------------------------------
598 // adding items
599 // ----------------------------------------------------------------------------
600
601 void wxListBox::GtkInsertItems(const wxArrayString& items,
602 void** clientData, int pos)
603 {
604 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
605
606 InvalidateBestSize();
607
608 // Create and set column ids and GValues
609
610 size_t nNum = items.GetCount();
611 int nCurCount = wxListBox::GetCount();
612 wxASSERT_MSG(pos <= nCurCount, wxT("Invalid index passed to wxListBox"));
613
614 GtkTreeIter* pIter = NULL; // append by default
615 GtkTreeIter iter;
616 if (pos != nCurCount)
617 {
618 gboolean res = gtk_tree_model_iter_nth_child(
619 GTK_TREE_MODEL(m_liststore),
620 &iter, NULL, //NULL = parent = get first
621 pos );
622 if(!res)
623 {
624 wxLogSysError(wxT("internal wxListBox error in insertion"));
625 return;
626 }
627
628 pIter = &iter;
629 }
630
631 for (size_t i = 0; i < nNum; ++i)
632 {
633 wxString label = items[i];
634
635 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
636 if (m_hasCheckBoxes)
637 {
638 label.Prepend(wxCHECKLBOX_STRING);
639 }
640 #endif // wxUSE_CHECKLISTBOX
641
642
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,
647 this);
648
649 if (clientData)
650 gtk_tree_entry_set_userdata(entry, clientData[i]);
651
652 GtkTreeIter itercur;
653 gtk_list_store_insert_before(m_liststore, &itercur, pIter);
654
655 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
656 if (m_hasCheckBoxes)
657 {
658 gtk_list_store_set(m_liststore, &itercur,
659 0, FALSE, //FALSE == not toggled
660 1, entry, -1);
661 }
662 else
663 #endif
664 gtk_list_store_set(m_liststore, &itercur,
665 0, entry, -1);
666
667 g_object_unref(G_OBJECT(entry)); //liststore always refs :)
668 }
669 }
670
671 void wxListBox::DoInsertItems(const wxArrayString& items, int pos)
672 {
673 GtkInsertItems(items, NULL, pos);
674 }
675
676 int wxListBox::DoAppend( const wxString& item )
677 {
678 // Call DoInsertItems
679 int nWhere = wxListBox::GetCount();
680 wxArrayString aItems;
681 aItems.Add(item);
682 wxListBox::DoInsertItems(aItems, nWhere);
683 return nWhere;
684 }
685
686 void wxListBox::DoSetItems( const wxArrayString& items,
687 void **clientData)
688 {
689 Clear();
690 GtkInsertItems(items, clientData, 0);
691 }
692
693 // ----------------------------------------------------------------------------
694 // deleting items
695 // ----------------------------------------------------------------------------
696
697 void wxListBox::Clear()
698 {
699 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
700
701 InvalidateBestSize();
702
703 gtk_list_store_clear( m_liststore ); /* well, THAT was easy :) */
704 }
705
706 void wxListBox::Delete( int n )
707 {
708 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
709
710 InvalidateBestSize();
711
712 GtkTreeIter iter;
713 gboolean res = gtk_tree_model_iter_nth_child(
714 GTK_TREE_MODEL(m_liststore),
715 &iter, NULL, //NULL = parent = get first
716 n
717 );
718
719 wxCHECK_RET( res, wxT("wrong listbox index") );
720
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);
724 }
725
726 // ----------------------------------------------------------------------------
727 // get GtkTreeEntry from position (note: you need to g_unref it if valid)
728 // ----------------------------------------------------------------------------
729
730 struct _GtkTreeEntry* wxListBox::GtkGetEntry(int n) const
731 {
732 GtkTreeIter iter;
733 gboolean res = gtk_tree_model_iter_nth_child(
734 GTK_TREE_MODEL(m_liststore),
735 &iter, NULL, //NULL = parent = get first
736 n );
737
738 if (!res)
739 {
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() );
743 return NULL;
744 }
745
746
747 GtkTreeEntry* entry = NULL;
748 gtk_tree_model_get(GTK_TREE_MODEL(m_liststore), &iter,
749 WXLISTBOX_DATACOLUMN, &entry, -1);
750
751 return entry;
752 }
753
754 // ----------------------------------------------------------------------------
755 // client data
756 // ----------------------------------------------------------------------------
757
758 void* wxListBox::DoGetItemClientData( int n ) const
759 {
760 wxCHECK_MSG( n >= 0 && n < wxListBox::GetCount(), NULL,
761 wxT("Invalid index passed to GetItemClientData") );
762
763 GtkTreeEntry* entry = GtkGetEntry(n);
764 wxCHECK_MSG(entry, NULL, wxT("could not get entry"));
765
766 void* userdata = gtk_tree_entry_get_userdata( entry );
767 g_object_unref(G_OBJECT(entry));
768 return userdata;
769 }
770
771 wxClientData* wxListBox::DoGetItemClientObject( int n ) const
772 {
773 return (wxClientData*) wxListBox::DoGetItemClientData(n);
774 }
775
776 void wxListBox::DoSetItemClientData( int n, void* clientData )
777 {
778 wxCHECK_RET( n >= 0 && n < wxListBox::GetCount(),
779 wxT("Invalid index passed to SetItemClientData") );
780
781 GtkTreeEntry* entry = GtkGetEntry(n);
782 wxCHECK_RET(entry, wxT("could not get entry"));
783
784 gtk_tree_entry_set_userdata( entry, clientData );
785 g_object_unref(G_OBJECT(entry));
786 }
787
788 void wxListBox::DoSetItemClientObject( int n, wxClientData* clientData )
789 {
790 // wxItemContainer already deletes data for us
791 wxListBox::DoSetItemClientData(n, (void*) clientData);
792 }
793
794 // ----------------------------------------------------------------------------
795 // string list access
796 // ----------------------------------------------------------------------------
797
798 void wxListBox::SetString( int n, const wxString &string )
799 {
800 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
801
802 GtkTreeEntry* entry = GtkGetEntry(n);
803 wxCHECK_RET( entry, wxT("wrong listbox index") );
804
805 wxString label = string;
806
807 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
808 if (m_hasCheckBoxes)
809 label.Prepend(wxCHECKLBOX_STRING);
810 #endif // wxUSE_CHECKLISTBOX
811
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));
818
819 bool bWasSelected = wxListBox::IsSelected(n);
820 wxListBox::Delete(n);
821
822 wxArrayString aItems;
823 aItems.Add(label);
824 GtkInsertItems(aItems, &userdata, n);
825 if (bWasSelected)
826 wxListBox::GtkSetSelection(n, TRUE, TRUE);
827 }
828
829 wxString wxListBox::GetString( int n ) const
830 {
831 wxCHECK_MSG( m_treeview != NULL, wxEmptyString, wxT("invalid listbox") );
832
833 GtkTreeEntry* entry = GtkGetEntry(n);
834 wxCHECK_MSG( entry, wxEmptyString, wxT("wrong listbox index") );
835
836 wxString label = wxGTK_CONV_BACK( gtk_tree_entry_get_label(entry) );
837
838 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
839 // checklistboxes have "[±] " prepended to their lables, remove it
840 //
841 // NB: 4 below is the length of wxCHECKLBOX_STRING from wx/gtk/checklst.h
842 if ( m_hasCheckBoxes )
843 label.erase(0, 4);
844 #endif // wxUSE_CHECKLISTBOX
845
846 g_object_unref(G_OBJECT(entry));
847 return label;
848 }
849
850 int wxListBox::GetCount() const
851 {
852 wxCHECK_MSG( m_treeview != NULL, -1, wxT("invalid listbox") );
853
854 return gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_liststore), NULL);
855 }
856
857 int wxListBox::FindString( const wxString &item, bool bCase ) const
858 {
859 wxCHECK_MSG( m_treeview != NULL, wxNOT_FOUND, wxT("invalid listbox") );
860
861 //Sort of hackish - maybe there is a faster way
862 int nCount = wxListBox::GetCount();
863
864 for(int i = 0; i < nCount; ++i)
865 {
866 if( item.IsSameAs( wxListBox::GetString(i), bCase ) )
867 return i;
868 }
869
870
871 // it's not an error if the string is not found -> no wxCHECK
872 return wxNOT_FOUND;
873 }
874
875 // ----------------------------------------------------------------------------
876 // selection
877 // ----------------------------------------------------------------------------
878
879 int wxListBox::GetSelection() const
880 {
881 wxCHECK_MSG( m_treeview != NULL, -1, wxT("invalid listbox"));
882 wxCHECK_MSG( HasFlag(wxLB_SINGLE), -1,
883 wxT("must be single selection listbox"));
884
885 GtkTreeIter iter;
886 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
887
888 // only works on single-sel
889 if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
890 return -1;
891
892 GtkTreePath* path =
893 gtk_tree_model_get_path(GTK_TREE_MODEL(m_liststore), &iter);
894
895 int sel = gtk_tree_path_get_indices(path)[0];
896
897 gtk_tree_path_free(path);
898
899 return sel;
900 }
901
902 int wxListBox::GetSelections( wxArrayInt& aSelections ) const
903 {
904 wxCHECK_MSG( m_treeview != NULL, -1, wxT("invalid listbox") );
905
906 aSelections.Empty();
907
908 int i = 0;
909 GtkTreeIter iter;
910 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
911
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
914 do
915 {
916 if (gtk_tree_selection_iter_is_selected(selection, &iter))
917 aSelections.Add(i);
918
919 i++;
920 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(m_liststore), &iter));
921 }
922
923 return aSelections.GetCount();
924 }
925
926 bool wxListBox::IsSelected( int n ) const
927 {
928 wxCHECK_MSG( m_treeview != NULL, FALSE, wxT("invalid listbox") );
929
930 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
931
932 GtkTreeIter iter;
933 gboolean res = gtk_tree_model_iter_nth_child(
934 GTK_TREE_MODEL(m_liststore),
935 &iter, NULL, //NULL = parent = get first
936 n );
937
938 wxCHECK_MSG( res, FALSE, wxT("Invalid index") );
939
940 return gtk_tree_selection_iter_is_selected(selection, &iter);
941 }
942
943 void wxListBox::DoSetSelection( int n, bool select )
944 {
945 return GtkSetSelection(n, select, TRUE); //docs say no events here
946 }
947
948 void wxListBox::GtkSetSelection(int n, const bool select, const bool blockEvent)
949 {
950 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
951
952 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
953
954 GtkTreeIter iter;
955 gboolean res = gtk_tree_model_iter_nth_child(
956 GTK_TREE_MODEL(m_liststore),
957 &iter, NULL, //NULL = parent = get first
958 n
959 );
960 wxCHECK_RET( res, wxT("Invalid index") );
961
962 m_blockEvent = blockEvent;
963
964 if (select)
965 gtk_tree_selection_select_iter(selection, &iter);
966 else
967 gtk_tree_selection_unselect_iter(selection, &iter);
968
969 m_blockEvent = FALSE;
970 }
971
972 void wxListBox::DoSetFirstItem( int n )
973 {
974 wxCHECK_RET( m_treeview, wxT("invalid listbox") );
975 wxCHECK_RET( n >= 0 && n < wxListBox::GetCount(), wxT("invalid index"));
976
977 //RN: I have no idea why this line is needed...
978 if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (m_treeview))
979 return;
980
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);
987
988 GtkAdjustment *adjustment =
989 (GtkAdjustment*) g_object_get_qdata(G_OBJECT (m_treeview), vadjustment_key_id);
990 wxCHECK_RET( adjustment, wxT("invalid listbox code") );
991
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;
996
997 while(curlist)
998 {
999 gtk_tree_view_column_cell_get_size(
1000 GTK_TREE_VIEW_COLUMN(curlist->data),
1001 NULL, NULL, NULL, NULL,
1002 &cellheightcur);
1003
1004 cellheight = cellheightcur > cellheight ?
1005 cellheightcur : cellheight;
1006
1007 curlist = curlist->next;
1008 }
1009
1010 g_list_free(columnlist);
1011
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 );
1016 }
1017
1018
1019 // ----------------------------------------------------------------------------
1020 // helpers
1021 // ----------------------------------------------------------------------------
1022
1023 #if wxUSE_TOOLTIPS
1024 void wxListBox::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
1025 {
1026 // RN: Is this needed anymore?
1027 gtk_tooltips_set_tip( tips, GTK_WIDGET( m_treeview ), wxGTK_CONV(tip), (gchar*) NULL );
1028 }
1029 #endif // wxUSE_TOOLTIPS
1030
1031 GtkWidget *wxListBox::GetConnectWidget()
1032 {
1033 // return GTK_WIDGET(m_treeview);
1034 return m_widget;
1035 }
1036
1037 bool wxListBox::IsOwnGtkWindow( GdkWindow *window )
1038 {
1039 return (window == gtk_tree_view_get_bin_window(m_treeview));
1040 }
1041
1042 void wxListBox::DoApplyWidgetStyle(GtkRcStyle *style)
1043 {
1044 if (m_hasBgCol && m_backgroundColour.Ok())
1045 {
1046 GdkWindow *window = gtk_tree_view_get_bin_window(m_treeview);
1047 if (window)
1048 {
1049 m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) );
1050 gdk_window_set_background( window, m_backgroundColour.GetColor() );
1051 gdk_window_clear( window );
1052 }
1053 }
1054
1055 gtk_widget_modify_style( GTK_WIDGET(m_treeview), style );
1056 }
1057
1058 void wxListBox::OnInternalIdle()
1059 {
1060 //RN: Is this needed anymore?
1061 wxCursor cursor = m_cursor;
1062 if (g_globalCursor.Ok()) cursor = g_globalCursor;
1063
1064 if (GTK_WIDGET(m_treeview)->window && cursor.Ok())
1065 {
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
1069 not possible. */
1070 GdkWindow *window = gtk_tree_view_get_bin_window(m_treeview);
1071 gdk_window_set_cursor( window, cursor.GetCursor() );
1072 }
1073
1074 if (wxUpdateUIEvent::CanUpdate(this))
1075 UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
1076 }
1077
1078 wxSize wxListBox::DoGetBestSize() const
1079 {
1080 wxCHECK_MSG(m_treeview, wxDefaultSize, wxT("invalid tree view"));
1081
1082 // Start with a minimum size that's not too small
1083 int cx, cy;
1084 GetTextExtent( wxT("X"), &cx, &cy);
1085 int lbWidth = 3 * cx;
1086 int lbHeight = 10;
1087
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();
1091 if (count)
1092 {
1093 int wLine;
1094
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);
1100 }
1101
1102 lbWidth += 3 * cx;
1103
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 )
1108 {
1109 lbWidth += 35;
1110 cy = cy > 25 ? cy : 25; // rough height of checkbox
1111 }
1112 #endif
1113
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);
1117 }
1118
1119 // Add room for the scrollbar
1120 lbWidth += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
1121
1122 wxSize best(lbWidth, lbHeight);
1123 CacheBestSize(best);
1124 return best;
1125 }
1126
1127 // static
1128 wxVisualAttributes
1129 wxListBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
1130 {
1131 return GetDefaultAttributesFromGTKWidget(gtk_tree_view_new, true);
1132 }
1133
1134 #endif // wxUSE_LISTBOX