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