Committing in .
[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_callback( GtkWidget *WXUNUSED(widget), wxListBox *listbox );
255
256 static void gtk_listitem_deselect_callback( GtkWidget *widget, wxListBox *listbox )
257 {
258 gtk_listitem_select_callback( widget, listbox );
259 }
260
261 static void gtk_listitem_select_callback( GtkWidget *WXUNUSED(widget), wxListBox *listbox )
262 {
263 if (g_isIdle) wxapp_install_idle_handler();
264
265 if (!listbox->m_hasVMT) return;
266 if (g_blockEventsOnDrag) return;
267
268 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, listbox->GetId() );
269 event.SetEventObject( listbox );
270
271 wxArrayInt aSelections;
272 int n, count = listbox->GetSelections(aSelections);
273 if ( count > 0 )
274 {
275 n = aSelections[0];
276 if ( listbox->HasClientObjectData() )
277 event.SetClientObject( listbox->GetClientObject(n) );
278 else if ( listbox->HasClientUntypedData() )
279 event.SetClientData( listbox->GetClientData(n) );
280 event.SetString( listbox->GetString(n) );
281 }
282 else
283 {
284 n = -1;
285 }
286
287 event.m_commandInt = n;
288
289 listbox->GetEventHandler()->AddPendingEvent( event );
290 // listbox->GetEventHandler()->ProcessEvent( event );
291 }
292
293 //-----------------------------------------------------------------------------
294 // wxListBox
295 //-----------------------------------------------------------------------------
296
297 IMPLEMENT_DYNAMIC_CLASS(wxListBox,wxControl)
298
299 // ----------------------------------------------------------------------------
300 // construction
301 // ----------------------------------------------------------------------------
302
303 wxListBox::wxListBox()
304 {
305 m_list = (GtkList *) NULL;
306 #if wxUSE_CHECKLISTBOX
307 m_hasCheckBoxes = FALSE;
308 #endif // wxUSE_CHECKLISTBOX
309 }
310
311 bool wxListBox::Create( wxWindow *parent, wxWindowID id,
312 const wxPoint &pos, const wxSize &size,
313 int n, const wxString choices[],
314 long style, const wxValidator& validator,
315 const wxString &name )
316 {
317 m_needParent = TRUE;
318 m_acceptsFocus = TRUE;
319
320 if (!PreCreation( parent, pos, size ) ||
321 !CreateBase( parent, id, pos, size, style, validator, name ))
322 {
323 wxFAIL_MSG( wxT("wxListBox creation failed") );
324 return FALSE;
325 }
326
327 m_widget = gtk_scrolled_window_new( (GtkAdjustment*) NULL, (GtkAdjustment*) NULL );
328 if (style & wxLB_ALWAYS_SB)
329 {
330 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
331 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
332 }
333 else
334 {
335 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
336 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
337 }
338
339 m_list = GTK_LIST( gtk_list_new() );
340
341 GtkSelectionMode mode;
342 if (style & wxLB_MULTIPLE)
343 {
344 mode = GTK_SELECTION_MULTIPLE;
345 }
346 else if (style & wxLB_EXTENDED)
347 {
348 mode = GTK_SELECTION_EXTENDED;
349 }
350 else
351 {
352 // if style was 0 set single mode
353 m_windowStyle |= wxLB_SINGLE;
354 mode = GTK_SELECTION_BROWSE;
355 }
356
357 gtk_list_set_selection_mode( GTK_LIST(m_list), mode );
358
359 #ifdef NEW_GTK_SCROLL_CODE
360 gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(m_widget), GTK_WIDGET(m_list) );
361 #else
362 gtk_container_add( GTK_CONTAINER(m_widget), GTK_WIDGET(m_list) );
363 #endif
364
365 /* make list scroll when moving the focus down using cursor keys */
366 gtk_container_set_focus_vadjustment(
367 GTK_CONTAINER(m_list),
368 gtk_scrolled_window_get_vadjustment(
369 GTK_SCROLLED_WINDOW(m_widget)));
370
371 gtk_widget_show( GTK_WIDGET(m_list) );
372
373 SetSizeOrDefault( size );
374
375 if ( style & wxLB_SORT )
376 {
377 // this will change DoAppend() behaviour
378 m_strings = new wxSortedArrayString;
379 }
380 else
381 {
382 m_strings = (wxSortedArrayString *)NULL;
383 }
384
385 for (int i = 0; i < n; i++)
386 {
387 // add one by one
388 DoAppend(choices[i]);
389 }
390
391 m_parent->DoAddChild( this );
392
393 PostCreation();
394
395 SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_LISTBOX ) );
396 SetForegroundColour( parent->GetForegroundColour() );
397 SetFont( parent->GetFont() );
398
399 Show( TRUE );
400
401 return TRUE;
402 }
403
404 wxListBox::~wxListBox()
405 {
406 m_hasVMT = FALSE;
407
408 Clear();
409
410 if (m_strings)
411 delete m_strings;
412 }
413
414 void wxListBox::DoInsertItems(const wxArrayString& items, int pos)
415 {
416 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
417
418 // VZ: notice that InsertItems knows nothing about sorting, so calling it
419 // from outside (and not from our own Append) is likely to break
420 // everything
421
422 // code elsewhere supposes we have as many items in m_clientList as items
423 // in the listbox
424 wxASSERT_MSG( m_clientList.GetCount() == (size_t)GetCount(),
425 wxT("bug in client data management") );
426
427 GList *children = m_list->children;
428 int length = g_list_length(children);
429
430 wxCHECK_RET( pos <= length, wxT("invalid index in wxListBox::InsertItems") );
431
432 size_t nItems = items.GetCount();
433
434 if (pos == length)
435 {
436 for ( size_t n = 0; n < nItems; n++ )
437 {
438 GtkAddItem( items[n] );
439
440 m_clientList.Append((wxObject *)NULL);
441 }
442 }
443 else
444 {
445 wxNode *node = m_clientList.Nth( pos );
446 for ( size_t n = 0; n < nItems; n++ )
447 {
448 GtkAddItem( items[n], pos+n );
449
450 m_clientList.Insert( node, (wxObject *)NULL );
451 }
452 }
453
454 wxASSERT_MSG( m_clientList.GetCount() == (size_t)GetCount(),
455 wxT("bug in client data management") );
456 }
457
458 int wxListBox::DoAppend( const wxString& item )
459 {
460 if (m_strings)
461 {
462 // need to determine the index
463 int index = m_strings->Add( item );
464
465 // only if not at the end anyway
466 if (index != GetCount())
467 {
468 GtkAddItem( item, index );
469
470 wxNode *node = m_clientList.Nth( index );
471 m_clientList.Insert( node, (wxObject *)NULL );
472
473 return index;
474 }
475 }
476
477 GtkAddItem(item);
478
479 m_clientList.Append((wxObject *)NULL);
480
481 return GetCount() - 1;
482 }
483
484 void wxListBox::GtkAddItem( const wxString &item, int pos )
485 {
486 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
487
488 GtkWidget *list_item;
489
490 wxString label(item);
491 #if wxUSE_CHECKLISTBOX
492 if (m_hasCheckBoxes)
493 {
494 label.Prepend(CHECKBOX_STRING);
495 }
496 #endif // wxUSE_CHECKLISTBOX
497
498 list_item = gtk_list_item_new_with_label( label.mbc_str() );
499
500 GList *gitem_list = g_list_alloc ();
501 gitem_list->data = list_item;
502
503 if (pos == -1)
504 gtk_list_append_items( GTK_LIST (m_list), gitem_list );
505 else
506 gtk_list_insert_items( GTK_LIST (m_list), gitem_list, pos );
507
508 gtk_signal_connect( GTK_OBJECT(list_item), "select",
509 GTK_SIGNAL_FUNC(gtk_listitem_select_callback), (gpointer)this );
510
511 if (HasFlag(wxLB_MULTIPLE))
512 gtk_signal_connect( GTK_OBJECT(list_item), "deselect",
513 GTK_SIGNAL_FUNC(gtk_listitem_deselect_callback), (gpointer)this );
514
515 gtk_signal_connect( GTK_OBJECT(list_item),
516 "button_press_event",
517 (GtkSignalFunc)gtk_listbox_button_press_callback,
518 (gpointer) this );
519
520 gtk_signal_connect_after( GTK_OBJECT(list_item),
521 "button_release_event",
522 (GtkSignalFunc)gtk_listbox_button_release_callback,
523 (gpointer) this );
524
525 gtk_signal_connect( GTK_OBJECT(list_item),
526 "key_press_event",
527 (GtkSignalFunc)gtk_listbox_key_press_callback,
528 (gpointer)this );
529
530 ConnectWidget( list_item );
531
532 gtk_widget_show( list_item );
533
534 if (GTK_WIDGET_REALIZED(m_widget))
535 {
536 gtk_widget_realize( list_item );
537 gtk_widget_realize( GTK_BIN(list_item)->child );
538
539 // Apply current widget style to the new list_item
540 if (m_widgetStyle)
541 {
542 gtk_widget_set_style( GTK_WIDGET( list_item ), m_widgetStyle );
543 GtkBin *bin = GTK_BIN( list_item );
544 GtkWidget *label = GTK_WIDGET( bin->child );
545 gtk_widget_set_style( label, m_widgetStyle );
546 }
547
548 #if wxUSE_TOOLTIPS
549 if (m_tooltip) m_tooltip->Apply( this );
550 #endif
551 }
552 }
553
554 void wxListBox::DoSetItems( const wxArrayString& items,
555 void **clientData)
556 {
557 Clear();
558
559 DoInsertItems(items, 0);
560
561 if ( clientData )
562 {
563 size_t count = items.GetCount();
564 for ( size_t n = 0; n < count; n++ )
565 {
566 SetClientData(n, clientData[n]);
567 }
568 }
569 }
570
571 // ----------------------------------------------------------------------------
572 // client data
573 // ----------------------------------------------------------------------------
574
575 void wxListBox::DoSetItemClientData( int n, void* clientData )
576 {
577 wxCHECK_RET( m_widget != NULL, wxT("invalid listbox control") );
578
579 wxNode *node = m_clientList.Nth( n );
580 wxCHECK_RET( node, wxT("invalid index in wxListBox::DoSetItemClientData") );
581
582 node->SetData( (wxObject*) clientData );
583 }
584
585 void* wxListBox::DoGetItemClientData( int n ) const
586 {
587 wxCHECK_MSG( m_widget != NULL, NULL, wxT("invalid listbox control") );
588
589 wxNode *node = m_clientList.Nth( n );
590 wxCHECK_MSG( node, NULL, wxT("invalid index in wxListBox::DoGetItemClientData") );
591
592 return node->Data();
593 }
594
595 void wxListBox::DoSetItemClientObject( int n, wxClientData* clientData )
596 {
597 wxCHECK_RET( m_widget != NULL, wxT("invalid listbox control") );
598
599 wxNode *node = m_clientList.Nth( n );
600 wxCHECK_RET( node, wxT("invalid index in wxListBox::DoSetItemClientObject") );
601
602 wxClientData *cd = (wxClientData*) node->Data();
603 delete cd;
604
605 node->SetData( (wxObject*) clientData );
606 }
607
608 wxClientData* wxListBox::DoGetItemClientObject( int n ) const
609 {
610 wxCHECK_MSG( m_widget != NULL, (wxClientData*) NULL, wxT("invalid listbox control") );
611
612 wxNode *node = m_clientList.Nth( n );
613 wxCHECK_MSG( node, (wxClientData *)NULL,
614 wxT("invalid index in wxListBox::DoGetItemClientObject") );
615
616 return (wxClientData*) node->Data();
617 }
618
619 void wxListBox::Clear()
620 {
621 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
622
623 gtk_list_clear_items( m_list, 0, Number() );
624
625 if ( HasClientObjectData() )
626 {
627 // destroy the data (due to Robert's idea of using wxList<wxObject>
628 // and not wxList<wxClientData> we can't just say
629 // m_clientList.DeleteContents(TRUE) - this would crash!
630 wxNode *node = m_clientList.First();
631 while ( node )
632 {
633 delete (wxClientData *)node->Data();
634 node = node->Next();
635 }
636 }
637 m_clientList.Clear();
638
639 if ( m_strings )
640 m_strings->Clear();
641 }
642
643 void wxListBox::Delete( int n )
644 {
645 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
646
647 GList *child = g_list_nth( m_list->children, n );
648
649 wxCHECK_RET( child, wxT("wrong listbox index") );
650
651 GList *list = g_list_append( (GList*) NULL, child->data );
652 gtk_list_remove_items( m_list, list );
653 g_list_free( list );
654
655 wxNode *node = m_clientList.Nth( n );
656 if ( node )
657 {
658 if ( m_clientDataItemsType == ClientData_Object )
659 {
660 wxClientData *cd = (wxClientData*)node->Data();
661 delete cd;
662 }
663
664 m_clientList.DeleteNode( node );
665 }
666
667 if ( m_strings )
668 m_strings->Remove(n);
669 }
670
671 // ----------------------------------------------------------------------------
672 // string list access
673 // ----------------------------------------------------------------------------
674
675 void wxListBox::SetString( int n, const wxString &string )
676 {
677 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
678
679 GList *child = g_list_nth( m_list->children, n );
680 if (child)
681 {
682 GtkBin *bin = GTK_BIN( child->data );
683 GtkLabel *label = GTK_LABEL( bin->child );
684
685 wxString str;
686 #if wxUSE_CHECKLISTBOX
687 if (m_hasCheckBoxes)
688 str += CHECKBOX_STRING;
689 #endif // wxUSE_CHECKLISTBOX
690 str += string;
691
692 gtk_label_set( label, str.mbc_str() );
693 }
694 else
695 {
696 wxFAIL_MSG(wxT("wrong listbox index"));
697 }
698 }
699
700 wxString wxListBox::GetString( int n ) const
701 {
702 wxCHECK_MSG( m_list != NULL, wxT(""), wxT("invalid listbox") );
703
704 GList *child = g_list_nth( m_list->children, n );
705 if (child)
706 {
707 GtkBin *bin = GTK_BIN( child->data );
708 GtkLabel *label = GTK_LABEL( bin->child );
709
710 wxString str = wxString(GET_REAL_LABEL(label->label),*wxConvCurrent);
711
712 return str;
713 }
714
715 wxFAIL_MSG(wxT("wrong listbox index"));
716
717 return wxT("");
718 }
719
720 int wxListBox::GetCount() const
721 {
722 wxCHECK_MSG( m_list != NULL, -1, wxT("invalid listbox") );
723
724 GList *children = m_list->children;
725 return g_list_length(children);
726 }
727
728 int wxListBox::FindString( const wxString &item ) const
729 {
730 wxCHECK_MSG( m_list != NULL, -1, wxT("invalid listbox") );
731
732 GList *child = m_list->children;
733 int count = 0;
734 while (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 if (str == item)
742 return count;
743
744 count++;
745 child = child->next;
746 }
747
748 // it's not an error if the string is not found -> no wxCHECK
749
750 return wxNOT_FOUND;
751 }
752
753 // ----------------------------------------------------------------------------
754 // selection
755 // ----------------------------------------------------------------------------
756
757 int wxListBox::GetSelection() 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 if (GTK_WIDGET(child->data)->state == GTK_STATE_SELECTED) return count;
766 count++;
767 child = child->next;
768 }
769 return -1;
770 }
771
772 int wxListBox::GetSelections( wxArrayInt& aSelections ) const
773 {
774 wxCHECK_MSG( m_list != NULL, -1, wxT("invalid listbox") );
775
776 // get the number of selected items first
777 GList *child = m_list->children;
778 int count = 0;
779 for (child = m_list->children; child != NULL; child = child->next)
780 {
781 if (GTK_WIDGET(child->data)->state == GTK_STATE_SELECTED)
782 count++;
783 }
784
785 aSelections.Empty();
786
787 if (count > 0)
788 {
789 // now fill the list
790 aSelections.Alloc(count); // optimization attempt
791 int i = 0;
792 for (child = m_list->children; child != NULL; child = child->next, i++)
793 {
794 if (GTK_WIDGET(child->data)->state == GTK_STATE_SELECTED)
795 aSelections.Add(i);
796 }
797 }
798
799 return count;
800 }
801
802 bool wxListBox::IsSelected( int n ) const
803 {
804 wxCHECK_MSG( m_list != NULL, FALSE, wxT("invalid listbox") );
805
806 GList *target = g_list_nth( m_list->children, n );
807
808 wxCHECK_MSG( target, FALSE, wxT("invalid listbox index") );
809
810 return (GTK_WIDGET(target->data)->state == GTK_STATE_SELECTED) ;
811 }
812
813 void wxListBox::SetSelection( int n, bool select )
814 {
815 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
816
817 GtkDisableEvents();
818
819 if (select)
820 gtk_list_select_item( m_list, n );
821 else
822 gtk_list_unselect_item( m_list, n );
823
824 GtkEnableEvents();
825 }
826
827 void wxListBox::DoSetFirstItem( int n )
828 {
829 wxCHECK_RET( m_list, wxT("invalid listbox") );
830
831 if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (m_list))
832 return;
833
834 // terribly efficient
835 const gchar *vadjustment_key = "gtk-vadjustment";
836 guint vadjustment_key_id = g_quark_from_static_string (vadjustment_key);
837
838 GtkAdjustment *adjustment =
839 (GtkAdjustment*) gtk_object_get_data_by_id (GTK_OBJECT (m_list), vadjustment_key_id);
840 wxCHECK_RET( adjustment, wxT("invalid listbox code") );
841
842 GList *target = g_list_nth( m_list->children, n );
843 wxCHECK_RET( target, wxT("invalid listbox index") );
844
845 GtkWidget *item = GTK_WIDGET(target->data);
846 wxCHECK_RET( item, wxT("invalid listbox code") );
847
848 if (item->allocation.y == -1)
849 {
850 wxlistbox_idle_struct* data = new wxlistbox_idle_struct;
851 data->m_listbox = this;
852 data->m_item = n;
853 data->m_tag = gtk_idle_add_priority( 800, wxlistbox_idle_callback, (gpointer) data );
854
855 return;
856 }
857
858 float y = item->allocation.y;
859 if (y > adjustment->upper - adjustment->page_size)
860 y = adjustment->upper - adjustment->page_size;
861 gtk_adjustment_set_value( adjustment, y );
862 }
863
864 // ----------------------------------------------------------------------------
865 // helpers
866 // ----------------------------------------------------------------------------
867
868 int wxListBox::GtkGetIndex( GtkWidget *item ) const
869 {
870 if (item)
871 {
872 GList *child = m_list->children;
873 int count = 0;
874 while (child)
875 {
876 if (GTK_WIDGET(child->data) == item) return count;
877 count++;
878 child = child->next;
879 }
880 }
881 return -1;
882 }
883
884 #if wxUSE_TOOLTIPS
885 void wxListBox::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
886 {
887 GList *child = m_list->children;
888 while (child)
889 {
890 gtk_tooltips_set_tip( tips, GTK_WIDGET( child->data ), wxConvCurrent->cWX2MB(tip), (gchar*) NULL );
891 child = child->next;
892 }
893 }
894 #endif // wxUSE_TOOLTIPS
895
896 void wxListBox::GtkDisableEvents()
897 {
898 GList *child = m_list->children;
899 while (child)
900 {
901 gtk_signal_disconnect_by_func( GTK_OBJECT(child->data),
902 GTK_SIGNAL_FUNC(gtk_listitem_select_callback), (gpointer)this );
903
904 if (HasFlag(wxLB_MULTIPLE))
905 gtk_signal_disconnect_by_func( GTK_OBJECT(child->data),
906 GTK_SIGNAL_FUNC(gtk_listitem_deselect_callback), (gpointer)this );
907
908 child = child->next;
909 }
910 }
911
912 void wxListBox::GtkEnableEvents()
913 {
914 GList *child = m_list->children;
915 while (child)
916 {
917 gtk_signal_connect( GTK_OBJECT(child->data), "select",
918 GTK_SIGNAL_FUNC(gtk_listitem_select_callback), (gpointer)this );
919
920 if (HasFlag(wxLB_MULTIPLE))
921 gtk_signal_connect( GTK_OBJECT(child->data), "deselect",
922 GTK_SIGNAL_FUNC(gtk_listitem_deselect_callback), (gpointer)this );
923
924 child = child->next;
925 }
926 }
927
928 GtkWidget *wxListBox::GetConnectWidget()
929 {
930 return GTK_WIDGET(m_list);
931 }
932
933 bool wxListBox::IsOwnGtkWindow( GdkWindow *window )
934 {
935 if (GTK_WIDGET(m_list)->window == window) return TRUE;
936
937 GList *child = m_list->children;
938 while (child)
939 {
940 GtkWidget *bin = GTK_WIDGET( child->data );
941 if (bin->window == window) return TRUE;
942 child = child->next;
943 }
944
945 return FALSE;
946 }
947
948 void wxListBox::ApplyWidgetStyle()
949 {
950 SetWidgetStyle();
951
952 if (m_backgroundColour.Ok())
953 {
954 GdkWindow *window = GTK_WIDGET(m_list)->window;
955 if ( window )
956 {
957 m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) );
958 gdk_window_set_background( window, m_backgroundColour.GetColor() );
959 gdk_window_clear( window );
960 }
961 }
962
963 GList *child = m_list->children;
964 while (child)
965 {
966 gtk_widget_set_style( GTK_WIDGET(child->data), m_widgetStyle );
967
968 GtkBin *bin = GTK_BIN( child->data );
969 GtkWidget *label = GTK_WIDGET( bin->child );
970 gtk_widget_set_style( label, m_widgetStyle );
971
972 child = child->next;
973 }
974 }
975
976 void wxListBox::OnInternalIdle()
977 {
978 wxCursor cursor = m_cursor;
979 if (g_globalCursor.Ok()) cursor = g_globalCursor;
980
981 if (GTK_WIDGET(m_list)->window && cursor.Ok())
982 {
983 /* I now set the cursor the anew in every OnInternalIdle call
984 as setting the cursor in a parent window also effects the
985 windows above so that checking for the current cursor is
986 not possible. */
987
988 gdk_window_set_cursor( GTK_WIDGET(m_list)->window, cursor.GetCursor() );
989
990 GList *child = m_list->children;
991 while (child)
992 {
993 GtkBin *bin = GTK_BIN( child->data );
994 GtkWidget *label = GTK_WIDGET( bin->child );
995
996 if (!label->window)
997 break;
998 else
999 gdk_window_set_cursor( label->window, cursor.GetCursor() );
1000
1001 child = child->next;
1002 }
1003 }
1004
1005 UpdateWindowUI();
1006 }
1007
1008 wxSize wxListBox::DoGetBestSize() const
1009 {
1010 return wxSize(100, 110);
1011 }
1012
1013 #endif