removed the code to manually send wxNavigationEvents, it's not needed any more
[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 #if wxUSE_LISTBOX
15
16 #include "wx/listbox.h"
17
18 #ifndef WX_PRECOMP
19 #include "wx/dynarray.h"
20 #include "wx/intl.h"
21 #include "wx/log.h"
22 #include "wx/utils.h"
23 #include "wx/settings.h"
24 #include "wx/checklst.h"
25 #include "wx/arrstr.h"
26 #endif
27
28 #include "wx/gtk/private.h"
29 #include "wx/gtk/treeentry_gtk.h"
30
31 #if wxUSE_TOOLTIPS
32 #include "wx/tooltip.h"
33 #endif
34
35 #include <gdk/gdk.h>
36 #include <gtk/gtk.h>
37 #include <gdk/gdkkeysyms.h>
38
39 //-----------------------------------------------------------------------------
40 // data
41 //-----------------------------------------------------------------------------
42
43 extern bool g_blockEventsOnDrag;
44 extern bool g_blockEventsOnScroll;
45
46
47
48 //-----------------------------------------------------------------------------
49 // Macro to tell which row the strings are in (1 if native checklist, 0 if not)
50 //-----------------------------------------------------------------------------
51
52 #if wxUSE_CHECKLISTBOX
53 # define WXLISTBOX_DATACOLUMN_ARG(x) (x->m_hasCheckBoxes ? 1 : 0)
54 #else
55 # define WXLISTBOX_DATACOLUMN_ARG(x) (0)
56 #endif // wxUSE_CHECKLISTBOX
57
58 #define WXLISTBOX_DATACOLUMN WXLISTBOX_DATACOLUMN_ARG(this)
59
60 //-----------------------------------------------------------------------------
61 // "row-activated"
62 //-----------------------------------------------------------------------------
63
64 extern "C" {
65 static void
66 gtk_listbox_row_activated_callback(GtkTreeView *treeview,
67 GtkTreePath *path,
68 GtkTreeViewColumn *col,
69 wxListBox *listbox)
70 {
71 if (g_isIdle) wxapp_install_idle_handler();
72
73 if (g_blockEventsOnDrag) return;
74 if (g_blockEventsOnScroll) return;
75
76 // This is triggered by either a double-click or a space press
77
78 int sel = gtk_tree_path_get_indices(path)[0];
79
80 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, listbox->GetId() );
81 event.SetEventObject( listbox );
82
83 if (listbox->IsSelected(sel))
84 {
85 GtkTreeEntry* entry = listbox->GtkGetEntry(sel);
86
87 if (entry)
88 {
89 event.SetInt(sel);
90 event.SetString(wxConvUTF8.cMB2WX(gtk_tree_entry_get_label(entry)));
91
92 if ( listbox->HasClientObjectData() )
93 event.SetClientObject( (wxClientData*) gtk_tree_entry_get_userdata(entry) );
94 else if ( listbox->HasClientUntypedData() )
95 event.SetClientData( gtk_tree_entry_get_userdata(entry) );
96
97 g_object_unref (entry);
98 }
99 else
100 {
101 wxLogSysError(wxT("Internal error - could not get entry for double-click"));
102 event.SetInt(-1);
103 }
104 }
105 else
106 {
107 event.SetInt(-1);
108 }
109
110 listbox->GetEventHandler()->ProcessEvent( event );
111 }
112 }
113
114 //-----------------------------------------------------------------------------
115 // "changed"
116 //-----------------------------------------------------------------------------
117
118 extern "C" {
119 static void
120 gtk_listitem_changed_callback( GtkTreeSelection* selection, wxListBox *listbox )
121 {
122 if (g_blockEventsOnDrag) return;
123
124 if (listbox->m_blockEvent) return;
125
126 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, listbox->GetId() );
127 event.SetEventObject( listbox );
128
129 if (listbox->HasFlag(wxLB_MULTIPLE) || listbox->HasFlag(wxLB_EXTENDED))
130 {
131 wxArrayInt selections;
132 listbox->GetSelections( selections );
133
134 if (selections.GetCount() == 0)
135 {
136 // indicate that this is a deselection
137 event.SetExtraLong( 0 );
138 event.SetInt( -1 );
139
140 listbox->GetEventHandler()->ProcessEvent( event );
141
142 return;
143 }
144 else
145 {
146 // indicate that this is a selection
147 event.SetExtraLong( 1 );
148 event.SetInt( selections[0] );
149
150 listbox->GetEventHandler()->ProcessEvent( event );
151 }
152 }
153 else
154 {
155 int index = listbox->GetSelection();
156 if (index == wxNOT_FOUND)
157 {
158 // indicate that this is a deselection
159 event.SetExtraLong( 0 );
160 event.SetInt( -1 );
161
162 listbox->GetEventHandler()->ProcessEvent( event );
163
164 return;
165 }
166 else
167 {
168 GtkTreeEntry* entry = listbox->GtkGetEntry( index );
169
170 // indicate that this is a selection
171 event.SetExtraLong( 1 );
172
173 event.SetInt( index );
174 event.SetString(wxConvUTF8.cMB2WX(gtk_tree_entry_get_label(entry)));
175
176 if ( listbox->HasClientObjectData() )
177 event.SetClientObject(
178 (wxClientData*) gtk_tree_entry_get_userdata(entry)
179 );
180 else if ( listbox->HasClientUntypedData() )
181 event.SetClientData( gtk_tree_entry_get_userdata(entry) );
182
183 listbox->GetEventHandler()->ProcessEvent( event );
184
185 g_object_unref (entry);
186 }
187 }
188 }
189 }
190
191 //-----------------------------------------------------------------------------
192 // GtkTreeEntry destruction (to destroy client data)
193 //-----------------------------------------------------------------------------
194
195 extern "C" {
196 static void gtk_tree_entry_destroy_cb(GtkTreeEntry* entry,
197 wxListBox* listbox)
198 {
199 if (listbox->HasClientObjectData())
200 {
201 gpointer userdata = gtk_tree_entry_get_userdata(entry);
202 if (userdata)
203 delete (wxClientData *)userdata;
204 }
205 }
206 }
207
208 //-----------------------------------------------------------------------------
209 // Sorting callback (standard CmpNoCase return value)
210 //-----------------------------------------------------------------------------
211
212 extern "C" {
213 static gint gtk_listbox_sort_callback(GtkTreeModel *model,
214 GtkTreeIter *a,
215 GtkTreeIter *b,
216 wxListBox *listbox)
217 {
218 GtkTreeEntry* entry;
219 GtkTreeEntry* entry2;
220
221 gtk_tree_model_get(GTK_TREE_MODEL(listbox->m_liststore),
222 a,
223 WXLISTBOX_DATACOLUMN_ARG(listbox),
224 &entry, -1);
225 gtk_tree_model_get(GTK_TREE_MODEL(listbox->m_liststore),
226 b,
227 WXLISTBOX_DATACOLUMN_ARG(listbox),
228 &entry2, -1);
229 wxCHECK_MSG(entry, 0, wxT("Could not get entry"));
230 wxCHECK_MSG(entry2, 0, wxT("Could not get entry2"));
231
232 //We compare collate keys here instead of calling g_utf8_collate
233 //as it is rather slow (and even the docs reccommend this)
234 int ret = strcasecmp(gtk_tree_entry_get_collate_key(entry),
235 gtk_tree_entry_get_collate_key(entry2));
236
237 g_object_unref (entry);
238 g_object_unref (entry2);
239
240 return ret;
241 }
242 }
243
244 //-----------------------------------------------------------------------------
245 // Searching callback (TRUE == not equal, FALSE == equal)
246 //-----------------------------------------------------------------------------
247
248 extern "C" {
249 static gboolean gtk_listbox_searchequal_callback(GtkTreeModel* model,
250 gint column,
251 const gchar* key,
252 GtkTreeIter* iter,
253 wxListBox* listbox)
254 {
255 GtkTreeEntry* entry;
256
257 gtk_tree_model_get(GTK_TREE_MODEL(listbox->m_liststore),
258 iter,
259 WXLISTBOX_DATACOLUMN_ARG(listbox),
260 &entry, -1);
261 wxCHECK_MSG(entry, 0, wxT("Could not get entry"));
262 wxGtkString keycollatekey(g_utf8_collate_key(key, -1));
263
264 int ret = strcasecmp(keycollatekey,
265 gtk_tree_entry_get_collate_key(entry));
266
267 g_object_unref (entry);
268
269 return ret != 0;
270 }
271 }
272
273 //-----------------------------------------------------------------------------
274 // wxListBox
275 //-----------------------------------------------------------------------------
276
277 IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControl)
278
279 // ----------------------------------------------------------------------------
280 // construction
281 // ----------------------------------------------------------------------------
282
283 void wxListBox::Init()
284 {
285 m_treeview = (GtkTreeView*) NULL;
286 #if wxUSE_CHECKLISTBOX
287 m_hasCheckBoxes = false;
288 #endif // wxUSE_CHECKLISTBOX
289 }
290
291 bool wxListBox::Create( wxWindow *parent, wxWindowID id,
292 const wxPoint &pos, const wxSize &size,
293 const wxArrayString& choices,
294 long style, const wxValidator& validator,
295 const wxString &name )
296 {
297 wxCArrayString chs(choices);
298
299 return Create( parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
300 style, validator, name );
301 }
302
303 bool wxListBox::Create( wxWindow *parent, wxWindowID id,
304 const wxPoint &pos, const wxSize &size,
305 int n, const wxString choices[],
306 long style, const wxValidator& validator,
307 const wxString &name )
308 {
309 m_needParent = true;
310 m_blockEvent = false;
311
312 if (!PreCreation( parent, pos, size ) ||
313 !CreateBase( parent, id, pos, size, style, validator, name ))
314 {
315 wxFAIL_MSG( wxT("wxListBox creation failed") );
316 return false;
317 }
318
319 m_widget = gtk_scrolled_window_new( (GtkAdjustment*) NULL, (GtkAdjustment*) NULL );
320 if (style & wxLB_ALWAYS_SB)
321 {
322 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
323 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
324 }
325 else
326 {
327 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
328 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
329 }
330
331
332 GtkScrolledWindowSetBorder(m_widget, style);
333
334 m_treeview = GTK_TREE_VIEW( gtk_tree_view_new( ) );
335
336 //wxListBox doesn't have a header :)
337 //NB: If enabled SetFirstItem doesn't work correctly
338 gtk_tree_view_set_headers_visible(m_treeview, FALSE);
339
340 #if wxUSE_CHECKLISTBOX
341 if(m_hasCheckBoxes)
342 ((wxCheckListBox*)this)->DoCreateCheckList();
343 #endif // wxUSE_CHECKLISTBOX
344
345 // Create the data column
346 gtk_tree_view_insert_column_with_attributes(m_treeview, -1, "",
347 gtk_cell_renderer_text_new(),
348 "text",
349 WXLISTBOX_DATACOLUMN, NULL);
350
351 // Now create+set the model (GtkListStore) - first argument # of columns
352 #if wxUSE_CHECKLISTBOX
353 if(m_hasCheckBoxes)
354 m_liststore = gtk_list_store_new(2, G_TYPE_BOOLEAN,
355 GTK_TYPE_TREE_ENTRY);
356 else
357 #endif
358 m_liststore = gtk_list_store_new(1, GTK_TYPE_TREE_ENTRY);
359
360 gtk_tree_view_set_model(m_treeview, GTK_TREE_MODEL(m_liststore));
361
362 g_object_unref (m_liststore); //free on treeview destruction
363
364 // Disable the pop-up textctrl that enables searching - note that
365 // the docs specify that even if this disabled (which we are doing)
366 // the user can still have it through the start-interactive-search
367 // key binding...either way we want to provide a searchequal callback
368 // NB: If this is enabled a doubleclick event (activate) gets sent
369 // on a successful search
370 gtk_tree_view_set_search_column(m_treeview, WXLISTBOX_DATACOLUMN);
371 gtk_tree_view_set_search_equal_func(m_treeview,
372 (GtkTreeViewSearchEqualFunc) gtk_listbox_searchequal_callback,
373 this,
374 NULL);
375
376 gtk_tree_view_set_enable_search(m_treeview, FALSE);
377
378
379 GtkTreeSelection* selection = gtk_tree_view_get_selection( m_treeview );
380
381 g_signal_connect_after (selection, "changed",
382 G_CALLBACK (gtk_listitem_changed_callback), this);
383
384 GtkSelectionMode mode;
385 if (style & wxLB_MULTIPLE)
386 {
387 mode = GTK_SELECTION_MULTIPLE;
388 }
389 else if (style & wxLB_EXTENDED)
390 {
391 mode = GTK_SELECTION_EXTENDED;
392 }
393 else
394 {
395 // if style was 0 set single mode
396 m_windowStyle |= wxLB_SINGLE;
397 mode = GTK_SELECTION_SINGLE;
398 }
399
400 gtk_tree_selection_set_mode( selection, mode );
401
402 // Handle sortable stuff
403 if(style & wxLB_SORT)
404 {
405 // Setup sorting in ascending (wx) order
406 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(m_liststore),
407 WXLISTBOX_DATACOLUMN,
408 GTK_SORT_ASCENDING);
409
410 // Set the sort callback
411 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(m_liststore),
412 WXLISTBOX_DATACOLUMN,
413 (GtkTreeIterCompareFunc) gtk_listbox_sort_callback,
414 this, //userdata
415 NULL //"destroy notifier"
416 );
417 }
418
419
420 gtk_container_add (GTK_CONTAINER (m_widget), GTK_WIDGET(m_treeview) );
421
422 gtk_widget_show( GTK_WIDGET(m_treeview) );
423 m_focusWidget = GTK_WIDGET(m_treeview);
424
425 wxListBox::DoInsertItems(wxArrayString(n, choices), 0); // insert initial items
426
427 // generate dclick events
428 g_signal_connect_after(m_treeview, "row-activated",
429 G_CALLBACK(gtk_listbox_row_activated_callback), this);
430
431 m_parent->DoAddChild( this );
432
433 PostCreation(size);
434 SetInitialSize(size); // need this too because this is a wxControlWithItems
435
436 return true;
437 }
438
439 wxListBox::~wxListBox()
440 {
441 m_hasVMT = false;
442
443 Clear();
444 }
445
446 // ----------------------------------------------------------------------------
447 // adding items
448 // ----------------------------------------------------------------------------
449
450 void wxListBox::GtkInsertItems(const wxArrayString& items,
451 void** clientData, unsigned int pos)
452 {
453 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
454
455 InvalidateBestSize();
456
457 // Create and set column ids and GValues
458
459 unsigned int nNum = items.GetCount();
460 unsigned int nCurCount = wxListBox::GetCount();
461 wxASSERT_MSG(pos <= nCurCount, wxT("Invalid index passed to wxListBox"));
462
463 GtkTreeIter* pIter = NULL; // append by default
464 GtkTreeIter iter;
465 if (pos != nCurCount)
466 {
467 wxCHECK_RET( GtkGetIteratorFor(pos, &iter),
468 wxT("internal wxListBox error in insertion") );
469
470 pIter = &iter;
471 }
472
473 for (unsigned int i = 0; i < nNum; ++i)
474 {
475 wxString label = items[i];
476
477 GtkTreeEntry* entry = gtk_tree_entry_new();
478 gtk_tree_entry_set_label(entry, wxGTK_CONV(label));
479 gtk_tree_entry_set_destroy_func(entry,
480 (GtkTreeEntryDestroy)gtk_tree_entry_destroy_cb,
481 this);
482
483 if (clientData)
484 gtk_tree_entry_set_userdata(entry, clientData[i]);
485
486 GtkTreeIter itercur;
487 gtk_list_store_insert_before(m_liststore, &itercur, pIter);
488
489 GtkSetItem(itercur, entry);
490
491 g_object_unref (entry);
492 }
493 }
494
495 void wxListBox::DoInsertItems(const wxArrayString& items, unsigned int pos)
496 {
497 wxCHECK_RET( IsValidInsert(pos), wxT("invalid index in wxListBox::InsertItems") );
498
499 GtkInsertItems(items, NULL, pos);
500 }
501
502 int wxListBox::DoAppend( const wxString& item )
503 {
504 wxCHECK_MSG( m_treeview != NULL, -1, wxT("invalid listbox") );
505
506 InvalidateBestSize();
507
508 GtkTreeEntry* entry = gtk_tree_entry_new();
509 gtk_tree_entry_set_label( entry, wxGTK_CONV(item) );
510 gtk_tree_entry_set_destroy_func(entry,
511 (GtkTreeEntryDestroy)gtk_tree_entry_destroy_cb,
512 this);
513
514 GtkTreeIter itercur;
515 gtk_list_store_insert_before( m_liststore, &itercur, NULL );
516
517 GtkSetItem(itercur, entry);
518
519 g_object_unref (entry);
520
521 return GtkGetIndexFor(itercur);
522 }
523
524 void wxListBox::DoSetItems( const wxArrayString& items,
525 void **clientData)
526 {
527 Clear();
528 GtkInsertItems(items, clientData, 0);
529 }
530
531 // ----------------------------------------------------------------------------
532 // deleting items
533 // ----------------------------------------------------------------------------
534
535 void wxListBox::Clear()
536 {
537 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
538
539 InvalidateBestSize();
540
541 gtk_list_store_clear( m_liststore ); /* well, THAT was easy :) */
542 }
543
544 void wxListBox::Delete(unsigned int n)
545 {
546 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
547
548 InvalidateBestSize();
549
550 GtkTreeIter iter;
551 wxCHECK_RET( GtkGetIteratorFor(n, &iter), wxT("wrong listbox index") );
552
553 // this returns false if iter is invalid (e.g. deleting item at end) but
554 // since we don't use iter, we ignore the return value
555 gtk_list_store_remove(m_liststore, &iter);
556 }
557
558 // ----------------------------------------------------------------------------
559 // helper functions for working with iterators
560 // ----------------------------------------------------------------------------
561
562 bool wxListBox::GtkGetIteratorFor(unsigned pos, GtkTreeIter *iter) const
563 {
564 if ( !gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(m_liststore),
565 iter, NULL, pos) )
566 {
567 wxLogDebug(wxT("gtk_tree_model_iter_nth_child(%u) failed"), pos);
568 return false;
569 }
570
571 return true;
572 }
573
574 int wxListBox::GtkGetIndexFor(GtkTreeIter& iter) const
575 {
576 GtkTreePath *path =
577 gtk_tree_model_get_path(GTK_TREE_MODEL(m_liststore), &iter);
578
579 gint* pIntPath = gtk_tree_path_get_indices(path);
580
581 wxCHECK_MSG( pIntPath, wxNOT_FOUND, _T("failed to get iterator path") );
582
583 int idx = pIntPath[0];
584
585 gtk_tree_path_free( path );
586
587 return idx;
588 }
589
590 // get GtkTreeEntry from position (note: you need to g_unref it if valid)
591 GtkTreeEntry *wxListBox::GtkGetEntry(unsigned n) const
592 {
593 GtkTreeIter iter;
594 if ( !GtkGetIteratorFor(n, &iter) )
595 return NULL;
596
597
598 GtkTreeEntry* entry = NULL;
599 gtk_tree_model_get(GTK_TREE_MODEL(m_liststore), &iter,
600 WXLISTBOX_DATACOLUMN, &entry, -1);
601
602 return entry;
603 }
604
605 void wxListBox::GtkSetItem(GtkTreeIter& iter, const GtkTreeEntry *entry)
606 {
607 #if wxUSE_CHECKLISTBOX
608 if ( m_hasCheckBoxes )
609 {
610 gtk_list_store_set(m_liststore, &iter,
611 0, FALSE, // FALSE == not toggled
612 1, entry,
613 -1);
614 }
615 else
616 #endif // wxUSE_CHECKLISTBOX
617 {
618 gtk_list_store_set(m_liststore, &iter, 0, entry, -1);
619 }
620 }
621
622 // ----------------------------------------------------------------------------
623 // client data
624 // ----------------------------------------------------------------------------
625
626 void* wxListBox::DoGetItemClientData(unsigned int n) const
627 {
628 wxCHECK_MSG( IsValid(n), NULL,
629 wxT("Invalid index passed to GetItemClientData") );
630
631 GtkTreeEntry* entry = GtkGetEntry(n);
632 wxCHECK_MSG(entry, NULL, wxT("could not get entry"));
633
634 void* userdata = gtk_tree_entry_get_userdata( entry );
635 g_object_unref (entry);
636 return userdata;
637 }
638
639 wxClientData* wxListBox::DoGetItemClientObject(unsigned int n) const
640 {
641 return (wxClientData*) wxListBox::DoGetItemClientData(n);
642 }
643
644 void wxListBox::DoSetItemClientData(unsigned int n, void* clientData)
645 {
646 wxCHECK_RET( IsValid(n),
647 wxT("Invalid index passed to SetItemClientData") );
648
649 GtkTreeEntry* entry = GtkGetEntry(n);
650 wxCHECK_RET(entry, wxT("could not get entry"));
651
652 gtk_tree_entry_set_userdata( entry, clientData );
653 g_object_unref (entry);
654 }
655
656 void wxListBox::DoSetItemClientObject(unsigned int n, wxClientData* clientData)
657 {
658 // wxItemContainer already deletes data for us
659 wxListBox::DoSetItemClientData(n, (void*) clientData);
660 }
661
662 // ----------------------------------------------------------------------------
663 // string list access
664 // ----------------------------------------------------------------------------
665
666 void wxListBox::SetString(unsigned int n, const wxString& label)
667 {
668 wxCHECK_RET( IsValid(n), wxT("invalid index in wxListBox::SetString") );
669 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
670
671 GtkTreeEntry* entry = GtkGetEntry(n);
672 wxCHECK_RET( entry, wxT("wrong listbox index") );
673
674 // update the item itself
675 gtk_tree_entry_set_label(entry, wxGTK_CONV(label));
676
677 // and update the model which will refresh the tree too
678 GtkTreeIter iter;
679 wxCHECK_RET( GtkGetIteratorFor(n, &iter), _T("failed to get iterator") );
680
681 // FIXME: this resets the checked status of a wxCheckListBox item
682
683 GtkSetItem(iter, entry);
684 }
685
686 wxString wxListBox::GetString(unsigned int n) const
687 {
688 wxCHECK_MSG( m_treeview != NULL, wxEmptyString, wxT("invalid listbox") );
689
690 GtkTreeEntry* entry = GtkGetEntry(n);
691 wxCHECK_MSG( entry, wxEmptyString, wxT("wrong listbox index") );
692
693 wxString label = wxGTK_CONV_BACK( gtk_tree_entry_get_label(entry) );
694
695 g_object_unref (entry);
696 return label;
697 }
698
699 unsigned int wxListBox::GetCount() const
700 {
701 wxCHECK_MSG( m_treeview != NULL, 0, wxT("invalid listbox") );
702
703 return (unsigned int)gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_liststore), NULL);
704 }
705
706 int wxListBox::FindString( const wxString &item, bool bCase ) const
707 {
708 wxCHECK_MSG( m_treeview != NULL, wxNOT_FOUND, wxT("invalid listbox") );
709
710 //Sort of hackish - maybe there is a faster way
711 unsigned int nCount = wxListBox::GetCount();
712
713 for(unsigned int i = 0; i < nCount; ++i)
714 {
715 if( item.IsSameAs( wxListBox::GetString(i), bCase ) )
716 return (int)i;
717 }
718
719
720 // it's not an error if the string is not found -> no wxCHECK
721 return wxNOT_FOUND;
722 }
723
724 // ----------------------------------------------------------------------------
725 // selection
726 // ----------------------------------------------------------------------------
727
728 int wxListBox::GetSelection() const
729 {
730 wxCHECK_MSG( m_treeview != NULL, wxNOT_FOUND, wxT("invalid listbox"));
731 wxCHECK_MSG( HasFlag(wxLB_SINGLE), wxNOT_FOUND,
732 wxT("must be single selection listbox"));
733
734 GtkTreeIter iter;
735 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
736
737 // only works on single-sel
738 if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
739 return wxNOT_FOUND;
740
741 return GtkGetIndexFor(iter);
742 }
743
744 int wxListBox::GetSelections( wxArrayInt& aSelections ) const
745 {
746 wxCHECK_MSG( m_treeview != NULL, wxNOT_FOUND, wxT("invalid listbox") );
747
748 aSelections.Empty();
749
750 int i = 0;
751 GtkTreeIter iter;
752 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
753
754 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(m_liststore), &iter))
755 { //gtk_tree_selection_get_selected_rows is GTK 2.2+ so iter instead
756 do
757 {
758 if (gtk_tree_selection_iter_is_selected(selection, &iter))
759 aSelections.Add(i);
760
761 i++;
762 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(m_liststore), &iter));
763 }
764
765 return aSelections.GetCount();
766 }
767
768 bool wxListBox::IsSelected( int n ) const
769 {
770 wxCHECK_MSG( m_treeview != NULL, false, wxT("invalid listbox") );
771
772 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
773
774 GtkTreeIter iter;
775 wxCHECK_MSG( GtkGetIteratorFor(n, &iter), false, wxT("Invalid index") );
776
777 return gtk_tree_selection_iter_is_selected(selection, &iter);
778 }
779
780 void wxListBox::DoSetSelection( int n, bool select )
781 {
782 // passing -1 to SetSelection() is documented to deselect all items
783 if ( n == wxNOT_FOUND )
784 {
785 // ... and not generate any events in the process
786 GtkDeselectAll();
787 return;
788 }
789
790 wxCHECK_RET( IsValid(n), wxT("invalid index in wxListBox::SetSelection") );
791
792 // don't generate the selection event
793 GtkSetSelection(n, select, true);
794 }
795
796 void wxListBox::GtkDeselectAll()
797 {
798 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
799
800 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
801
802 m_blockEvent = true;
803
804 gtk_tree_selection_unselect_all(selection);
805
806 m_blockEvent = false;
807 }
808
809 void wxListBox::GtkSetSelection(int n, const bool select, const bool blockEvent)
810 {
811 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
812
813 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
814
815 GtkTreeIter iter;
816 wxCHECK_RET( GtkGetIteratorFor(n, &iter), wxT("Invalid index") );
817
818 m_blockEvent = blockEvent;
819
820 if (select)
821 gtk_tree_selection_select_iter(selection, &iter);
822 else
823 gtk_tree_selection_unselect_iter(selection, &iter);
824
825 m_blockEvent = false;
826 }
827
828 void wxListBox::DoSetFirstItem( int n )
829 {
830 wxCHECK_RET( m_treeview, wxT("invalid listbox") );
831 wxCHECK_RET( IsValid(n), wxT("invalid index"));
832
833 //RN: I have no idea why this line is needed...
834 if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (m_treeview))
835 return;
836
837 GtkTreeIter iter;
838 if ( !GtkGetIteratorFor(n, &iter) )
839 return;
840
841 GtkTreePath* path = gtk_tree_model_get_path(
842 GTK_TREE_MODEL(m_liststore), &iter);
843
844 // Scroll to the desired cell (0.0 == topleft alignment)
845 gtk_tree_view_scroll_to_cell(m_treeview, path, NULL,
846 TRUE, 0.0f, 0.0f);
847
848 gtk_tree_path_free(path);
849 }
850
851 // ----------------------------------------------------------------------------
852 // hittest
853 // ----------------------------------------------------------------------------
854
855 int wxListBox::DoListHitTest(const wxPoint& point) const
856 {
857 // gtk_tree_view_get_path_at_pos() also gets items that are not visible and
858 // we only want visible items we need to check for it manually here
859 if ( !GetClientRect().Contains(point) )
860 return wxNOT_FOUND;
861
862 // need to translate from master window since it is in client coords
863 gint binx, biny;
864 gdk_window_get_geometry(gtk_tree_view_get_bin_window(m_treeview),
865 &binx, &biny, NULL, NULL, NULL);
866
867 GtkTreePath* path;
868 if ( !gtk_tree_view_get_path_at_pos
869 (
870 m_treeview,
871 point.x - binx,
872 point.y - biny,
873 &path,
874 NULL, // [out] column (always 0 here)
875 NULL, // [out] x-coord relative to the cell (not interested)
876 NULL // [out] y-coord relative to the cell
877 ) )
878 {
879 return wxNOT_FOUND;
880 }
881
882 int index = gtk_tree_path_get_indices(path)[0];
883 gtk_tree_path_free(path);
884
885 return index;
886 }
887
888 // ----------------------------------------------------------------------------
889 // helpers
890 // ----------------------------------------------------------------------------
891
892 #if wxUSE_TOOLTIPS
893 void wxListBox::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
894 {
895 // RN: Is this needed anymore?
896 gtk_tooltips_set_tip( tips, GTK_WIDGET( m_treeview ), wxGTK_CONV(tip), (gchar*) NULL );
897 }
898 #endif // wxUSE_TOOLTIPS
899
900 GtkWidget *wxListBox::GetConnectWidget()
901 {
902 // the correct widget for listbox events (such as mouse clicks for example)
903 // is m_treeview, not the parent scrolled window
904 return GTK_WIDGET(m_treeview);
905 }
906
907 GdkWindow *wxListBox::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
908 {
909 return gtk_tree_view_get_bin_window(m_treeview);
910 }
911
912 void wxListBox::DoApplyWidgetStyle(GtkRcStyle *style)
913 {
914 if (m_hasBgCol && m_backgroundColour.Ok())
915 {
916 GdkWindow *window = gtk_tree_view_get_bin_window(m_treeview);
917 if (window)
918 {
919 m_backgroundColour.CalcPixel( gdk_drawable_get_colormap( window ) );
920 gdk_window_set_background( window, m_backgroundColour.GetColor() );
921 gdk_window_clear( window );
922 }
923 }
924
925 gtk_widget_modify_style( GTK_WIDGET(m_treeview), style );
926 }
927
928 wxSize wxListBox::DoGetBestSize() const
929 {
930 wxCHECK_MSG(m_treeview, wxDefaultSize, wxT("invalid tree view"));
931
932 // Start with a minimum size that's not too small
933 int cx, cy;
934 GetTextExtent( wxT("X"), &cx, &cy);
935 int lbWidth = 3 * cx;
936 int lbHeight = 10;
937
938 // Get the visible area of the tree view (limit to the 10th item
939 // so that it isn't too big)
940 unsigned int count = GetCount();
941 if (count)
942 {
943 int wLine;
944
945 // Find the widest line
946 for(unsigned int i = 0; i < count; i++) {
947 wxString str(GetString(i));
948 GetTextExtent(str, &wLine, NULL);
949 lbWidth = wxMax(lbWidth, wLine);
950 }
951
952 lbWidth += 3 * cx;
953
954 // And just a bit more for the checkbox if present and then some
955 // (these are rough guesses)
956 #if wxUSE_CHECKLISTBOX
957 if ( m_hasCheckBoxes )
958 {
959 lbWidth += 35;
960 cy = cy > 25 ? cy : 25; // rough height of checkbox
961 }
962 #endif
963
964 // don't make the listbox too tall (limit height to around 10 items) but don't
965 // make it too small neither
966 lbHeight = (cy+4) * wxMin(wxMax(count, 3), 10);
967 }
968
969 // Add room for the scrollbar
970 lbWidth += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
971
972 wxSize best(lbWidth, lbHeight);
973 CacheBestSize(best);
974 return best;
975 }
976
977 // static
978 wxVisualAttributes
979 wxListBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
980 {
981 return GetDefaultAttributesFromGTKWidget(gtk_tree_view_new, true);
982 }
983
984 #endif // wxUSE_LISTBOX