Added wxCommandEvent::IsSeection for listbox,
[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 #include "wx/listbox.h"
16
17 #if wxUSE_LISTBOX
18
19 #include "wx/dynarray.h"
20 #include "wx/utils.h"
21 #include "wx/intl.h"
22 #include "wx/checklst.h"
23 #include "wx/settings.h"
24
25 #if wxUSE_TOOLTIPS
26 #include "wx/tooltip.h"
27 #endif
28
29 #include <gdk/gdk.h>
30 #include <gtk/gtk.h>
31 #include <gdk/gdkkeysyms.h>
32
33 //-----------------------------------------------------------------------------
34 // idle system
35 //-----------------------------------------------------------------------------
36
37 extern void wxapp_install_idle_handler();
38 extern bool g_isIdle;
39
40 //-------------------------------------------------------------------------
41 // conditional compilation
42 //-------------------------------------------------------------------------
43
44 #if (GTK_MINOR_VERSION > 0)
45 #define NEW_GTK_SCROLL_CODE
46 #endif
47
48 //-----------------------------------------------------------------------------
49 // private functions
50 //-----------------------------------------------------------------------------
51
52 #if wxUSE_CHECKLISTBOX
53
54 #define CHECKBOX_STRING "[-] "
55
56 // checklistboxes have "[±] " prepended to their lables, this macro removes it
57 // (NB: 4 below is the length of CHECKBOX_STRING above)
58 //
59 // the argument to it is a "const char *" pointer
60 #define GET_REAL_LABEL(label) ((m_hasCheckBoxes)?(label)+4 : (label))
61
62 #else // !wxUSE_CHECKLISTBOX
63
64 #define GET_REAL_LABEL(label) (label)
65
66 #endif // wxUSE_CHECKLISTBOX
67
68 //-----------------------------------------------------------------------------
69 // data
70 //-----------------------------------------------------------------------------
71
72 extern bool g_blockEventsOnDrag;
73 extern bool g_blockEventsOnScroll;
74 extern wxCursor g_globalCursor;
75
76 static bool g_hasDoubleClicked = FALSE;
77
78 //-----------------------------------------------------------------------------
79 // idle callback for SetFirstItem
80 //-----------------------------------------------------------------------------
81
82 struct wxlistbox_idle_struct
83 {
84 wxListBox *m_listbox;
85 int m_item;
86 gint m_tag;
87 };
88
89 static gint wxlistbox_idle_callback( gpointer gdata )
90 {
91 wxlistbox_idle_struct* data = (wxlistbox_idle_struct*) gdata;
92 gdk_threads_enter();
93
94 gtk_idle_remove( data->m_tag );
95
96 data->m_listbox->SetFirstItem( data->m_item );
97
98 delete data;
99
100 gdk_threads_leave();
101
102 return TRUE;
103 }
104
105 //-----------------------------------------------------------------------------
106 // "button_release_event"
107 //-----------------------------------------------------------------------------
108
109 /* we would normally emit a wxEVT_COMMAND_LISTBOX_DOUBLECLICKED event once
110 a GDK_2BUTTON_PRESS occurs, but this has the particular problem of the
111 listbox keeping the focus until it receives a GDK_BUTTON_RELEASE event.
112 this can lead to race conditions so that we emit the dclick event
113 after the GDK_BUTTON_RELEASE event after the GDK_2BUTTON_PRESS event */
114
115 static gint
116 gtk_listbox_button_release_callback( GtkWidget * WXUNUSED(widget),
117 GdkEventButton * WXUNUSED(gdk_event),
118 wxListBox *listbox )
119 {
120 if (g_isIdle) wxapp_install_idle_handler();
121
122 if (g_blockEventsOnDrag) return FALSE;
123 if (g_blockEventsOnScroll) return FALSE;
124
125 if (!listbox->m_hasVMT) return FALSE;
126
127 if (!g_hasDoubleClicked) return FALSE;
128
129 wxCommandEvent event( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, listbox->GetId() );
130 event.SetEventObject( listbox );
131
132 wxArrayInt aSelections;
133 int n, count = listbox->GetSelections(aSelections);
134 if ( count > 0 )
135 {
136 n = aSelections[0];
137 if ( listbox->HasClientObjectData() )
138 event.SetClientObject( listbox->GetClientObject(n) );
139 else if ( listbox->HasClientUntypedData() )
140 event.SetClientData( listbox->GetClientData(n) );
141 event.SetString( listbox->GetString(n) );
142 }
143 else
144 {
145 n = -1;
146 }
147
148 event.m_commandInt = n;
149
150 listbox->GetEventHandler()->ProcessEvent( event );
151
152 return FALSE;
153 }
154
155 //-----------------------------------------------------------------------------
156 // "button_press_event"
157 //-----------------------------------------------------------------------------
158
159 static gint
160 gtk_listbox_button_press_callback( GtkWidget *widget,
161 GdkEventButton *gdk_event,
162 wxListBox *listbox )
163 {
164 if (g_isIdle) wxapp_install_idle_handler();
165
166 if (g_blockEventsOnDrag) return FALSE;
167 if (g_blockEventsOnScroll) return FALSE;
168
169 if (!listbox->m_hasVMT) return FALSE;
170
171 int sel = listbox->GtkGetIndex( widget );
172
173 #if wxUSE_CHECKLISTBOX
174 if ((listbox->m_hasCheckBoxes) && (gdk_event->x < 15) && (gdk_event->type != GDK_2BUTTON_PRESS))
175 {
176 wxCheckListBox *clb = (wxCheckListBox *)listbox;
177
178 clb->Check( sel, !clb->IsChecked(sel) );
179
180 wxCommandEvent event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, listbox->GetId() );
181 event.SetEventObject( listbox );
182 event.SetInt( sel );
183 listbox->GetEventHandler()->ProcessEvent( event );
184 }
185 #endif // wxUSE_CHECKLISTBOX
186
187 /* emit wxEVT_COMMAND_LISTBOX_DOUBLECLICKED later */
188 g_hasDoubleClicked = (gdk_event->type == GDK_2BUTTON_PRESS);
189
190 return FALSE;
191 }
192
193 //-----------------------------------------------------------------------------
194 // "key_press_event"
195 //-----------------------------------------------------------------------------
196
197 static gint
198 gtk_listbox_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxListBox *listbox )
199 {
200 if (g_isIdle)
201 wxapp_install_idle_handler();
202
203 if (g_blockEventsOnDrag)
204 return FALSE;
205
206 bool ret = FALSE;
207
208 if ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab))
209 {
210 wxNavigationKeyEvent new_event;
211 /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */
212 new_event.SetDirection( (gdk_event->keyval == GDK_Tab) );
213 /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
214 new_event.SetWindowChange( (gdk_event->state & GDK_CONTROL_MASK) );
215 new_event.SetCurrentFocus( listbox );
216 ret = listbox->GetEventHandler()->ProcessEvent( new_event );
217 }
218
219 if ((gdk_event->keyval == GDK_Return) && (!ret))
220 {
221 // eat return in all modes
222 ret = TRUE;
223 }
224
225 #if wxUSE_CHECKLISTBOX
226 if ((gdk_event->keyval == ' ') && (listbox->m_hasCheckBoxes) && (!ret))
227 {
228 int sel = listbox->GtkGetIndex( widget );
229
230 wxCheckListBox *clb = (wxCheckListBox *)listbox;
231
232 clb->Check( sel, !clb->IsChecked(sel) );
233
234 wxCommandEvent new_event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, listbox->GetId() );
235 new_event.SetEventObject( listbox );
236 new_event.SetInt( sel );
237 ret = listbox->GetEventHandler()->ProcessEvent( new_event );
238 }
239 #endif // wxUSE_CHECKLISTBOX
240
241 if (ret)
242 {
243 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
244 return TRUE;
245 }
246
247 return FALSE;
248 }
249
250 //-----------------------------------------------------------------------------
251 // "select" and "deselect"
252 //-----------------------------------------------------------------------------
253
254 static void gtk_listitem_select_cb( GtkWidget *widget, wxListBox *listbox, bool is_selection );
255
256 static void gtk_listitem_select_callback( GtkWidget *widget, wxListBox *listbox )
257 {
258 gtk_listitem_select_cb( widget, listbox, TRUE );
259 }
260
261 static void gtk_listitem_deselect_callback( GtkWidget *widget, wxListBox *listbox )
262 {
263 gtk_listitem_select_cb( widget, listbox, FALSE );
264 }
265
266 static void gtk_listitem_select_cb( GtkWidget *WXUNUSED(widget), wxListBox *listbox, bool is_selection )
267 {
268 if (g_isIdle) wxapp_install_idle_handler();
269
270 if (!listbox->m_hasVMT) return;
271 if (g_blockEventsOnDrag) return;
272
273 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, listbox->GetId() );
274 event.SetEventObject( listbox );
275 event.SetExtraLong( (long) is_selection );
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 int index;
440
441 if (m_strings)
442 {
443 for (size_t n = 0; n < nItems; n++)
444 {
445 index = m_strings->Add( items[n] );
446
447 if (index != GetCount())
448 {
449 GtkAddItem( items[n], index );
450 wxNode *node = m_clientList.Nth( index );
451 m_clientList.Insert( node, (wxObject*) NULL );
452 }
453 else
454 {
455 GtkAddItem( items[n] );
456 m_clientList.Append( (wxObject*) NULL );
457 }
458 }
459 }
460 else
461 {
462 if (pos == length)
463 {
464 for ( size_t n = 0; n < nItems; n++ )
465 {
466 GtkAddItem( items[n] );
467
468 m_clientList.Append((wxObject *)NULL);
469 }
470 }
471 else
472 {
473 wxNode *node = m_clientList.Nth( pos );
474 for ( size_t n = 0; n < nItems; n++ )
475 {
476 GtkAddItem( items[n], pos+n );
477
478 m_clientList.Insert( node, (wxObject *)NULL );
479 }
480 }
481 }
482
483 wxASSERT_MSG( m_clientList.GetCount() == (size_t)GetCount(),
484 wxT("bug in client data management") );
485 }
486
487 int wxListBox::DoAppend( const wxString& item )
488 {
489 if (m_strings)
490 {
491 // need to determine the index
492 int index = m_strings->Add( item );
493
494 // only if not at the end anyway
495 if (index != GetCount())
496 {
497 GtkAddItem( item, index );
498
499 wxNode *node = m_clientList.Nth( index );
500 m_clientList.Insert( node, (wxObject *)NULL );
501
502 return index;
503 }
504 }
505
506 GtkAddItem(item);
507
508 m_clientList.Append((wxObject *)NULL);
509
510 return GetCount() - 1;
511 }
512
513 void wxListBox::GtkAddItem( const wxString &item, int pos )
514 {
515 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
516
517 GtkWidget *list_item;
518
519 wxString label(item);
520 #if wxUSE_CHECKLISTBOX
521 if (m_hasCheckBoxes)
522 {
523 label.Prepend(CHECKBOX_STRING);
524 }
525 #endif // wxUSE_CHECKLISTBOX
526
527 list_item = gtk_list_item_new_with_label( label.mbc_str() );
528
529 GList *gitem_list = g_list_alloc ();
530 gitem_list->data = list_item;
531
532 if (pos == -1)
533 gtk_list_append_items( GTK_LIST (m_list), gitem_list );
534 else
535 gtk_list_insert_items( GTK_LIST (m_list), gitem_list, pos );
536
537 gtk_signal_connect( GTK_OBJECT(list_item), "select",
538 GTK_SIGNAL_FUNC(gtk_listitem_select_callback), (gpointer)this );
539
540 if (HasFlag(wxLB_MULTIPLE))
541 gtk_signal_connect( GTK_OBJECT(list_item), "deselect",
542 GTK_SIGNAL_FUNC(gtk_listitem_deselect_callback), (gpointer)this );
543
544 gtk_signal_connect( GTK_OBJECT(list_item),
545 "button_press_event",
546 (GtkSignalFunc)gtk_listbox_button_press_callback,
547 (gpointer) this );
548
549 gtk_signal_connect_after( GTK_OBJECT(list_item),
550 "button_release_event",
551 (GtkSignalFunc)gtk_listbox_button_release_callback,
552 (gpointer) this );
553
554 gtk_signal_connect( GTK_OBJECT(list_item),
555 "key_press_event",
556 (GtkSignalFunc)gtk_listbox_key_press_callback,
557 (gpointer)this );
558
559 ConnectWidget( list_item );
560
561 gtk_widget_show( list_item );
562
563 if (GTK_WIDGET_REALIZED(m_widget))
564 {
565 gtk_widget_realize( list_item );
566 gtk_widget_realize( GTK_BIN(list_item)->child );
567
568 // Apply current widget style to the new list_item
569 if (m_widgetStyle)
570 {
571 gtk_widget_set_style( GTK_WIDGET( list_item ), m_widgetStyle );
572 GtkBin *bin = GTK_BIN( list_item );
573 GtkWidget *label = GTK_WIDGET( bin->child );
574 gtk_widget_set_style( label, m_widgetStyle );
575 }
576
577 #if wxUSE_TOOLTIPS
578 if (m_tooltip) m_tooltip->Apply( this );
579 #endif
580 }
581 }
582
583 void wxListBox::DoSetItems( const wxArrayString& items,
584 void **clientData)
585 {
586 Clear();
587
588 DoInsertItems(items, 0);
589
590 if ( clientData )
591 {
592 size_t count = items.GetCount();
593 for ( size_t n = 0; n < count; n++ )
594 {
595 SetClientData(n, clientData[n]);
596 }
597 }
598 }
599
600 // ----------------------------------------------------------------------------
601 // client data
602 // ----------------------------------------------------------------------------
603
604 void wxListBox::DoSetItemClientData( int n, void* clientData )
605 {
606 wxCHECK_RET( m_widget != NULL, wxT("invalid listbox control") );
607
608 wxNode *node = m_clientList.Nth( n );
609 wxCHECK_RET( node, wxT("invalid index in wxListBox::DoSetItemClientData") );
610
611 node->SetData( (wxObject*) clientData );
612 }
613
614 void* wxListBox::DoGetItemClientData( int n ) const
615 {
616 wxCHECK_MSG( m_widget != NULL, NULL, wxT("invalid listbox control") );
617
618 wxNode *node = m_clientList.Nth( n );
619 wxCHECK_MSG( node, NULL, wxT("invalid index in wxListBox::DoGetItemClientData") );
620
621 return node->Data();
622 }
623
624 void wxListBox::DoSetItemClientObject( int n, wxClientData* clientData )
625 {
626 wxCHECK_RET( m_widget != NULL, wxT("invalid listbox control") );
627
628 wxNode *node = m_clientList.Nth( n );
629 wxCHECK_RET( node, wxT("invalid index in wxListBox::DoSetItemClientObject") );
630
631 wxClientData *cd = (wxClientData*) node->Data();
632 delete cd;
633
634 node->SetData( (wxObject*) clientData );
635 }
636
637 wxClientData* wxListBox::DoGetItemClientObject( int n ) const
638 {
639 wxCHECK_MSG( m_widget != NULL, (wxClientData*) NULL, wxT("invalid listbox control") );
640
641 wxNode *node = m_clientList.Nth( n );
642 wxCHECK_MSG( node, (wxClientData *)NULL,
643 wxT("invalid index in wxListBox::DoGetItemClientObject") );
644
645 return (wxClientData*) node->Data();
646 }
647
648 void wxListBox::Clear()
649 {
650 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
651
652 gtk_list_clear_items( m_list, 0, Number() );
653
654 if ( HasClientObjectData() )
655 {
656 // destroy the data (due to Robert's idea of using wxList<wxObject>
657 // and not wxList<wxClientData> we can't just say
658 // m_clientList.DeleteContents(TRUE) - this would crash!
659 wxNode *node = m_clientList.First();
660 while ( node )
661 {
662 delete (wxClientData *)node->Data();
663 node = node->Next();
664 }
665 }
666 m_clientList.Clear();
667
668 if ( m_strings )
669 m_strings->Clear();
670 }
671
672 void wxListBox::Delete( int n )
673 {
674 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
675
676 GList *child = g_list_nth( m_list->children, n );
677
678 wxCHECK_RET( child, wxT("wrong listbox index") );
679
680 GList *list = g_list_append( (GList*) NULL, child->data );
681 gtk_list_remove_items( m_list, list );
682 g_list_free( list );
683
684 wxNode *node = m_clientList.Nth( n );
685 if ( node )
686 {
687 if ( m_clientDataItemsType == ClientData_Object )
688 {
689 wxClientData *cd = (wxClientData*)node->Data();
690 delete cd;
691 }
692
693 m_clientList.DeleteNode( node );
694 }
695
696 if ( m_strings )
697 m_strings->Remove(n);
698 }
699
700 // ----------------------------------------------------------------------------
701 // string list access
702 // ----------------------------------------------------------------------------
703
704 void wxListBox::SetString( int n, const wxString &string )
705 {
706 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
707
708 GList *child = g_list_nth( m_list->children, n );
709 if (child)
710 {
711 GtkBin *bin = GTK_BIN( child->data );
712 GtkLabel *label = GTK_LABEL( bin->child );
713
714 wxString str;
715 #if wxUSE_CHECKLISTBOX
716 if (m_hasCheckBoxes)
717 str += CHECKBOX_STRING;
718 #endif // wxUSE_CHECKLISTBOX
719 str += string;
720
721 gtk_label_set( label, str.mbc_str() );
722 }
723 else
724 {
725 wxFAIL_MSG(wxT("wrong listbox index"));
726 }
727 }
728
729 wxString wxListBox::GetString( int n ) const
730 {
731 wxCHECK_MSG( m_list != NULL, wxT(""), wxT("invalid listbox") );
732
733 GList *child = g_list_nth( m_list->children, n );
734 if (child)
735 {
736 GtkBin *bin = GTK_BIN( child->data );
737 GtkLabel *label = GTK_LABEL( bin->child );
738
739 wxString str = wxString(GET_REAL_LABEL(label->label),*wxConvCurrent);
740
741 return str;
742 }
743
744 wxFAIL_MSG(wxT("wrong listbox index"));
745
746 return wxT("");
747 }
748
749 int wxListBox::GetCount() const
750 {
751 wxCHECK_MSG( m_list != NULL, -1, wxT("invalid listbox") );
752
753 GList *children = m_list->children;
754 return g_list_length(children);
755 }
756
757 int wxListBox::FindString( const wxString &item ) const
758 {
759 wxCHECK_MSG( m_list != NULL, -1, wxT("invalid listbox") );
760
761 GList *child = m_list->children;
762 int count = 0;
763 while (child)
764 {
765 GtkBin *bin = GTK_BIN( child->data );
766 GtkLabel *label = GTK_LABEL( bin->child );
767
768 wxString str = wxString(GET_REAL_LABEL(label->label),*wxConvCurrent);
769
770 if (str == item)
771 return count;
772
773 count++;
774 child = child->next;
775 }
776
777 // it's not an error if the string is not found -> no wxCHECK
778
779 return wxNOT_FOUND;
780 }
781
782 // ----------------------------------------------------------------------------
783 // selection
784 // ----------------------------------------------------------------------------
785
786 int wxListBox::GetSelection() const
787 {
788 wxCHECK_MSG( m_list != NULL, -1, wxT("invalid listbox") );
789
790 GList *child = m_list->children;
791 int count = 0;
792 while (child)
793 {
794 if (GTK_WIDGET(child->data)->state == GTK_STATE_SELECTED) return count;
795 count++;
796 child = child->next;
797 }
798 return -1;
799 }
800
801 int wxListBox::GetSelections( wxArrayInt& aSelections ) const
802 {
803 wxCHECK_MSG( m_list != NULL, -1, wxT("invalid listbox") );
804
805 // get the number of selected items first
806 GList *child = m_list->children;
807 int count = 0;
808 for (child = m_list->children; child != NULL; child = child->next)
809 {
810 if (GTK_WIDGET(child->data)->state == GTK_STATE_SELECTED)
811 count++;
812 }
813
814 aSelections.Empty();
815
816 if (count > 0)
817 {
818 // now fill the list
819 aSelections.Alloc(count); // optimization attempt
820 int i = 0;
821 for (child = m_list->children; child != NULL; child = child->next, i++)
822 {
823 if (GTK_WIDGET(child->data)->state == GTK_STATE_SELECTED)
824 aSelections.Add(i);
825 }
826 }
827
828 return count;
829 }
830
831 bool wxListBox::IsSelected( int n ) const
832 {
833 wxCHECK_MSG( m_list != NULL, FALSE, wxT("invalid listbox") );
834
835 GList *target = g_list_nth( m_list->children, n );
836
837 wxCHECK_MSG( target, FALSE, wxT("invalid listbox index") );
838
839 return (GTK_WIDGET(target->data)->state == GTK_STATE_SELECTED) ;
840 }
841
842 void wxListBox::SetSelection( int n, bool select )
843 {
844 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
845
846 GtkDisableEvents();
847
848 if (select)
849 gtk_list_select_item( m_list, n );
850 else
851 gtk_list_unselect_item( m_list, n );
852
853 GtkEnableEvents();
854 }
855
856 void wxListBox::DoSetFirstItem( int n )
857 {
858 wxCHECK_RET( m_list, wxT("invalid listbox") );
859
860 if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (m_list))
861 return;
862
863 // terribly efficient
864 const gchar *vadjustment_key = "gtk-vadjustment";
865 guint vadjustment_key_id = g_quark_from_static_string (vadjustment_key);
866
867 GtkAdjustment *adjustment =
868 (GtkAdjustment*) gtk_object_get_data_by_id (GTK_OBJECT (m_list), vadjustment_key_id);
869 wxCHECK_RET( adjustment, wxT("invalid listbox code") );
870
871 GList *target = g_list_nth( m_list->children, n );
872 wxCHECK_RET( target, wxT("invalid listbox index") );
873
874 GtkWidget *item = GTK_WIDGET(target->data);
875 wxCHECK_RET( item, wxT("invalid listbox code") );
876
877 if (item->allocation.y == -1)
878 {
879 wxlistbox_idle_struct* data = new wxlistbox_idle_struct;
880 data->m_listbox = this;
881 data->m_item = n;
882 data->m_tag = gtk_idle_add_priority( 800, wxlistbox_idle_callback, (gpointer) data );
883
884 return;
885 }
886
887 float y = item->allocation.y;
888 if (y > adjustment->upper - adjustment->page_size)
889 y = adjustment->upper - adjustment->page_size;
890 gtk_adjustment_set_value( adjustment, y );
891 }
892
893 // ----------------------------------------------------------------------------
894 // helpers
895 // ----------------------------------------------------------------------------
896
897 int wxListBox::GtkGetIndex( GtkWidget *item ) const
898 {
899 if (item)
900 {
901 GList *child = m_list->children;
902 int count = 0;
903 while (child)
904 {
905 if (GTK_WIDGET(child->data) == item) return count;
906 count++;
907 child = child->next;
908 }
909 }
910 return -1;
911 }
912
913 #if wxUSE_TOOLTIPS
914 void wxListBox::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
915 {
916 GList *child = m_list->children;
917 while (child)
918 {
919 gtk_tooltips_set_tip( tips, GTK_WIDGET( child->data ), wxConvCurrent->cWX2MB(tip), (gchar*) NULL );
920 child = child->next;
921 }
922 }
923 #endif // wxUSE_TOOLTIPS
924
925 void wxListBox::GtkDisableEvents()
926 {
927 GList *child = m_list->children;
928 while (child)
929 {
930 gtk_signal_disconnect_by_func( GTK_OBJECT(child->data),
931 GTK_SIGNAL_FUNC(gtk_listitem_select_callback), (gpointer)this );
932
933 if (HasFlag(wxLB_MULTIPLE))
934 gtk_signal_disconnect_by_func( GTK_OBJECT(child->data),
935 GTK_SIGNAL_FUNC(gtk_listitem_deselect_callback), (gpointer)this );
936
937 child = child->next;
938 }
939 }
940
941 void wxListBox::GtkEnableEvents()
942 {
943 GList *child = m_list->children;
944 while (child)
945 {
946 gtk_signal_connect( GTK_OBJECT(child->data), "select",
947 GTK_SIGNAL_FUNC(gtk_listitem_select_callback), (gpointer)this );
948
949 if (HasFlag(wxLB_MULTIPLE))
950 gtk_signal_connect( GTK_OBJECT(child->data), "deselect",
951 GTK_SIGNAL_FUNC(gtk_listitem_deselect_callback), (gpointer)this );
952
953 child = child->next;
954 }
955 }
956
957 GtkWidget *wxListBox::GetConnectWidget()
958 {
959 return GTK_WIDGET(m_list);
960 }
961
962 bool wxListBox::IsOwnGtkWindow( GdkWindow *window )
963 {
964 if (GTK_WIDGET(m_list)->window == window) return TRUE;
965
966 GList *child = m_list->children;
967 while (child)
968 {
969 GtkWidget *bin = GTK_WIDGET( child->data );
970 if (bin->window == window) return TRUE;
971 child = child->next;
972 }
973
974 return FALSE;
975 }
976
977 void wxListBox::ApplyWidgetStyle()
978 {
979 SetWidgetStyle();
980
981 if (m_backgroundColour.Ok())
982 {
983 GdkWindow *window = GTK_WIDGET(m_list)->window;
984 if ( window )
985 {
986 m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) );
987 gdk_window_set_background( window, m_backgroundColour.GetColor() );
988 gdk_window_clear( window );
989 }
990 }
991
992 GList *child = m_list->children;
993 while (child)
994 {
995 gtk_widget_set_style( GTK_WIDGET(child->data), m_widgetStyle );
996
997 GtkBin *bin = GTK_BIN( child->data );
998 GtkWidget *label = GTK_WIDGET( bin->child );
999 gtk_widget_set_style( label, m_widgetStyle );
1000
1001 child = child->next;
1002 }
1003 }
1004
1005 void wxListBox::OnInternalIdle()
1006 {
1007 wxCursor cursor = m_cursor;
1008 if (g_globalCursor.Ok()) cursor = g_globalCursor;
1009
1010 if (GTK_WIDGET(m_list)->window && cursor.Ok())
1011 {
1012 /* I now set the cursor the anew in every OnInternalIdle call
1013 as setting the cursor in a parent window also effects the
1014 windows above so that checking for the current cursor is
1015 not possible. */
1016
1017 gdk_window_set_cursor( GTK_WIDGET(m_list)->window, cursor.GetCursor() );
1018
1019 GList *child = m_list->children;
1020 while (child)
1021 {
1022 GtkBin *bin = GTK_BIN( child->data );
1023 GtkWidget *label = GTK_WIDGET( bin->child );
1024
1025 if (!label->window)
1026 break;
1027 else
1028 gdk_window_set_cursor( label->window, cursor.GetCursor() );
1029
1030 child = child->next;
1031 }
1032 }
1033
1034 UpdateWindowUI();
1035 }
1036
1037 wxSize wxListBox::DoGetBestSize() const
1038 {
1039 return wxSize(100, 110);
1040 }
1041
1042 #endif