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