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