Upported wxListBox key handling and SetFirstItem()
[wxWidgets.git] / src / gtk1 / listbox.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: listbox.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10
11 #ifdef __GNUG__
12 #pragma implementation "listbox.h"
13 #endif
14
15 #ifdef __VMS
16 #define gtk_scrolled_window_add_with_viewport gtk_scrolled_window_add_with_vi
17 #define gtk_container_set_focus_vadjustment gtk_container_set_focus_vadjust
18 #define gtk_scrolled_window_get_vadjustment gtk_scrolled_window_get_vadjust
19 #endif
20
21 #include "wx/listbox.h"
22
23 #if wxUSE_LISTBOX
24
25 #include "wx/dynarray.h"
26 #include "wx/utils.h"
27 #include "wx/intl.h"
28 #include "wx/checklst.h"
29 #include "wx/settings.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 // idle system
41 //-----------------------------------------------------------------------------
42
43 extern void wxapp_install_idle_handler();
44 extern bool g_isIdle;
45
46 //-------------------------------------------------------------------------
47 // conditional compilation
48 //-------------------------------------------------------------------------
49
50 #if (GTK_MINOR_VERSION > 0)
51 #define NEW_GTK_SCROLL_CODE
52 #endif
53
54 //-----------------------------------------------------------------------------
55 // private functions
56 //-----------------------------------------------------------------------------
57
58 #if wxUSE_CHECKLISTBOX
59
60 #define CHECKBOX_STRING "[-] "
61
62 // checklistboxes have "[±] " prepended to their lables, this macro removes it
63 // (NB: 4 below is the length of CHECKBOX_STRING above)
64 //
65 // the argument to it is a "const char *" pointer
66 #define GET_REAL_LABEL(label) ((m_hasCheckBoxes)?(label)+4 : (label))
67
68 #else // !wxUSE_CHECKLISTBOX
69
70 #define GET_REAL_LABEL(label) (label)
71
72 #endif // wxUSE_CHECKLISTBOX
73
74 //-----------------------------------------------------------------------------
75 // data
76 //-----------------------------------------------------------------------------
77
78 extern bool g_blockEventsOnDrag;
79 extern bool g_blockEventsOnScroll;
80 extern wxCursor g_globalCursor;
81
82 static bool g_hasDoubleClicked = FALSE;
83
84 //-----------------------------------------------------------------------------
85 // idle callback for SetFirstItem
86 //-----------------------------------------------------------------------------
87
88 struct wxlistbox_idle_struct
89 {
90 wxListBox *m_listbox;
91 int m_item;
92 gint m_tag;
93 };
94
95 static gint wxlistbox_idle_callback( gpointer gdata )
96 {
97 wxlistbox_idle_struct* data = (wxlistbox_idle_struct*) gdata;
98 gdk_threads_enter();
99
100 gtk_idle_remove( data->m_tag );
101
102 data->m_listbox->SetFirstItem( data->m_item );
103
104 delete data;
105
106 gdk_threads_leave();
107
108 return TRUE;
109 }
110
111 //-----------------------------------------------------------------------------
112 // "button_release_event"
113 //-----------------------------------------------------------------------------
114
115 /* we would normally emit a wxEVT_COMMAND_LISTBOX_DOUBLECLICKED event once
116 a GDK_2BUTTON_PRESS occurs, but this has the particular problem of the
117 listbox keeping the focus until it receives a GDK_BUTTON_RELEASE event.
118 this can lead to race conditions so that we emit the dclick event
119 after the GDK_BUTTON_RELEASE event after the GDK_2BUTTON_PRESS event */
120
121 static gint
122 gtk_listbox_button_release_callback( GtkWidget * WXUNUSED(widget),
123 GdkEventButton * WXUNUSED(gdk_event),
124 wxListBox *listbox )
125 {
126 if (g_isIdle) wxapp_install_idle_handler();
127
128 if (g_blockEventsOnDrag) return FALSE;
129 if (g_blockEventsOnScroll) return FALSE;
130
131 if (!listbox->m_hasVMT) return FALSE;
132
133 if (!g_hasDoubleClicked) return FALSE;
134
135 wxCommandEvent event( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, listbox->GetId() );
136 event.SetEventObject( listbox );
137
138 wxArrayInt aSelections;
139 int n, count = listbox->GetSelections(aSelections);
140 if ( count > 0 )
141 {
142 n = aSelections[0];
143 if ( listbox->HasClientObjectData() )
144 event.SetClientObject( listbox->GetClientObject(n) );
145 else if ( listbox->HasClientUntypedData() )
146 event.SetClientData( listbox->GetClientData(n) );
147 event.SetString( listbox->GetString(n) );
148 }
149 else
150 {
151 n = -1;
152 }
153
154 event.m_commandInt = n;
155
156 listbox->GetEventHandler()->ProcessEvent( event );
157
158 return FALSE;
159 }
160
161 //-----------------------------------------------------------------------------
162 // "button_press_event"
163 //-----------------------------------------------------------------------------
164
165 static gint
166 gtk_listbox_button_press_callback( GtkWidget *widget,
167 GdkEventButton *gdk_event,
168 wxListBox *listbox )
169 {
170 if (g_isIdle) wxapp_install_idle_handler();
171
172 if (g_blockEventsOnDrag) return FALSE;
173 if (g_blockEventsOnScroll) return FALSE;
174
175 if (!listbox->m_hasVMT) return FALSE;
176
177 int sel = listbox->GtkGetIndex( widget );
178
179 #if wxUSE_CHECKLISTBOX
180 if ((listbox->m_hasCheckBoxes) && (gdk_event->x < 15) && (gdk_event->type != GDK_2BUTTON_PRESS))
181 {
182 wxCheckListBox *clb = (wxCheckListBox *)listbox;
183
184 clb->Check( sel, !clb->IsChecked(sel) );
185
186 wxCommandEvent event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, listbox->GetId() );
187 event.SetEventObject( listbox );
188 event.SetInt( sel );
189 listbox->GetEventHandler()->ProcessEvent( event );
190 }
191 #endif // wxUSE_CHECKLISTBOX
192
193 /* emit wxEVT_COMMAND_LISTBOX_DOUBLECLICKED later */
194 g_hasDoubleClicked = (gdk_event->type == GDK_2BUTTON_PRESS);
195
196 return FALSE;
197 }
198
199 //-----------------------------------------------------------------------------
200 // "key_press_event"
201 //-----------------------------------------------------------------------------
202
203 static gint
204 gtk_listbox_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxListBox *listbox )
205 {
206 if (g_isIdle)
207 wxapp_install_idle_handler();
208
209 if (g_blockEventsOnDrag)
210 return FALSE;
211
212 bool ret = FALSE;
213
214 if ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab))
215 {
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 );
223 }
224
225 if ((gdk_event->keyval == GDK_Return) && (!ret))
226 {
227 // eat return in all modes
228 ret = TRUE;
229 }
230
231 #if wxUSE_CHECKLISTBOX
232 if ((gdk_event->keyval == ' ') && (listbox->m_hasCheckBoxes) && (!ret))
233 {
234 int sel = listbox->GtkGetIndex( widget );
235
236 wxCheckListBox *clb = (wxCheckListBox *)listbox;
237
238 clb->Check( sel, !clb->IsChecked(sel) );
239
240 wxCommandEvent new_event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, listbox->GetId() );
241 new_event.SetEventObject( listbox );
242 new_event.SetInt( sel );
243 ret = listbox->GetEventHandler()->ProcessEvent( new_event );
244 }
245 #endif // wxUSE_CHECKLISTBOX
246
247 if (ret)
248 {
249 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
250 return TRUE;
251 }
252
253 return FALSE;
254 }
255
256 //-----------------------------------------------------------------------------
257 // "select" and "deselect"
258 //-----------------------------------------------------------------------------
259
260 static void gtk_listitem_select_callback( GtkWidget *WXUNUSED(widget), wxListBox *listbox );
261
262 static void gtk_listitem_deselect_callback( GtkWidget *widget, wxListBox *listbox )
263 {
264 gtk_listitem_select_callback( widget, listbox );
265 }
266
267 static void gtk_listitem_select_callback( GtkWidget *WXUNUSED(widget), wxListBox *listbox )
268 {
269 if (g_isIdle) wxapp_install_idle_handler();
270
271 if (!listbox->m_hasVMT) return;
272 if (g_blockEventsOnDrag) return;
273
274 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, listbox->GetId() );
275 event.SetEventObject( listbox );
276
277 wxArrayInt aSelections;
278 int n, count = listbox->GetSelections(aSelections);
279 if ( count > 0 )
280 {
281 n = aSelections[0];
282 if ( listbox->HasClientObjectData() )
283 event.SetClientObject( listbox->GetClientObject(n) );
284 else if ( listbox->HasClientUntypedData() )
285 event.SetClientData( listbox->GetClientData(n) );
286 event.SetString( listbox->GetString(n) );
287 }
288 else
289 {
290 n = -1;
291 }
292
293 event.m_commandInt = n;
294
295 listbox->GetEventHandler()->AddPendingEvent( event );
296 // listbox->GetEventHandler()->ProcessEvent( event );
297 }
298
299 //-----------------------------------------------------------------------------
300 // wxListBox
301 //-----------------------------------------------------------------------------
302
303 IMPLEMENT_DYNAMIC_CLASS(wxListBox,wxControl)
304
305 // ----------------------------------------------------------------------------
306 // construction
307 // ----------------------------------------------------------------------------
308
309 wxListBox::wxListBox()
310 {
311 m_list = (GtkList *) NULL;
312 #if wxUSE_CHECKLISTBOX
313 m_hasCheckBoxes = FALSE;
314 #endif // wxUSE_CHECKLISTBOX
315 }
316
317 bool wxListBox::Create( wxWindow *parent, wxWindowID id,
318 const wxPoint &pos, const wxSize &size,
319 int n, const wxString choices[],
320 long style, const wxValidator& validator,
321 const wxString &name )
322 {
323 m_needParent = TRUE;
324 m_acceptsFocus = TRUE;
325
326 if (!PreCreation( parent, pos, size ) ||
327 !CreateBase( parent, id, pos, size, style, validator, name ))
328 {
329 wxFAIL_MSG( wxT("wxListBox creation failed") );
330 return FALSE;
331 }
332
333 m_widget = gtk_scrolled_window_new( (GtkAdjustment*) NULL, (GtkAdjustment*) NULL );
334 if (style & wxLB_ALWAYS_SB)
335 {
336 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
337 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
338 }
339 else
340 {
341 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
342 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
343 }
344
345 m_list = GTK_LIST( gtk_list_new() );
346
347 GtkSelectionMode mode;
348 if (style & wxLB_MULTIPLE)
349 {
350 mode = GTK_SELECTION_MULTIPLE;
351 }
352 else if (style & wxLB_EXTENDED)
353 {
354 mode = GTK_SELECTION_EXTENDED;
355 }
356 else
357 {
358 // if style was 0 set single mode
359 m_windowStyle |= wxLB_SINGLE;
360 mode = GTK_SELECTION_BROWSE;
361 }
362
363 gtk_list_set_selection_mode( GTK_LIST(m_list), mode );
364
365 #ifdef NEW_GTK_SCROLL_CODE
366 gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(m_widget), GTK_WIDGET(m_list) );
367 #else
368 gtk_container_add( GTK_CONTAINER(m_widget), GTK_WIDGET(m_list) );
369 #endif
370
371 /* make list scroll when moving the focus down using cursor keys */
372 gtk_container_set_focus_vadjustment(
373 GTK_CONTAINER(m_list),
374 gtk_scrolled_window_get_vadjustment(
375 GTK_SCROLLED_WINDOW(m_widget)));
376
377 gtk_widget_show( GTK_WIDGET(m_list) );
378
379 SetSizeOrDefault( size );
380
381 if ( style & wxLB_SORT )
382 {
383 // this will change DoAppend() behaviour
384 m_strings = new wxSortedArrayString;
385 }
386 else
387 {
388 m_strings = (wxSortedArrayString *)NULL;
389 }
390
391 for (int i = 0; i < n; i++)
392 {
393 // add one by one
394 DoAppend(choices[i]);
395 }
396
397 m_parent->DoAddChild( this );
398
399 PostCreation();
400
401 SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_LISTBOX ) );
402 SetForegroundColour( parent->GetForegroundColour() );
403 SetFont( parent->GetFont() );
404
405 Show( TRUE );
406
407 return TRUE;
408 }
409
410 wxListBox::~wxListBox()
411 {
412 m_hasVMT = FALSE;
413
414 Clear();
415
416 if (m_strings)
417 delete m_strings;
418 }
419
420 void wxListBox::DoInsertItems(const wxArrayString& items, int pos)
421 {
422 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
423
424 // VZ: notice that InsertItems knows nothing about sorting, so calling it
425 // from outside (and not from our own Append) is likely to break
426 // everything
427
428 // code elsewhere supposes we have as many items in m_clientList as items
429 // in the listbox
430 wxASSERT_MSG( m_clientList.GetCount() == (size_t)GetCount(),
431 wxT("bug in client data management") );
432
433 GList *children = m_list->children;
434 int length = g_list_length(children);
435
436 wxCHECK_RET( pos <= length, wxT("invalid index in wxListBox::InsertItems") );
437
438 size_t nItems = items.GetCount();
439
440 if (pos == length)
441 {
442 for ( size_t n = 0; n < nItems; n++ )
443 {
444 GtkAddItem( items[n] );
445
446 m_clientList.Append((wxObject *)NULL);
447 }
448 }
449 else
450 {
451 wxNode *node = m_clientList.Nth( pos );
452 for ( size_t n = 0; n < nItems; n++ )
453 {
454 GtkAddItem( items[n], pos+n );
455
456 m_clientList.Insert( node, (wxObject *)NULL );
457 }
458 }
459
460 wxASSERT_MSG( m_clientList.GetCount() == (size_t)GetCount(),
461 wxT("bug in client data management") );
462 }
463
464 int wxListBox::DoAppend( const wxString& item )
465 {
466 if (m_strings)
467 {
468 // need to determine the index
469 int index = m_strings->Add( item );
470
471 // only if not at the end anyway
472 if (index != GetCount())
473 {
474 GtkAddItem( item, index );
475
476 wxNode *node = m_clientList.Nth( index );
477 m_clientList.Insert( node, (wxObject *)NULL );
478
479 return index;
480 }
481 }
482
483 GtkAddItem(item);
484
485 m_clientList.Append((wxObject *)NULL);
486
487 return GetCount() - 1;
488 }
489
490 void wxListBox::GtkAddItem( const wxString &item, int pos )
491 {
492 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
493
494 GtkWidget *list_item;
495
496 wxString label(item);
497 #if wxUSE_CHECKLISTBOX
498 if (m_hasCheckBoxes)
499 {
500 label.Prepend(CHECKBOX_STRING);
501 }
502 #endif // wxUSE_CHECKLISTBOX
503
504 list_item = gtk_list_item_new_with_label( label.mbc_str() );
505
506 GList *gitem_list = g_list_alloc ();
507 gitem_list->data = list_item;
508
509 if (pos == -1)
510 gtk_list_append_items( GTK_LIST (m_list), gitem_list );
511 else
512 gtk_list_insert_items( GTK_LIST (m_list), gitem_list, pos );
513
514 gtk_signal_connect( GTK_OBJECT(list_item), "select",
515 GTK_SIGNAL_FUNC(gtk_listitem_select_callback), (gpointer)this );
516
517 if (HasFlag(wxLB_MULTIPLE))
518 gtk_signal_connect( GTK_OBJECT(list_item), "deselect",
519 GTK_SIGNAL_FUNC(gtk_listitem_deselect_callback), (gpointer)this );
520
521 gtk_signal_connect( GTK_OBJECT(list_item),
522 "button_press_event",
523 (GtkSignalFunc)gtk_listbox_button_press_callback,
524 (gpointer) this );
525
526 gtk_signal_connect_after( GTK_OBJECT(list_item),
527 "button_release_event",
528 (GtkSignalFunc)gtk_listbox_button_release_callback,
529 (gpointer) this );
530
531 gtk_signal_connect( GTK_OBJECT(list_item),
532 "key_press_event",
533 (GtkSignalFunc)gtk_listbox_key_press_callback,
534 (gpointer)this );
535
536 ConnectWidget( list_item );
537
538 gtk_widget_show( list_item );
539
540 if (GTK_WIDGET_REALIZED(m_widget))
541 {
542 gtk_widget_realize( list_item );
543 gtk_widget_realize( GTK_BIN(list_item)->child );
544
545 // Apply current widget style to the new list_item
546 if (m_widgetStyle)
547 {
548 gtk_widget_set_style( GTK_WIDGET( list_item ), m_widgetStyle );
549 GtkBin *bin = GTK_BIN( list_item );
550 GtkWidget *label = GTK_WIDGET( bin->child );
551 gtk_widget_set_style( label, m_widgetStyle );
552 }
553
554 #if wxUSE_TOOLTIPS
555 if (m_tooltip) m_tooltip->Apply( this );
556 #endif
557 }
558 }
559
560 void wxListBox::DoSetItems( const wxArrayString& items,
561 void **clientData)
562 {
563 Clear();
564
565 DoInsertItems(items, 0);
566
567 if ( clientData )
568 {
569 size_t count = items.GetCount();
570 for ( size_t n = 0; n < count; n++ )
571 {
572 SetClientData(n, clientData[n]);
573 }
574 }
575 }
576
577 // ----------------------------------------------------------------------------
578 // client data
579 // ----------------------------------------------------------------------------
580
581 void wxListBox::DoSetItemClientData( int n, void* clientData )
582 {
583 wxCHECK_RET( m_widget != NULL, wxT("invalid listbox control") );
584
585 wxNode *node = m_clientList.Nth( n );
586 wxCHECK_RET( node, wxT("invalid index in wxListBox::DoSetItemClientData") );
587
588 node->SetData( (wxObject*) clientData );
589 }
590
591 void* wxListBox::DoGetItemClientData( int n ) const
592 {
593 wxCHECK_MSG( m_widget != NULL, NULL, wxT("invalid listbox control") );
594
595 wxNode *node = m_clientList.Nth( n );
596 wxCHECK_MSG( node, NULL, wxT("invalid index in wxListBox::DoGetItemClientData") );
597
598 return node->Data();
599 }
600
601 void wxListBox::DoSetItemClientObject( int n, wxClientData* clientData )
602 {
603 wxCHECK_RET( m_widget != NULL, wxT("invalid listbox control") );
604
605 wxNode *node = m_clientList.Nth( n );
606 wxCHECK_RET( node, wxT("invalid index in wxListBox::DoSetItemClientObject") );
607
608 wxClientData *cd = (wxClientData*) node->Data();
609 delete cd;
610
611 node->SetData( (wxObject*) clientData );
612 }
613
614 wxClientData* wxListBox::DoGetItemClientObject( int n ) const
615 {
616 wxCHECK_MSG( m_widget != NULL, (wxClientData*) NULL, wxT("invalid listbox control") );
617
618 wxNode *node = m_clientList.Nth( n );
619 wxCHECK_MSG( node, (wxClientData *)NULL,
620 wxT("invalid index in wxListBox::DoGetItemClientObject") );
621
622 return (wxClientData*) node->Data();
623 }
624
625 void wxListBox::Clear()
626 {
627 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
628
629 gtk_list_clear_items( m_list, 0, Number() );
630
631 if ( HasClientObjectData() )
632 {
633 // destroy the data (due to Robert's idea of using wxList<wxObject>
634 // and not wxList<wxClientData> we can't just say
635 // m_clientList.DeleteContents(TRUE) - this would crash!
636 wxNode *node = m_clientList.First();
637 while ( node )
638 {
639 delete (wxClientData *)node->Data();
640 node = node->Next();
641 }
642 }
643 m_clientList.Clear();
644
645 if ( m_strings )
646 m_strings->Clear();
647 }
648
649 void wxListBox::Delete( int n )
650 {
651 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
652
653 GList *child = g_list_nth( m_list->children, n );
654
655 wxCHECK_RET( child, wxT("wrong listbox index") );
656
657 GList *list = g_list_append( (GList*) NULL, child->data );
658 gtk_list_remove_items( m_list, list );
659 g_list_free( list );
660
661 wxNode *node = m_clientList.Nth( n );
662 if ( node )
663 {
664 if ( m_clientDataItemsType == ClientData_Object )
665 {
666 wxClientData *cd = (wxClientData*)node->Data();
667 delete cd;
668 }
669
670 m_clientList.DeleteNode( node );
671 }
672
673 if ( m_strings )
674 m_strings->Remove(n);
675 }
676
677 // ----------------------------------------------------------------------------
678 // string list access
679 // ----------------------------------------------------------------------------
680
681 void wxListBox::SetString( int n, const wxString &string )
682 {
683 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
684
685 GList *child = g_list_nth( m_list->children, n );
686 if (child)
687 {
688 GtkBin *bin = GTK_BIN( child->data );
689 GtkLabel *label = GTK_LABEL( bin->child );
690
691 wxString str;
692 #if wxUSE_CHECKLISTBOX
693 if (m_hasCheckBoxes)
694 str += CHECKBOX_STRING;
695 #endif // wxUSE_CHECKLISTBOX
696 str += string;
697
698 gtk_label_set( label, str.mbc_str() );
699 }
700 else
701 {
702 wxFAIL_MSG(wxT("wrong listbox index"));
703 }
704 }
705
706 wxString wxListBox::GetString( int n ) const
707 {
708 wxCHECK_MSG( m_list != NULL, wxT(""), wxT("invalid listbox") );
709
710 GList *child = g_list_nth( m_list->children, n );
711 if (child)
712 {
713 GtkBin *bin = GTK_BIN( child->data );
714 GtkLabel *label = GTK_LABEL( bin->child );
715
716 wxString str = wxString(GET_REAL_LABEL(label->label),*wxConvCurrent);
717
718 return str;
719 }
720
721 wxFAIL_MSG(wxT("wrong listbox index"));
722
723 return wxT("");
724 }
725
726 int wxListBox::GetCount() const
727 {
728 wxCHECK_MSG( m_list != NULL, -1, wxT("invalid listbox") );
729
730 GList *children = m_list->children;
731 return g_list_length(children);
732 }
733
734 int wxListBox::FindString( const wxString &item ) const
735 {
736 wxCHECK_MSG( m_list != NULL, -1, wxT("invalid listbox") );
737
738 GList *child = m_list->children;
739 int count = 0;
740 while (child)
741 {
742 GtkBin *bin = GTK_BIN( child->data );
743 GtkLabel *label = GTK_LABEL( bin->child );
744
745 wxString str = wxString(GET_REAL_LABEL(label->label),*wxConvCurrent);
746
747 if (str == item)
748 return count;
749
750 count++;
751 child = child->next;
752 }
753
754 // it's not an error if the string is not found -> no wxCHECK
755
756 return wxNOT_FOUND;
757 }
758
759 // ----------------------------------------------------------------------------
760 // selection
761 // ----------------------------------------------------------------------------
762
763 int wxListBox::GetSelection() const
764 {
765 wxCHECK_MSG( m_list != NULL, -1, wxT("invalid listbox") );
766
767 GList *child = m_list->children;
768 int count = 0;
769 while (child)
770 {
771 if (GTK_WIDGET(child->data)->state == GTK_STATE_SELECTED) return count;
772 count++;
773 child = child->next;
774 }
775 return -1;
776 }
777
778 int wxListBox::GetSelections( wxArrayInt& aSelections ) const
779 {
780 wxCHECK_MSG( m_list != NULL, -1, wxT("invalid listbox") );
781
782 // get the number of selected items first
783 GList *child = m_list->children;
784 int count = 0;
785 for (child = m_list->children; child != NULL; child = child->next)
786 {
787 if (GTK_WIDGET(child->data)->state == GTK_STATE_SELECTED)
788 count++;
789 }
790
791 aSelections.Empty();
792
793 if (count > 0)
794 {
795 // now fill the list
796 aSelections.Alloc(count); // optimization attempt
797 int i = 0;
798 for (child = m_list->children; child != NULL; child = child->next, i++)
799 {
800 if (GTK_WIDGET(child->data)->state == GTK_STATE_SELECTED)
801 aSelections.Add(i);
802 }
803 }
804
805 return count;
806 }
807
808 bool wxListBox::IsSelected( int n ) const
809 {
810 wxCHECK_MSG( m_list != NULL, FALSE, wxT("invalid listbox") );
811
812 GList *target = g_list_nth( m_list->children, n );
813
814 wxCHECK_MSG( target, FALSE, wxT("invalid listbox index") );
815
816 return (GTK_WIDGET(target->data)->state == GTK_STATE_SELECTED) ;
817 }
818
819 void wxListBox::SetSelection( int n, bool select )
820 {
821 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
822
823 GtkDisableEvents();
824
825 if (select)
826 gtk_list_select_item( m_list, n );
827 else
828 gtk_list_unselect_item( m_list, n );
829
830 GtkEnableEvents();
831 }
832
833 void wxListBox::DoSetFirstItem( int n )
834 {
835 wxCHECK_RET( m_list, wxT("invalid listbox") );
836
837 if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (m_list))
838 return;
839
840 // terribly efficient
841 const gchar *vadjustment_key = "gtk-vadjustment";
842 guint vadjustment_key_id = g_quark_from_static_string (vadjustment_key);
843
844 GtkAdjustment *adjustment =
845 (GtkAdjustment*) gtk_object_get_data_by_id (GTK_OBJECT (m_list), vadjustment_key_id);
846 wxCHECK_RET( adjustment, wxT("invalid listbox code") );
847
848 GList *target = g_list_nth( m_list->children, n );
849 wxCHECK_RET( target, wxT("invalid listbox index") );
850
851 GtkWidget *item = GTK_WIDGET(target->data);
852 wxCHECK_RET( item, wxT("invalid listbox code") );
853
854 if (item->allocation.y == -1)
855 {
856 wxlistbox_idle_struct* data = new wxlistbox_idle_struct;
857 data->m_listbox = this;
858 data->m_item = n;
859 data->m_tag = gtk_idle_add_priority( 800, wxlistbox_idle_callback, (gpointer) data );
860
861 return;
862 }
863
864 float y = item->allocation.y;
865 if (y > adjustment->upper - adjustment->page_size)
866 y = adjustment->upper - adjustment->page_size;
867 gtk_adjustment_set_value( adjustment, y );
868 }
869
870 // ----------------------------------------------------------------------------
871 // helpers
872 // ----------------------------------------------------------------------------
873
874 int wxListBox::GtkGetIndex( GtkWidget *item ) const
875 {
876 if (item)
877 {
878 GList *child = m_list->children;
879 int count = 0;
880 while (child)
881 {
882 if (GTK_WIDGET(child->data) == item) return count;
883 count++;
884 child = child->next;
885 }
886 }
887 return -1;
888 }
889
890 #if wxUSE_TOOLTIPS
891 void wxListBox::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
892 {
893 GList *child = m_list->children;
894 while (child)
895 {
896 gtk_tooltips_set_tip( tips, GTK_WIDGET( child->data ), wxConvCurrent->cWX2MB(tip), (gchar*) NULL );
897 child = child->next;
898 }
899 }
900 #endif // wxUSE_TOOLTIPS
901
902 void wxListBox::GtkDisableEvents()
903 {
904 GList *child = m_list->children;
905 while (child)
906 {
907 gtk_signal_disconnect_by_func( GTK_OBJECT(child->data),
908 GTK_SIGNAL_FUNC(gtk_listitem_select_callback), (gpointer)this );
909
910 if (HasFlag(wxLB_MULTIPLE))
911 gtk_signal_disconnect_by_func( GTK_OBJECT(child->data),
912 GTK_SIGNAL_FUNC(gtk_listitem_deselect_callback), (gpointer)this );
913
914 child = child->next;
915 }
916 }
917
918 void wxListBox::GtkEnableEvents()
919 {
920 GList *child = m_list->children;
921 while (child)
922 {
923 gtk_signal_connect( GTK_OBJECT(child->data), "select",
924 GTK_SIGNAL_FUNC(gtk_listitem_select_callback), (gpointer)this );
925
926 if (HasFlag(wxLB_MULTIPLE))
927 gtk_signal_connect( GTK_OBJECT(child->data), "deselect",
928 GTK_SIGNAL_FUNC(gtk_listitem_deselect_callback), (gpointer)this );
929
930 child = child->next;
931 }
932 }
933
934 GtkWidget *wxListBox::GetConnectWidget()
935 {
936 return GTK_WIDGET(m_list);
937 }
938
939 bool wxListBox::IsOwnGtkWindow( GdkWindow *window )
940 {
941 if (GTK_WIDGET(m_list)->window == window) return TRUE;
942
943 GList *child = m_list->children;
944 while (child)
945 {
946 GtkWidget *bin = GTK_WIDGET( child->data );
947 if (bin->window == window) return TRUE;
948 child = child->next;
949 }
950
951 return FALSE;
952 }
953
954 void wxListBox::ApplyWidgetStyle()
955 {
956 SetWidgetStyle();
957
958 if (m_backgroundColour.Ok())
959 {
960 GdkWindow *window = GTK_WIDGET(m_list)->window;
961 if ( window )
962 {
963 m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) );
964 gdk_window_set_background( window, m_backgroundColour.GetColor() );
965 gdk_window_clear( window );
966 }
967 }
968
969 GList *child = m_list->children;
970 while (child)
971 {
972 gtk_widget_set_style( GTK_WIDGET(child->data), m_widgetStyle );
973
974 GtkBin *bin = GTK_BIN( child->data );
975 GtkWidget *label = GTK_WIDGET( bin->child );
976 gtk_widget_set_style( label, m_widgetStyle );
977
978 child = child->next;
979 }
980 }
981
982 void wxListBox::OnInternalIdle()
983 {
984 wxCursor cursor = m_cursor;
985 if (g_globalCursor.Ok()) cursor = g_globalCursor;
986
987 if (GTK_WIDGET(m_list)->window && cursor.Ok())
988 {
989 /* I now set the cursor the anew in every OnInternalIdle call
990 as setting the cursor in a parent window also effects the
991 windows above so that checking for the current cursor is
992 not possible. */
993
994 gdk_window_set_cursor( GTK_WIDGET(m_list)->window, cursor.GetCursor() );
995
996 GList *child = m_list->children;
997 while (child)
998 {
999 GtkBin *bin = GTK_BIN( child->data );
1000 GtkWidget *label = GTK_WIDGET( bin->child );
1001
1002 if (!label->window)
1003 break;
1004 else
1005 gdk_window_set_cursor( label->window, cursor.GetCursor() );
1006
1007 child = child->next;
1008 }
1009 }
1010
1011 UpdateWindowUI();
1012 }
1013
1014 wxSize wxListBox::DoGetBestSize() const
1015 {
1016 return wxSize(100, 110);
1017 }
1018
1019 #endif