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