]> git.saurik.com Git - wxWidgets.git/blame - src/gtk1/listbox.cpp
better fix for infinite loop in HandleOnNavigationKey() when wxUSE_STL==1 and start_n...
[wxWidgets.git] / src / gtk1 / listbox.cpp
CommitLineData
c801d85f 1/////////////////////////////////////////////////////////////////////////////
3cbab641 2// Name: src/gtk1/listbox.cpp
c801d85f
KB
3// Purpose:
4// Author: Robert Roebling
f96aa4d9
RR
5// Id: $Id$
6// Copyright: (c) 1998 Robert Roebling
65571936 7// Licence: wxWindows licence
c801d85f
KB
8/////////////////////////////////////////////////////////////////////////////
9
14f355c2
VS
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
dcf924a3
RR
13#if wxUSE_LISTBOX
14
2da2f941 15#include "wx/listbox.h"
dcf924a3 16#include "wx/dynarray.h"
2da2f941 17#include "wx/arrstr.h"
09cf7c58 18#include "wx/utils.h"
caaa4cfd
RR
19#include "wx/intl.h"
20#include "wx/checklst.h"
72a16063 21#include "wx/settings.h"
3cbab641 22#include "wx/gtk1/private.h"
291a8f20
RR
23
24#if wxUSE_TOOLTIPS
b1170810 25#include "wx/tooltip.h"
291a8f20 26#endif
c801d85f 27
4a82abc8 28#include <gdk/gdk.h>
16c1f79c
RR
29#include <gtk/gtk.h>
30#include <gdk/gdkkeysyms.h>
83624f79 31
acfd422a
RR
32//-----------------------------------------------------------------------------
33// idle system
34//-----------------------------------------------------------------------------
35
36extern void wxapp_install_idle_handler();
37extern bool g_isIdle;
38
66bd6b93
RR
39//-----------------------------------------------------------------------------
40// data
41//-----------------------------------------------------------------------------
42
d7fa7eaa
RR
43extern bool g_blockEventsOnDrag;
44extern bool g_blockEventsOnScroll;
45extern wxCursor g_globalCursor;
46extern wxWindowGTK *g_delayedFocus;
c9433ea9
RR
47extern wxWindowGTK *g_focusWindow;
48extern wxWindowGTK *g_focusWindowLast;
caaa4cfd 49
caf6e6de 50static bool g_hasDoubleClicked = false;
7a30ee8f 51
4a82abc8
RR
52//-----------------------------------------------------------------------------
53// idle callback for SetFirstItem
54//-----------------------------------------------------------------------------
55
56struct wxlistbox_idle_struct
57{
58 wxListBox *m_listbox;
59 int m_item;
60 gint m_tag;
61};
62
865bb325
VZ
63extern "C" {
64static gint wxlistbox_idle_callback( gpointer gdata )
4a82abc8
RR
65{
66 wxlistbox_idle_struct* data = (wxlistbox_idle_struct*) gdata;
67 gdk_threads_enter();
68
69 gtk_idle_remove( data->m_tag );
f96b15a3 70
992295c4
VZ
71 // check that the items haven't been deleted from the listbox since we had
72 // installed this callback
73 wxListBox *lbox = data->m_listbox;
8228b893 74 if ( data->m_item < (int)lbox->GetCount() )
992295c4
VZ
75 {
76 lbox->SetFirstItem( data->m_item );
77 }
f96b15a3 78
4a82abc8 79 delete data;
f96b15a3 80
4a82abc8
RR
81 gdk_threads_leave();
82
83 return TRUE;
84}
865bb325 85}
4a82abc8 86
c9433ea9
RR
87//-----------------------------------------------------------------------------
88// "focus_in_event"
89//-----------------------------------------------------------------------------
90
865bb325 91extern "C" {
c9433ea9
RR
92static gint gtk_listitem_focus_in_callback( GtkWidget *widget,
93 GdkEvent *WXUNUSED(event),
94 wxWindow *win )
95{
96 if (g_isIdle)
97 wxapp_install_idle_handler();
98
99 g_focusWindowLast =
100 g_focusWindow = win;
101
102 // does the window itself think that it has the focus?
103 if ( !win->m_hasFocus )
104 {
105 // not yet, notify it
caf6e6de 106 win->m_hasFocus = true;
17a1ebd1 107
c9433ea9
RR
108 wxChildFocusEvent eventChildFocus(win);
109 (void)win->GetEventHandler()->ProcessEvent(eventChildFocus);
110
111 wxFocusEvent eventFocus(wxEVT_SET_FOCUS, win->GetId());
112 eventFocus.SetEventObject(win);
113
114 (void)win->GetEventHandler()->ProcessEvent(eventFocus);
115 }
116
117 return FALSE;
118}
865bb325 119}
c9433ea9
RR
120
121//-----------------------------------------------------------------------------
122// "focus_out_event"
123//-----------------------------------------------------------------------------
124
865bb325 125extern "C" {
c9433ea9
RR
126static gint gtk_listitem_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk_event, wxWindowGTK *win )
127{
128 if (g_isIdle)
129 wxapp_install_idle_handler();
130
131 g_focusWindow = (wxWindowGTK *)NULL;
132
133 // don't send the window a kill focus event if it thinks that it doesn't
134 // have focus already
135 if ( win->m_hasFocus )
136 {
caf6e6de 137 win->m_hasFocus = false;
c9433ea9
RR
138
139 wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
140 event.SetEventObject( win );
141
142 // even if we did process the event in wx code, still let GTK itself
143 // process it too as otherwise bad things happen, especially in GTK2
144 // where the text control simply aborts the program if it doesn't get
145 // the matching focus out event
146 (void)win->GetEventHandler()->ProcessEvent( event );
147 }
148
149 return FALSE;
150}
865bb325 151}
c9433ea9 152
caaa4cfd 153//-----------------------------------------------------------------------------
7a30ee8f 154// "button_release_event"
caaa4cfd
RR
155//-----------------------------------------------------------------------------
156
7a30ee8f
RR
157/* we would normally emit a wxEVT_COMMAND_LISTBOX_DOUBLECLICKED event once
158 a GDK_2BUTTON_PRESS occurs, but this has the particular problem of the
159 listbox keeping the focus until it receives a GDK_BUTTON_RELEASE event.
160 this can lead to race conditions so that we emit the dclick event
161 after the GDK_BUTTON_RELEASE event after the GDK_2BUTTON_PRESS event */
162
865bb325 163extern "C" {
ff8bfdbb 164static gint
014b0d06
VZ
165gtk_listbox_button_release_callback( GtkWidget * WXUNUSED(widget),
166 GdkEventButton * WXUNUSED(gdk_event),
167 wxListBox *listbox )
caaa4cfd 168{
acfd422a
RR
169 if (g_isIdle) wxapp_install_idle_handler();
170
caaa4cfd
RR
171 if (g_blockEventsOnDrag) return FALSE;
172 if (g_blockEventsOnScroll) return FALSE;
173
a2053b27 174 if (!listbox->m_hasVMT) return FALSE;
caaa4cfd 175
7a30ee8f 176 if (!g_hasDoubleClicked) return FALSE;
ff8bfdbb 177
6c8a980f
VZ
178 wxCommandEvent event( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, listbox->GetId() );
179 event.SetEventObject( listbox );
ff8bfdbb 180
6c8a980f
VZ
181 wxArrayInt aSelections;
182 int n, count = listbox->GetSelections(aSelections);
183 if ( count > 0 )
184 {
185 n = aSelections[0];
186 if ( listbox->HasClientObjectData() )
187 event.SetClientObject( listbox->GetClientObject(n) );
188 else if ( listbox->HasClientUntypedData() )
189 event.SetClientData( listbox->GetClientData(n) );
190 event.SetString( listbox->GetString(n) );
191 }
192 else
193 {
194 n = -1;
195 }
5b077d48 196
687706f5 197 event.SetInt(n);
6c8a980f
VZ
198
199 listbox->GetEventHandler()->ProcessEvent( event );
ff8bfdbb 200
7a30ee8f
RR
201 return FALSE;
202}
865bb325 203}
7a30ee8f
RR
204
205//-----------------------------------------------------------------------------
206// "button_press_event"
207//-----------------------------------------------------------------------------
208
865bb325 209extern "C" {
7a30ee8f 210static gint
014b0d06
VZ
211gtk_listbox_button_press_callback( GtkWidget *widget,
212 GdkEventButton *gdk_event,
213 wxListBox *listbox )
7a30ee8f
RR
214{
215 if (g_isIdle) wxapp_install_idle_handler();
216
217 if (g_blockEventsOnDrag) return FALSE;
218 if (g_blockEventsOnScroll) return FALSE;
219
220 if (!listbox->m_hasVMT) return FALSE;
221
11e1c70d 222 int sel = listbox->GtkGetIndex( widget );
7a30ee8f 223
88ac883a 224#if wxUSE_CHECKLISTBOX
7a30ee8f
RR
225 if ((listbox->m_hasCheckBoxes) && (gdk_event->x < 15) && (gdk_event->type != GDK_2BUTTON_PRESS))
226 {
227 wxCheckListBox *clb = (wxCheckListBox *)listbox;
228
229 clb->Check( sel, !clb->IsChecked(sel) );
230
231 wxCommandEvent event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, listbox->GetId() );
232 event.SetEventObject( listbox );
233 event.SetInt( sel );
234 listbox->GetEventHandler()->ProcessEvent( event );
4f22cf8d 235 }
88ac883a 236#endif // wxUSE_CHECKLISTBOX
014b0d06 237
4fcfa27c
RR
238 if ((gdk_event->state == 0) &&
239 (((listbox->GetWindowStyleFlag() & wxLB_MULTIPLE) != 0) ||
240 ((listbox->GetWindowStyleFlag() & wxLB_EXTENDED) != 0)) )
241 {
caf6e6de 242 listbox->m_blockEvent = true;
376c0148
RR
243
244 int i;
245 for (i = 0; i < (int)listbox->GetCount(); i++)
246 if (i != sel)
247 gtk_list_unselect_item( GTK_LIST(listbox->m_list), i );
17a1ebd1 248
caf6e6de 249 listbox->m_blockEvent = false;
17a1ebd1 250
376c0148 251 return false;
4fcfa27c
RR
252 }
253
7a30ee8f
RR
254 /* emit wxEVT_COMMAND_LISTBOX_DOUBLECLICKED later */
255 g_hasDoubleClicked = (gdk_event->type == GDK_2BUTTON_PRESS);
4f22cf8d 256
caaa4cfd
RR
257 return FALSE;
258}
865bb325 259}
66bd6b93 260
1144d24d
RR
261//-----------------------------------------------------------------------------
262// "key_press_event"
263//-----------------------------------------------------------------------------
264
865bb325 265extern "C" {
ff8bfdbb 266static gint
1144d24d
RR
267gtk_listbox_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxListBox *listbox )
268{
9ef6ff8c 269 if (g_isIdle)
354aa1e3 270 wxapp_install_idle_handler();
acfd422a 271
9ef6ff8c 272 if (g_blockEventsOnDrag)
354aa1e3 273 return FALSE;
1144d24d 274
caf6e6de 275 bool ret = false;
1144d24d 276
354aa1e3
RR
277 if ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab))
278 {
279 wxNavigationKeyEvent new_event;
280 /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */
281 new_event.SetDirection( (gdk_event->keyval == GDK_Tab) );
282 /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
283 new_event.SetWindowChange( (gdk_event->state & GDK_CONTROL_MASK) );
284 new_event.SetCurrentFocus( listbox );
285 ret = listbox->GetEventHandler()->ProcessEvent( new_event );
286 }
9ef6ff8c 287
4a82abc8
RR
288 if ((gdk_event->keyval == GDK_Return) && (!ret))
289 {
290 // eat return in all modes
caf6e6de 291 ret = true;
4a82abc8 292 }
f96b15a3 293
354aa1e3 294#if wxUSE_CHECKLISTBOX
1e8d2f69 295 if ((gdk_event->keyval == ' ') && (listbox->m_hasCheckBoxes) && (!ret))
354aa1e3
RR
296 {
297 int sel = listbox->GtkGetIndex( widget );
ff8bfdbb 298
354aa1e3 299 wxCheckListBox *clb = (wxCheckListBox *)listbox;
ff8bfdbb 300
354aa1e3 301 clb->Check( sel, !clb->IsChecked(sel) );
ff8bfdbb 302
354aa1e3
RR
303 wxCommandEvent new_event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, listbox->GetId() );
304 new_event.SetEventObject( listbox );
305 new_event.SetInt( sel );
306 ret = listbox->GetEventHandler()->ProcessEvent( new_event );
307 }
308#endif // wxUSE_CHECKLISTBOX
ff8bfdbb 309
fec195b2 310 // Check or uncheck item with SPACE
17a1ebd1 311 if ((gdk_event->keyval == ' ') && (!ret) &&
fec195b2
RR
312 (((listbox->GetWindowStyleFlag() & wxLB_MULTIPLE) != 0) ||
313 ((listbox->GetWindowStyleFlag() & wxLB_EXTENDED) != 0)) )
314 {
315 int sel = listbox->GtkGetIndex( widget );
17a1ebd1 316
fec195b2
RR
317 if (sel != -1)
318 {
caf6e6de 319 ret = true;
17a1ebd1 320
fec195b2
RR
321 if (listbox->IsSelected( sel ))
322 gtk_list_unselect_item( listbox->m_list, sel );
323 else
324 gtk_list_select_item( listbox->m_list, sel );
17a1ebd1 325
fec195b2
RR
326 wxCommandEvent new_event(wxEVT_COMMAND_LISTBOX_SELECTED, listbox->GetId() );
327 new_event.SetEventObject( listbox );
328 wxArrayInt aSelections;
329 int n, count = listbox->GetSelections(aSelections);
330 if ( count > 0 )
331 {
332 n = aSelections[0];
333 if ( listbox->HasClientObjectData() )
334 new_event.SetClientObject( listbox->GetClientObject(n) );
335 else if ( listbox->HasClientUntypedData() )
336 new_event.SetClientData( listbox->GetClientData(n) );
337 new_event.SetString( listbox->GetString(n) );
338 }
339 else
340 {
341 n = -1;
342 }
687706f5 343 new_event.SetInt(n);
fec195b2
RR
344 listbox->GetEventHandler()->ProcessEvent( new_event );
345 }
346 }
17a1ebd1 347
354aa1e3
RR
348 if (ret)
349 {
350 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
351 return TRUE;
352 }
ff8bfdbb 353
1144d24d
RR
354 return FALSE;
355}
865bb325 356}
1144d24d 357
c801d85f 358//-----------------------------------------------------------------------------
a60c99e6 359// "select" and "deselect"
c801d85f
KB
360//-----------------------------------------------------------------------------
361
9c9c3d7a
VZ
362static void gtk_listitem_select_cb( GtkWidget *widget,
363 wxListBox *listbox,
364 bool is_selection )
c801d85f 365{
acfd422a
RR
366 if (g_isIdle) wxapp_install_idle_handler();
367
a2053b27 368 if (!listbox->m_hasVMT) return;
fd0eed64 369 if (g_blockEventsOnDrag) return;
8161b5b9 370
bac95742 371 if (listbox->m_blockEvent) return;
9ef6ff8c 372
fd0eed64 373 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, listbox->GetId() );
6c8a980f 374 event.SetEventObject( listbox );
8161b5b9 375
9c9c3d7a
VZ
376 // indicate whether this is a selection or a deselection
377 event.SetExtraLong( is_selection );
159b66c0
RR
378
379 if ((listbox->GetWindowStyleFlag() & wxLB_SINGLE) != 0)
380 {
381 int sel = listbox->GtkGetIndex( widget );
382
383 if (listbox->m_prevSelection != sel)
384 gtk_list_unselect_item( listbox->m_list, listbox->m_prevSelection );
385
386 listbox->m_prevSelection = sel;
387 }
dcf40a56 388
09cf7c58 389 wxArrayInt aSelections;
2ee3ee1b 390 int n, count = listbox->GetSelections(aSelections);
09cf7c58
RR
391 if ( count > 0 )
392 {
2ee3ee1b 393 n = aSelections[0];
6c8a980f
VZ
394 if ( listbox->HasClientObjectData() )
395 event.SetClientObject( listbox->GetClientObject(n) );
396 else if ( listbox->HasClientUntypedData() )
397 event.SetClientData( listbox->GetClientData(n) );
398 event.SetString( listbox->GetString(n) );
09cf7c58
RR
399 }
400 else
401 {
2ee3ee1b 402 n = -1;
09cf7c58
RR
403 }
404
687706f5 405 event.SetInt(n);
2ee3ee1b 406
159b66c0
RR
407// No longer required with new code in wxLB_SINGLE
408// listbox->GetEventHandler()->AddPendingEvent( event );
409 listbox->GetEventHandler()->ProcessEvent( event );
6de97a3b 410}
c801d85f 411
865bb325
VZ
412extern "C" {
413static void gtk_listitem_select_callback( GtkWidget *widget, wxListBox *listbox )
414{
415 gtk_listitem_select_cb( widget, listbox, TRUE );
416}
417}
418
419extern "C" {
420static void gtk_listitem_deselect_callback( GtkWidget *widget, wxListBox *listbox )
421{
422 gtk_listitem_select_cb( widget, listbox, FALSE );
423}
424}
425
a60c99e6
RR
426//-----------------------------------------------------------------------------
427// wxListBox
c801d85f
KB
428//-----------------------------------------------------------------------------
429
865bb325 430extern "C" {
f2e93537
RR
431static gint
432gtk_listbox_realized_callback( GtkWidget *m_widget, wxListBox *win )
433{
434 if (g_isIdle)
435 wxapp_install_idle_handler();
17a1ebd1 436
f2e93537
RR
437 GList *child = win->m_list->children;
438 for (child = win->m_list->children; child != NULL; child = child->next)
439 gtk_widget_show( GTK_WIDGET(child->data) );
17a1ebd1 440
f2e93537
RR
441 return false;
442}
865bb325 443}
f2e93537
RR
444
445//-----------------------------------------------------------------------------
446// wxListBox
447//-----------------------------------------------------------------------------
448
c801d85f
KB
449IMPLEMENT_DYNAMIC_CLASS(wxListBox,wxControl)
450
2ee3ee1b
VZ
451// ----------------------------------------------------------------------------
452// construction
453// ----------------------------------------------------------------------------
454
fd0eed64 455wxListBox::wxListBox()
c801d85f 456{
fd0eed64 457 m_list = (GtkList *) NULL;
88ac883a 458#if wxUSE_CHECKLISTBOX
caf6e6de 459 m_hasCheckBoxes = false;
88ac883a 460#endif // wxUSE_CHECKLISTBOX
6de97a3b 461}
c801d85f 462
584ad2a3
MB
463bool wxListBox::Create( wxWindow *parent, wxWindowID id,
464 const wxPoint &pos, const wxSize &size,
465 const wxArrayString& choices,
466 long style, const wxValidator& validator,
467 const wxString &name )
468{
469 wxCArrayString chs(choices);
470
471 return Create( parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
472 style, validator, name );
473}
474
dcf40a56 475bool wxListBox::Create( wxWindow *parent, wxWindowID id,
fd0eed64
RR
476 const wxPoint &pos, const wxSize &size,
477 int n, const wxString choices[],
2ee3ee1b
VZ
478 long style, const wxValidator& validator,
479 const wxString &name )
c801d85f 480{
caf6e6de
WS
481 m_needParent = true;
482 m_acceptsFocus = true;
159b66c0 483 m_prevSelection = 0; // or -1 ??
caf6e6de 484 m_blockEvent = false;
dcf40a56 485
4dcaf11a
RR
486 if (!PreCreation( parent, pos, size ) ||
487 !CreateBase( parent, id, pos, size, style, validator, name ))
488 {
223d09f6 489 wxFAIL_MSG( wxT("wxListBox creation failed") );
caf6e6de 490 return false;
4dcaf11a 491 }
6de97a3b 492
fd0eed64 493 m_widget = gtk_scrolled_window_new( (GtkAdjustment*) NULL, (GtkAdjustment*) NULL );
034be888
RR
494 if (style & wxLB_ALWAYS_SB)
495 {
496 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
497 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
498 }
499 else
500 {
501 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
502 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
503 }
ff8bfdbb 504
fd0eed64 505 m_list = GTK_LIST( gtk_list_new() );
dcf40a56 506
4a82abc8 507 GtkSelectionMode mode;
fd0eed64 508 if (style & wxLB_MULTIPLE)
4a82abc8 509 {
fd0eed64 510 mode = GTK_SELECTION_MULTIPLE;
4a82abc8 511 }
fd0eed64 512 else if (style & wxLB_EXTENDED)
4a82abc8 513 {
fd0eed64 514 mode = GTK_SELECTION_EXTENDED;
4a82abc8
RR
515 }
516 else
517 {
518 // if style was 0 set single mode
519 m_windowStyle |= wxLB_SINGLE;
4fcfa27c 520 mode = GTK_SELECTION_SINGLE;
4a82abc8 521 }
6a6d4eed 522
fd0eed64 523 gtk_list_set_selection_mode( GTK_LIST(m_list), mode );
dcf40a56 524
38c7b3d3 525 gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(m_widget), GTK_WIDGET(m_list) );
38c7b3d3 526
e115e771
RR
527 /* make list scroll when moving the focus down using cursor keys */
528 gtk_container_set_focus_vadjustment(
529 GTK_CONTAINER(m_list),
530 gtk_scrolled_window_get_vadjustment(
2ee3ee1b 531 GTK_SCROLLED_WINDOW(m_widget)));
e115e771 532
fd0eed64 533 gtk_widget_show( GTK_WIDGET(m_list) );
dcf40a56 534
f2e93537
RR
535 gtk_signal_connect( GTK_OBJECT(m_list), "realize",
536 GTK_SIGNAL_FUNC(gtk_listbox_realized_callback), (gpointer) this );
17a1ebd1 537
2ee3ee1b
VZ
538 if ( style & wxLB_SORT )
539 {
540 // this will change DoAppend() behaviour
541 m_strings = new wxSortedArrayString;
542 }
eb553cb2
VZ
543 else
544 {
545 m_strings = (wxSortedArrayString *)NULL;
546 }
2ee3ee1b 547
fd0eed64
RR
548 for (int i = 0; i < n; i++)
549 {
11e1c70d 550 // add one by one
2ee3ee1b 551 DoAppend(choices[i]);
fd0eed64 552 }
dcf40a56 553
f03fc89f 554 m_parent->DoAddChild( this );
ff8bfdbb 555
abdeb9e7
RD
556 PostCreation(size);
557 SetBestSize(size); // need this too because this is a wxControlWithItems
dcf40a56 558
caf6e6de 559 return true;
6de97a3b 560}
c801d85f 561
fd0eed64 562wxListBox::~wxListBox()
c801d85f 563{
caf6e6de 564 m_hasVMT = false;
4a82abc8 565
caaa4cfd 566 Clear();
f96b15a3 567
6981d3ec
JS
568 if (m_strings)
569 delete m_strings;
6de97a3b 570}
c801d85f 571
8161b5b9
VZ
572// ----------------------------------------------------------------------------
573// adding items
574// ----------------------------------------------------------------------------
575
2ee3ee1b 576void wxListBox::DoInsertItems(const wxArrayString& items, int pos)
bb0ca8df 577{
223d09f6 578 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
bb0ca8df 579
11e1c70d
RR
580 // VZ: notice that InsertItems knows nothing about sorting, so calling it
581 // from outside (and not from our own Append) is likely to break
582 // everything
583
584 // code elsewhere supposes we have as many items in m_clientList as items
2ee3ee1b 585 // in the listbox
8228b893 586 wxASSERT_MSG( m_clientList.GetCount() == GetCount(),
2ee3ee1b
VZ
587 wxT("bug in client data management") );
588
9f884528
RD
589 InvalidateBestSize();
590
bb0ca8df
VZ
591 GList *children = m_list->children;
592 int length = g_list_length(children);
9ef6ff8c 593
223d09f6 594 wxCHECK_RET( pos <= length, wxT("invalid index in wxListBox::InsertItems") );
bb0ca8df 595
2ee3ee1b 596 size_t nItems = items.GetCount();
0a07a7d8 597 int index;
2ee3ee1b 598
0a07a7d8 599 if (m_strings)
bb0ca8df 600 {
0a07a7d8 601 for (size_t n = 0; n < nItems; n++)
bb0ca8df 602 {
0a07a7d8 603 index = m_strings->Add( items[n] );
f96b15a3 604
8228b893 605 if (index != (int)GetCount())
0a07a7d8
RR
606 {
607 GtkAddItem( items[n], index );
222ed1d6 608 wxList::compatibility_iterator node = m_clientList.Item( index );
0a07a7d8
RR
609 m_clientList.Insert( node, (wxObject*) NULL );
610 }
611 else
612 {
613 GtkAddItem( items[n] );
614 m_clientList.Append( (wxObject*) NULL );
615 }
bb0ca8df 616 }
bb0ca8df 617 }
11e1c70d 618 else
bb0ca8df 619 {
0a07a7d8
RR
620 if (pos == length)
621 {
622 for ( size_t n = 0; n < nItems; n++ )
623 {
624 GtkAddItem( items[n] );
625
626 m_clientList.Append((wxObject *)NULL);
627 }
628 }
629 else
bb0ca8df 630 {
222ed1d6 631 wxList::compatibility_iterator node = m_clientList.Item( pos );
0a07a7d8
RR
632 for ( size_t n = 0; n < nItems; n++ )
633 {
634 GtkAddItem( items[n], pos+n );
bb0ca8df 635
0a07a7d8
RR
636 m_clientList.Insert( node, (wxObject *)NULL );
637 }
bb0ca8df
VZ
638 }
639 }
2ee3ee1b 640
8228b893 641 wxASSERT_MSG( m_clientList.GetCount() == GetCount(),
11e1c70d 642 wxT("bug in client data management") );
2ee3ee1b
VZ
643}
644
645int wxListBox::DoAppend( const wxString& item )
646{
9f884528
RD
647 InvalidateBestSize();
648
11e1c70d 649 if (m_strings)
2ee3ee1b
VZ
650 {
651 // need to determine the index
11e1c70d 652 int index = m_strings->Add( item );
9ef6ff8c
VZ
653
654 // only if not at the end anyway
8228b893 655 if (index != (int)GetCount())
9ef6ff8c
VZ
656 {
657 GtkAddItem( item, index );
658
222ed1d6 659 wxList::compatibility_iterator node = m_clientList.Item( index );
11e1c70d 660 m_clientList.Insert( node, (wxObject *)NULL );
9ef6ff8c
VZ
661
662 return index;
663 }
2ee3ee1b 664 }
9ef6ff8c 665
11e1c70d 666 GtkAddItem(item);
2ee3ee1b 667
11e1c70d 668 m_clientList.Append((wxObject *)NULL);
2ee3ee1b 669
11e1c70d 670 return GetCount() - 1;
bb0ca8df
VZ
671}
672
11e1c70d 673void wxListBox::GtkAddItem( const wxString &item, int pos )
c801d85f 674{
223d09f6 675 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
ff8bfdbb 676
caaa4cfd 677 GtkWidget *list_item;
ff8bfdbb 678
bb0ca8df 679 wxString label(item);
88ac883a 680#if wxUSE_CHECKLISTBOX
caaa4cfd
RR
681 if (m_hasCheckBoxes)
682 {
d752a3c3 683 label.Prepend(wxCHECKLBOX_STRING);
caaa4cfd 684 }
88ac883a 685#endif // wxUSE_CHECKLISTBOX
fc54776e 686
fab591c5 687 list_item = gtk_list_item_new_with_label( wxGTK_CONV( label ) );
bb0ca8df 688
11e1c70d
RR
689 GList *gitem_list = g_list_alloc ();
690 gitem_list->data = list_item;
9ef6ff8c 691
11e1c70d
RR
692 if (pos == -1)
693 gtk_list_append_items( GTK_LIST (m_list), gitem_list );
694 else
695 gtk_list_insert_items( GTK_LIST (m_list), gitem_list, pos );
3502e687 696
c4526184 697 gtk_signal_connect_after( GTK_OBJECT(list_item), "select",
fd0eed64 698 GTK_SIGNAL_FUNC(gtk_listitem_select_callback), (gpointer)this );
dcf40a56 699
159b66c0 700 if (HasFlag(wxLB_MULTIPLE) || HasFlag(wxLB_EXTENDED))
c4526184 701 gtk_signal_connect_after( GTK_OBJECT(list_item), "deselect",
65045edd 702 GTK_SIGNAL_FUNC(gtk_listitem_deselect_callback), (gpointer)this );
dcf40a56 703
ff8bfdbb
VZ
704 gtk_signal_connect( GTK_OBJECT(list_item),
705 "button_press_event",
706 (GtkSignalFunc)gtk_listbox_button_press_callback,
707 (gpointer) this );
708
74505862
RR
709 gtk_signal_connect_after( GTK_OBJECT(list_item),
710 "button_release_event",
711 (GtkSignalFunc)gtk_listbox_button_release_callback,
712 (gpointer) this );
713
354aa1e3 714 gtk_signal_connect( GTK_OBJECT(list_item),
bb0ca8df
VZ
715 "key_press_event",
716 (GtkSignalFunc)gtk_listbox_key_press_callback,
717 (gpointer)this );
ff8bfdbb 718
c9433ea9
RR
719
720 gtk_signal_connect( GTK_OBJECT(list_item), "focus_in_event",
721 GTK_SIGNAL_FUNC(gtk_listitem_focus_in_callback), (gpointer)this );
722
723 gtk_signal_connect( GTK_OBJECT(list_item), "focus_out_event",
724 GTK_SIGNAL_FUNC(gtk_listitem_focus_out_callback), (gpointer)this );
725
fd0eed64
RR
726 ConnectWidget( list_item );
727
2b07d713
RR
728 if (GTK_WIDGET_REALIZED(m_widget))
729 {
f2e93537 730 gtk_widget_show( list_item );
17a1ebd1 731
2b07d713
RR
732 gtk_widget_realize( list_item );
733 gtk_widget_realize( GTK_BIN(list_item)->child );
014b0d06 734
291a8f20 735#if wxUSE_TOOLTIPS
f03fc89f 736 if (m_tooltip) m_tooltip->Apply( this );
291a8f20 737#endif
2b07d713 738 }
631a05be
RL
739
740 // Apply current widget style to the new list_item
741 GtkRcStyle *style = CreateWidgetStyle();
742 if (style)
743 {
744 gtk_widget_modify_style( GTK_WIDGET( list_item ), style );
745 GtkBin *bin = GTK_BIN( list_item );
17a1ebd1 746 gtk_widget_modify_style( GTK_WIDGET( bin->child ), style );
631a05be
RL
747 gtk_rc_style_unref( style );
748 }
fd0eed64
RR
749}
750
2ee3ee1b
VZ
751void wxListBox::DoSetItems( const wxArrayString& items,
752 void **clientData)
fd0eed64 753{
2ee3ee1b 754 Clear();
fd0eed64 755
2ee3ee1b 756 DoInsertItems(items, 0);
ff8bfdbb 757
2ee3ee1b
VZ
758 if ( clientData )
759 {
760 size_t count = items.GetCount();
761 for ( size_t n = 0; n < count; n++ )
762 {
763 SetClientData(n, clientData[n]);
764 }
765 }
fd0eed64 766}
dcf40a56 767
2ee3ee1b 768// ----------------------------------------------------------------------------
8161b5b9 769// deleting items
2ee3ee1b 770// ----------------------------------------------------------------------------
ff8bfdbb 771
fd0eed64
RR
772void wxListBox::Clear()
773{
223d09f6 774 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
fc54776e 775
8228b893 776 gtk_list_clear_items( m_list, 0, (int)GetCount() );
8161b5b9 777
780bb874
RR
778 if ( GTK_LIST(m_list)->last_focus_child != NULL )
779 {
780 // This should be NULL, I think.
781 GTK_LIST(m_list)->last_focus_child = NULL;
782 }
dcf40a56 783
6c8a980f
VZ
784 if ( HasClientObjectData() )
785 {
786 // destroy the data (due to Robert's idea of using wxList<wxObject>
787 // and not wxList<wxClientData> we can't just say
caf6e6de 788 // m_clientList.DeleteContents(true) - this would crash!
222ed1d6 789 wxList::compatibility_iterator node = m_clientList.GetFirst();
6c8a980f
VZ
790 while ( node )
791 {
b1d4dd7a
RL
792 delete (wxClientData *)node->GetData();
793 node = node->GetNext();
6c8a980f
VZ
794 }
795 }
11e1c70d 796 m_clientList.Clear();
ff8bfdbb 797
2ee3ee1b
VZ
798 if ( m_strings )
799 m_strings->Clear();
6de97a3b 800}
c801d85f
KB
801
802void wxListBox::Delete( int n )
803{
223d09f6 804 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
fc54776e 805
fd0eed64 806 GList *child = g_list_nth( m_list->children, n );
dcf40a56 807
223d09f6 808 wxCHECK_RET( child, wxT("wrong listbox index") );
dcf40a56 809
bbe0af5b 810 GList *list = g_list_append( (GList*) NULL, child->data );
fd0eed64
RR
811 gtk_list_remove_items( m_list, list );
812 g_list_free( list );
dcf40a56 813
222ed1d6 814 wxList::compatibility_iterator node = m_clientList.Item( n );
2ee3ee1b 815 if ( node )
fd0eed64 816 {
1e6feb95 817 if ( m_clientDataItemsType == wxClientData_Object )
2ee3ee1b 818 {
b1d4dd7a 819 wxClientData *cd = (wxClientData*)node->GetData();
2ee3ee1b
VZ
820 delete cd;
821 }
822
222ed1d6 823 m_clientList.Erase( node );
f5e27805 824 }
ff8bfdbb 825
2ee3ee1b 826 if ( m_strings )
ba8c1601 827 m_strings->RemoveAt(n);
2ee3ee1b
VZ
828}
829
8161b5b9
VZ
830// ----------------------------------------------------------------------------
831// client data
832// ----------------------------------------------------------------------------
833
834void wxListBox::DoSetItemClientData( int n, void* clientData )
835{
836 wxCHECK_RET( m_widget != NULL, wxT("invalid listbox control") );
837
222ed1d6 838 wxList::compatibility_iterator node = m_clientList.Item( n );
8161b5b9
VZ
839 wxCHECK_RET( node, wxT("invalid index in wxListBox::DoSetItemClientData") );
840
841 node->SetData( (wxObject*) clientData );
842}
843
844void* wxListBox::DoGetItemClientData( int n ) const
845{
846 wxCHECK_MSG( m_widget != NULL, NULL, wxT("invalid listbox control") );
847
222ed1d6 848 wxList::compatibility_iterator node = m_clientList.Item( n );
8161b5b9
VZ
849 wxCHECK_MSG( node, NULL, wxT("invalid index in wxListBox::DoGetItemClientData") );
850
b1d4dd7a 851 return node->GetData();
8161b5b9
VZ
852}
853
854void wxListBox::DoSetItemClientObject( int n, wxClientData* clientData )
855{
856 wxCHECK_RET( m_widget != NULL, wxT("invalid listbox control") );
857
222ed1d6 858 wxList::compatibility_iterator node = m_clientList.Item( n );
8161b5b9
VZ
859 wxCHECK_RET( node, wxT("invalid index in wxListBox::DoSetItemClientObject") );
860
861 // wxItemContainer already deletes data for us
862
863 node->SetData( (wxObject*) clientData );
864}
865
866wxClientData* wxListBox::DoGetItemClientObject( int n ) const
867{
868 wxCHECK_MSG( m_widget != NULL, (wxClientData*) NULL, wxT("invalid listbox control") );
869
222ed1d6 870 wxList::compatibility_iterator node = m_clientList.Item( n );
8161b5b9
VZ
871 wxCHECK_MSG( node, (wxClientData *)NULL,
872 wxT("invalid index in wxListBox::DoGetItemClientObject") );
873
b1d4dd7a 874 return (wxClientData*) node->GetData();
8161b5b9
VZ
875}
876
2ee3ee1b
VZ
877// ----------------------------------------------------------------------------
878// string list access
879// ----------------------------------------------------------------------------
880
8161b5b9
VZ
881wxString wxListBox::GetRealLabel(GList *item) const
882{
883 GtkBin *bin = GTK_BIN( item->data );
884 GtkLabel *label = GTK_LABEL( bin->child );
885
886 wxString str;
887
8161b5b9 888 str = wxString( label->label );
8161b5b9
VZ
889
890#if wxUSE_CHECKLISTBOX
891