I've put live into Vadim's wxNavigationKeyEvent idea
[wxWidgets.git] / src / gtk / 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/dynarray.h"
16 #include "wx/listbox.h"
17 #include "wx/utils.h"
18 #include "wx/intl.h"
19 #include "wx/checklst.h"
20
21 //-------------------------------------------------------------------------
22 // conditional compilation
23 //-------------------------------------------------------------------------
24
25 #if (GTK_MINOR_VERSION == 1)
26 #if (GTK_MICRO_VERSION >= 5)
27 #define NEW_GTK_SCROLL_CODE
28 #endif
29 #endif
30
31 //-----------------------------------------------------------------------------
32 // data
33 //-----------------------------------------------------------------------------
34
35 extern bool g_blockEventsOnDrag;
36 extern bool g_blockEventsOnScroll;
37
38 //-----------------------------------------------------------------------------
39 // "button_press_event"
40 //-----------------------------------------------------------------------------
41
42 static gint
43 gtk_listbox_button_press_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxListBox *listbox )
44 {
45 if (g_blockEventsOnDrag) return FALSE;
46 if (g_blockEventsOnScroll) return FALSE;
47
48 if (!listbox->HasVMT()) return FALSE;
49
50 if (gdk_event->x > 15) return FALSE;
51
52 int sel = listbox->GetIndex( widget );
53
54 wxCheckListBox *clb = (wxCheckListBox *)listbox;
55
56 clb->Check( sel, !clb->IsChecked(sel) );
57
58 return FALSE;
59 }
60
61 //-----------------------------------------------------------------------------
62 // "key_press_event"
63 //-----------------------------------------------------------------------------
64
65 static gint
66 gtk_listbox_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxListBox *listbox )
67 {
68 if (g_blockEventsOnDrag) return FALSE;
69
70 if (!listbox->HasVMT()) return FALSE;
71
72 if (gdk_event->keyval != ' ') return FALSE;
73
74 int sel = listbox->GetIndex( widget );
75
76 wxCheckListBox *clb = (wxCheckListBox *)listbox;
77
78 clb->Check( sel, !clb->IsChecked(sel) );
79
80 return FALSE;
81 }
82
83 //-----------------------------------------------------------------------------
84 // "select" and "deselect"
85 //-----------------------------------------------------------------------------
86
87 static void gtk_listitem_select_callback( GtkWidget *WXUNUSED(widget), wxListBox *listbox )
88 {
89 if (!listbox->HasVMT()) return;
90 if (g_blockEventsOnDrag) return;
91
92 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, listbox->GetId() );
93
94 wxArrayInt aSelections;
95 int count = listbox->GetSelections(aSelections);
96 if ( count > 0 )
97 {
98 event.m_commandInt = aSelections[0] ;
99 event.m_clientData = listbox->GetClientData( event.m_commandInt );
100 wxString str(listbox->GetString(event.m_commandInt));
101 if (str != "") event.m_commandString = copystring((char *)(const char *)str);
102 }
103 else
104 {
105 event.m_commandInt = -1 ;
106 event.m_commandString = copystring("") ;
107 }
108
109 event.SetEventObject( listbox );
110
111 listbox->GetEventHandler()->ProcessEvent( event );
112 if (event.m_commandString) delete[] event.m_commandString ;
113 }
114
115 //-----------------------------------------------------------------------------
116 // wxListBox
117 //-----------------------------------------------------------------------------
118
119 IMPLEMENT_DYNAMIC_CLASS(wxListBox,wxControl)
120
121 wxListBox::wxListBox()
122 {
123 m_list = (GtkList *) NULL;
124 m_hasCheckBoxes = FALSE;
125 }
126
127 bool wxListBox::Create( wxWindow *parent, wxWindowID id,
128 const wxPoint &pos, const wxSize &size,
129 int n, const wxString choices[],
130 long style, const wxValidator& validator, const wxString &name )
131 {
132 m_needParent = TRUE;
133 m_acceptsFocus = TRUE;
134
135 PreCreation( parent, id, pos, size, style, name );
136
137 SetValidator( validator );
138
139 m_widget = gtk_scrolled_window_new( (GtkAdjustment*) NULL, (GtkAdjustment*) NULL );
140 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
141 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
142
143 m_list = GTK_LIST( gtk_list_new() );
144
145 GtkSelectionMode mode = GTK_SELECTION_BROWSE;
146 if (style & wxLB_MULTIPLE)
147 mode = GTK_SELECTION_MULTIPLE;
148 else if (style & wxLB_EXTENDED)
149 mode = GTK_SELECTION_EXTENDED;
150
151 gtk_list_set_selection_mode( GTK_LIST(m_list), mode );
152
153 #ifdef NEW_GTK_SCROLL_CODE
154 gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(m_widget), GTK_WIDGET(m_list) );
155 #else
156 gtk_container_add( GTK_CONTAINER(m_widget), GTK_WIDGET(m_list) );
157 #endif
158
159 #ifdef __WXDEBUG__
160 debug_focus_in( m_widget, "wxListBox::m_widget", name );
161
162 debug_focus_in( GTK_WIDGET(m_list), "wxListBox::m_list", name );
163
164 GtkScrolledWindow *s_window = GTK_SCROLLED_WINDOW(m_widget);
165
166 debug_focus_in( s_window->hscrollbar, "wxWindow::hsrcollbar", name );
167 debug_focus_in( s_window->vscrollbar, "wxWindow::vsrcollbar", name );
168
169 #ifdef NEW_GTK_SCROLL_CODE
170 GtkViewport *viewport = GTK_VIEWPORT(s_window->child);
171 #else
172 GtkViewport *viewport = GTK_VIEWPORT(s_window->viewport);
173 #endif
174
175 debug_focus_in( GTK_WIDGET(viewport), "wxWindow::viewport", name );
176 #endif
177
178 gtk_widget_show( GTK_WIDGET(m_list) );
179
180 wxSize newSize = size;
181 if (newSize.x == -1) newSize.x = 100;
182 if (newSize.y == -1) newSize.y = 110;
183 SetSize( newSize.x, newSize.y );
184
185 for (int i = 0; i < n; i++)
186 {
187 m_clientDataList.Append( (wxObject*) NULL );
188 m_clientObjectList.Append( (wxObject*) NULL );
189
190 GtkWidget *list_item;
191
192 if (m_hasCheckBoxes)
193 {
194 wxString str = "[-] ";
195 str += choices[i];
196 list_item = gtk_list_item_new_with_label( str );
197 }
198 else
199 {
200 list_item = gtk_list_item_new_with_label( choices[i] );
201 }
202
203 #ifdef __WXDEBUG__
204 debug_focus_in( list_item, "wxListBox::list_item", name );
205 #endif
206
207 gtk_container_add( GTK_CONTAINER(m_list), list_item );
208
209 gtk_signal_connect( GTK_OBJECT(list_item), "select",
210 GTK_SIGNAL_FUNC(gtk_listitem_select_callback), (gpointer)this );
211
212 if (style & wxLB_MULTIPLE)
213 gtk_signal_connect( GTK_OBJECT(list_item), "deselect",
214 GTK_SIGNAL_FUNC(gtk_listitem_select_callback), (gpointer)this );
215
216 if (m_hasCheckBoxes)
217 {
218 gtk_signal_connect( GTK_OBJECT(list_item),
219 "button_press_event",
220 (GtkSignalFunc)gtk_listbox_button_press_callback,
221 (gpointer) this );
222
223 gtk_signal_connect( GTK_OBJECT(list_item),
224 "key_press_event",
225 (GtkSignalFunc)gtk_listbox_key_press_callback,
226 (gpointer)this );
227 }
228
229 ConnectWidget( list_item );
230
231 gtk_widget_show( list_item );
232 }
233
234 m_parent->AddChild( this );
235
236 (m_parent->m_insertCallback)( m_parent, this );
237
238 PostCreation();
239
240 gtk_widget_realize( GTK_WIDGET(m_list) );
241
242 SetBackgroundColour( parent->GetBackgroundColour() );
243 SetForegroundColour( parent->GetForegroundColour() );
244
245 Show( TRUE );
246
247 return TRUE;
248 }
249
250 wxListBox::~wxListBox()
251 {
252 Clear();
253 }
254
255 void wxListBox::AppendCommon( const wxString &item )
256 {
257 wxCHECK_RET( m_list != NULL, "invalid listbox" );
258
259 GtkWidget *list_item;
260
261 if (m_hasCheckBoxes)
262 {
263 wxString str = "[-] ";
264 str += item;
265 list_item = gtk_list_item_new_with_label( str );
266 }
267 else
268 {
269 list_item = gtk_list_item_new_with_label( item );
270 }
271
272 gtk_signal_connect( GTK_OBJECT(list_item), "select",
273 GTK_SIGNAL_FUNC(gtk_listitem_select_callback), (gpointer)this );
274
275 if (GetWindowStyleFlag() & wxLB_MULTIPLE)
276 gtk_signal_connect( GTK_OBJECT(list_item), "deselect",
277 GTK_SIGNAL_FUNC(gtk_listitem_select_callback), (gpointer)this );
278
279 gtk_container_add( GTK_CONTAINER(m_list), list_item );
280
281 if (m_widgetStyle) ApplyWidgetStyle();
282
283 if (m_hasCheckBoxes)
284 {
285 gtk_signal_connect( GTK_OBJECT(list_item),
286 "button_press_event",
287 (GtkSignalFunc)gtk_listbox_button_press_callback,
288 (gpointer) this );
289
290 gtk_signal_connect( GTK_OBJECT(list_item),
291 "key_press_event",
292 (GtkSignalFunc)gtk_listbox_key_press_callback,
293 (gpointer)this );
294 }
295
296 gtk_widget_show( list_item );
297
298 ConnectWidget( list_item );
299
300 #ifndef NEW_GTK_DND_CODE
301 if (m_dropTarget) m_dropTarget->RegisterWidget( list_item );
302 #endif
303 }
304
305 void wxListBox::Append( const wxString &item )
306 {
307 m_clientDataList.Append( (wxObject*) NULL );
308 m_clientObjectList.Append( (wxObject*) NULL );
309
310 AppendCommon( item );
311 }
312
313 void wxListBox::Append( const wxString &item, void *clientData )
314 {
315 m_clientDataList.Append( (wxObject*) clientData );
316 m_clientObjectList.Append( (wxObject*) NULL );
317
318 AppendCommon( item );
319 }
320
321 void wxListBox::Append( const wxString &item, wxClientData *clientData )
322 {
323 m_clientObjectList.Append( (wxObject*) clientData );
324 m_clientDataList.Append( (wxObject*) NULL );
325
326 AppendCommon( item );
327 }
328
329 void wxListBox::SetClientData( int n, void* clientData )
330 {
331 wxCHECK_RET( m_widget != NULL, "invalid combobox" );
332
333 wxNode *node = m_clientDataList.Nth( n );
334 if (!node) return;
335
336 node->SetData( (wxObject*) clientData );
337 }
338
339 void* wxListBox::GetClientData( int n )
340 {
341 wxCHECK_MSG( m_widget != NULL, NULL, "invalid combobox" );
342
343 wxNode *node = m_clientDataList.Nth( n );
344 if (!node) return NULL;
345
346 return node->Data();
347 }
348
349 void wxListBox::SetClientObject( int n, wxClientData* clientData )
350 {
351 wxCHECK_RET( m_widget != NULL, "invalid combobox" );
352
353 wxNode *node = m_clientObjectList.Nth( n );
354 if (!node) return;
355
356 wxClientData *cd = (wxClientData*) node->Data();
357 if (cd) delete cd;
358
359 node->SetData( (wxObject*) clientData );
360 }
361
362 wxClientData* wxListBox::GetClientObject( int n )
363 {
364 wxCHECK_MSG( m_widget != NULL, (wxClientData*)NULL, "invalid combobox" );
365
366 wxNode *node = m_clientObjectList.Nth( n );
367 if (!node) return (wxClientData*) NULL;
368
369 return (wxClientData*) node->Data();
370 }
371
372 void wxListBox::Clear()
373 {
374 wxCHECK_RET( m_list != NULL, "invalid listbox" );
375
376 gtk_list_clear_items( m_list, 0, Number() );
377
378 wxNode *node = m_clientObjectList.First();
379 while (node)
380 {
381 wxClientData *cd = (wxClientData*)node->Data();
382 if (cd) delete cd;
383 node = node->Next();
384 }
385 m_clientObjectList.Clear();
386
387 m_clientDataList.Clear();
388 }
389
390 void wxListBox::Delete( int n )
391 {
392 wxCHECK_RET( m_list != NULL, "invalid listbox" );
393
394 GList *child = g_list_nth( m_list->children, n );
395
396 wxCHECK_RET( child, "wrong listbox index" );
397
398 GList *list = g_list_append( NULL, child->data );
399 gtk_list_remove_items( m_list, list );
400 g_list_free( list );
401
402 wxNode *node = m_clientObjectList.Nth( n );
403 if (node)
404 {
405 wxClientData *cd = (wxClientData*)node->Data();
406 if (cd) delete cd;
407 m_clientObjectList.DeleteNode( node );
408 }
409
410 node = m_clientDataList.Nth( n );
411 if (node)
412 {
413 m_clientDataList.DeleteNode( node );
414 }
415 }
416
417 void wxListBox::Deselect( int n )
418 {
419 wxCHECK_RET( m_list != NULL, "invalid listbox" );
420
421 gtk_list_unselect_item( m_list, n );
422 }
423
424 int wxListBox::FindString( const wxString &item ) const
425 {
426 wxCHECK_MSG( m_list != NULL, -1, "invalid listbox" );
427
428 GList *child = m_list->children;
429 int count = 0;
430 while (child)
431 {
432 GtkBin *bin = GTK_BIN( child->data );
433 GtkLabel *label = GTK_LABEL( bin->child );
434
435 wxString str = label->label;
436 if (m_hasCheckBoxes) str.Remove( 0, 4 );
437
438 if (str == item) return count;
439
440 count++;
441 child = child->next;
442 }
443
444 // it's not an error if the string is not found -> no wxCHECK
445
446 return -1;
447 }
448
449 int wxListBox::GetSelection() const
450 {
451 wxCHECK_MSG( m_list != NULL, -1, "invalid listbox" );
452
453 GList *child = m_list->children;
454 int count = 0;
455 while (child)
456 {
457 if (GTK_WIDGET(child->data)->state == GTK_STATE_SELECTED) return count;
458 count++;
459 child = child->next;
460 }
461 return -1;
462 }
463
464 int wxListBox::GetSelections( wxArrayInt& aSelections ) const
465 {
466 wxCHECK_MSG( m_list != NULL, -1, "invalid listbox" );
467
468 // get the number of selected items first
469 GList *child = m_list->children;
470 int count = 0;
471 for (child = m_list->children; child != NULL; child = child->next)
472 {
473 if (GTK_WIDGET(child->data)->state == GTK_STATE_SELECTED)
474 count++;
475 }
476
477 aSelections.Empty();
478
479 if (count > 0)
480 {
481 // now fill the list
482 aSelections.Alloc(count); // optimization attempt
483 int i = 0;
484 for (child = m_list->children; child != NULL; child = child->next, i++)
485 {
486 if (GTK_WIDGET(child->data)->state == GTK_STATE_SELECTED)
487 aSelections.Add(i);
488 }
489 }
490
491 return count;
492 }
493
494 wxString wxListBox::GetString( int n ) const
495 {
496 wxCHECK_MSG( m_list != NULL, "", "invalid listbox" );
497
498 GList *child = g_list_nth( m_list->children, n );
499 if (child)
500 {
501 GtkBin *bin = GTK_BIN( child->data );
502 GtkLabel *label = GTK_LABEL( bin->child );
503
504 wxString str = label->label;
505 if (m_hasCheckBoxes) str.Remove( 0, 4 );
506
507 return str;
508 }
509 wxFAIL_MSG("wrong listbox index");
510 return "";
511 }
512
513 wxString wxListBox::GetStringSelection() const
514 {
515 wxCHECK_MSG( m_list != NULL, "", "invalid listbox" );
516
517 GList *selection = m_list->selection;
518 if (selection)
519 {
520 GtkBin *bin = GTK_BIN( selection->data );
521 GtkLabel *label = GTK_LABEL( bin->child );
522
523 wxString str = label->label;
524 if (m_hasCheckBoxes) str.Remove( 0, 4 );
525
526 return str;
527 }
528
529 wxFAIL_MSG("no listbox selection available");
530 return "";
531 }
532
533 int wxListBox::Number()
534 {
535 wxCHECK_MSG( m_list != NULL, -1, "invalid listbox" );
536
537 GList *child = m_list->children;
538 int count = 0;
539 while (child) { count++; child = child->next; }
540 return count;
541 }
542
543 bool wxListBox::Selected( int n )
544 {
545 wxCHECK_MSG( m_list != NULL, FALSE, "invalid listbox" );
546
547 GList *target = g_list_nth( m_list->children, n );
548 if (target)
549 {
550 GList *child = m_list->selection;
551 while (child)
552 {
553 if (child->data == target->data) return TRUE;
554 child = child->next;
555 }
556 }
557 wxFAIL_MSG("wrong listbox index");
558 return FALSE;
559 }
560
561 void wxListBox::Set( int WXUNUSED(n), const wxString *WXUNUSED(choices) )
562 {
563 wxFAIL_MSG("wxListBox::Set not implemented");
564 }
565
566 void wxListBox::SetFirstItem( int WXUNUSED(n) )
567 {
568 wxFAIL_MSG("wxListBox::SetFirstItem not implemented");
569 }
570
571 void wxListBox::SetFirstItem( const wxString &WXUNUSED(item) )
572 {
573 wxFAIL_MSG("wxListBox::SetFirstItem not implemented");
574 }
575
576 void wxListBox::SetSelection( int n, bool select )
577 {
578 wxCHECK_RET( m_list != NULL, "invalid listbox" );
579
580 if (select)
581 gtk_list_select_item( m_list, n );
582 else
583 gtk_list_unselect_item( m_list, n );
584 }
585
586 void wxListBox::SetString( int n, const wxString &string )
587 {
588 wxCHECK_RET( m_list != NULL, "invalid listbox" );
589
590 GList *child = g_list_nth( m_list->children, n );
591 if (child)
592 {
593 GtkBin *bin = GTK_BIN( child->data );
594 GtkLabel *label = GTK_LABEL( bin->child );
595
596 wxString str;
597 if (m_hasCheckBoxes) str += "[-] ";
598 str += string;
599
600 gtk_label_set( label, str );
601 }
602 else
603 {
604 wxFAIL_MSG("wrong listbox index");
605 }
606 }
607
608 void wxListBox::SetStringSelection( const wxString &string, bool select )
609 {
610 wxCHECK_RET( m_list != NULL, "invalid listbox" );
611
612 SetSelection( FindString(string), select );
613 }
614
615 int wxListBox::GetIndex( GtkWidget *item ) const
616 {
617 if (item)
618 {
619 GList *child = m_list->children;
620 int count = 0;
621 while (child)
622 {
623 if (GTK_WIDGET(child->data) == item) return count;
624 count++;
625 child = child->next;
626 }
627 }
628 return -1;
629 }
630
631 void wxListBox::SetDropTarget( wxDropTarget *dropTarget )
632 {
633 wxCHECK_RET( m_list != NULL, "invalid listbox" );
634
635 #ifndef NEW_GTK_DND_CODE
636 if (m_dropTarget)
637 {
638 GList *child = m_list->children;
639 while (child)
640 {
641 m_dropTarget->UnregisterWidget( GTK_WIDGET( child->data ) );
642 child = child->next;
643 }
644 }
645 #endif
646
647 wxWindow::SetDropTarget( dropTarget );
648
649 #ifndef NEW_GTK_DND_CODE
650 if (m_dropTarget)
651 {
652 GList *child = m_list->children;
653 while (child)
654 {
655 m_dropTarget->RegisterWidget( GTK_WIDGET( child->data ) );
656 child = child->next;
657 }
658 }
659 #endif
660 }
661
662 GtkWidget *wxListBox::GetConnectWidget()
663 {
664 return GTK_WIDGET(m_list);
665 }
666
667 bool wxListBox::IsOwnGtkWindow( GdkWindow *window )
668 {
669 if (wxWindow::IsOwnGtkWindow( window )) return TRUE;
670
671 GList *child = m_list->children;
672 while (child)
673 {
674 GtkWidget *bin = GTK_WIDGET( child->data );
675 if (bin->window == window) return TRUE;
676 child = child->next;
677 }
678
679 return FALSE;
680 }
681
682 void wxListBox::ApplyWidgetStyle()
683 {
684 SetWidgetStyle();
685
686 if (m_backgroundColour.Ok())
687 {
688 GdkWindow *window = GTK_WIDGET(m_list)->window;
689 m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) );
690 gdk_window_set_background( window, m_backgroundColour.GetColor() );
691 gdk_window_clear( window );
692 }
693
694 GList *child = m_list->children;
695 while (child)
696 {
697 gtk_widget_set_style( GTK_WIDGET(child->data), m_widgetStyle );
698
699 GtkBin *bin = GTK_BIN( child->data );
700 GtkWidget *label = GTK_WIDGET( bin->child );
701 gtk_widget_set_style( label, m_widgetStyle );
702
703 child = child->next;
704 }
705 }