]> git.saurik.com Git - wxWidgets.git/blame - src/gtk1/listbox.cpp
The alignment controls are now left-aligned if the floating controls are not shown.
[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 5// Copyright: (c) 1998 Robert Roebling
65571936 6// Licence: wxWindows licence
c801d85f
KB
7/////////////////////////////////////////////////////////////////////////////
8
14f355c2
VS
9// For compilers that support precompilation, includes "wx.h".
10#include "wx/wxprec.h"
11
dcf924a3
RR
12#if wxUSE_LISTBOX
13
88a7a4e1
WS
14#include "wx/listbox.h"
15
ad9835c9
WS
16#ifndef WX_PRECOMP
17 #include "wx/dynarray.h"
88a7a4e1 18 #include "wx/intl.h"
de6185e2 19 #include "wx/utils.h"
9eddec69 20 #include "wx/settings.h"
e6e83933 21 #include "wx/checklst.h"
aaa6d89a 22 #include "wx/arrstr.h"
ad9835c9
WS
23#endif
24
3cbab641 25#include "wx/gtk1/private.h"
291a8f20
RR
26
27#if wxUSE_TOOLTIPS
88a7a4e1 28 #include "wx/tooltip.h"
291a8f20 29#endif
c801d85f 30
4a82abc8 31#include <gdk/gdk.h>
16c1f79c
RR
32#include <gtk/gtk.h>
33#include <gdk/gdkkeysyms.h>
83624f79 34
acfd422a
RR
35//-----------------------------------------------------------------------------
36// idle system
37//-----------------------------------------------------------------------------
38
39extern void wxapp_install_idle_handler();
40extern bool g_isIdle;
41
66bd6b93
RR
42//-----------------------------------------------------------------------------
43// data
44//-----------------------------------------------------------------------------
45
d7fa7eaa
RR
46extern bool g_blockEventsOnDrag;
47extern bool g_blockEventsOnScroll;
48extern wxCursor g_globalCursor;
49extern wxWindowGTK *g_delayedFocus;
c9433ea9
RR
50extern wxWindowGTK *g_focusWindow;
51extern wxWindowGTK *g_focusWindowLast;
caaa4cfd 52
caf6e6de 53static bool g_hasDoubleClicked = false;
7a30ee8f 54
4a82abc8
RR
55//-----------------------------------------------------------------------------
56// idle callback for SetFirstItem
57//-----------------------------------------------------------------------------
58
59struct wxlistbox_idle_struct
60{
61 wxListBox *m_listbox;
62 int m_item;
63 gint m_tag;
64};
65
865bb325
VZ
66extern "C" {
67static gint wxlistbox_idle_callback( gpointer gdata )
4a82abc8
RR
68{
69 wxlistbox_idle_struct* data = (wxlistbox_idle_struct*) gdata;
70 gdk_threads_enter();
71
72 gtk_idle_remove( data->m_tag );
f96b15a3 73
992295c4
VZ
74 // check that the items haven't been deleted from the listbox since we had
75 // installed this callback
76 wxListBox *lbox = data->m_listbox;
8228b893 77 if ( data->m_item < (int)lbox->GetCount() )
992295c4
VZ
78 {
79 lbox->SetFirstItem( data->m_item );
80 }
f96b15a3 81
4a82abc8 82 delete data;
f96b15a3 83
4a82abc8
RR
84 gdk_threads_leave();
85
86 return TRUE;
87}
865bb325 88}
4a82abc8 89
c9433ea9
RR
90//-----------------------------------------------------------------------------
91// "focus_in_event"
92//-----------------------------------------------------------------------------
93
865bb325 94extern "C" {
89954433
VZ
95static gint gtk_listitem_focus_in_callback( GtkWidget *WXUNUSED(widget),
96 GdkEvent *WXUNUSED(event),
97 wxWindow *win )
c9433ea9
RR
98{
99 if (g_isIdle)
100 wxapp_install_idle_handler();
101
102 g_focusWindowLast =
103 g_focusWindow = win;
104
105 // does the window itself think that it has the focus?
106 if ( !win->m_hasFocus )
107 {
108 // not yet, notify it
caf6e6de 109 win->m_hasFocus = true;
17a1ebd1 110
c9433ea9 111 wxChildFocusEvent eventChildFocus(win);
937013e0 112 (void)win->HandleWindowEvent(eventChildFocus);
c9433ea9
RR
113
114 wxFocusEvent eventFocus(wxEVT_SET_FOCUS, win->GetId());
115 eventFocus.SetEventObject(win);
116
937013e0 117 (void)win->HandleWindowEvent(eventFocus);
c9433ea9
RR
118 }
119
120 return FALSE;
121}
865bb325 122}
c9433ea9
RR
123
124//-----------------------------------------------------------------------------
125// "focus_out_event"
126//-----------------------------------------------------------------------------
127
865bb325 128extern "C" {
89954433
VZ
129static gint gtk_listitem_focus_out_callback( GtkWidget *WXUNUSED(widget),
130 GdkEventFocus *WXUNUSED(gdk_event),
131 wxWindowGTK *win )
c9433ea9
RR
132{
133 if (g_isIdle)
134 wxapp_install_idle_handler();
135
d3b9f782 136 g_focusWindow = NULL;
c9433ea9
RR
137
138 // don't send the window a kill focus event if it thinks that it doesn't
139 // have focus already
140 if ( win->m_hasFocus )
141 {
caf6e6de 142 win->m_hasFocus = false;
c9433ea9
RR
143
144 wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
145 event.SetEventObject( win );
146
147 // even if we did process the event in wx code, still let GTK itself
148 // process it too as otherwise bad things happen, especially in GTK2
149 // where the text control simply aborts the program if it doesn't get
150 // the matching focus out event
937013e0 151 (void)win->HandleWindowEvent( event );
c9433ea9
RR
152 }
153
154 return FALSE;
155}
865bb325 156}
c9433ea9 157
caaa4cfd 158//-----------------------------------------------------------------------------
7a30ee8f 159// "button_release_event"
caaa4cfd
RR
160//-----------------------------------------------------------------------------
161
ce7fe42e 162/* we would normally emit a wxEVT_LISTBOX_DCLICK event once
7a30ee8f
RR
163 a GDK_2BUTTON_PRESS occurs, but this has the particular problem of the
164 listbox keeping the focus until it receives a GDK_BUTTON_RELEASE event.
165 this can lead to race conditions so that we emit the dclick event
166 after the GDK_BUTTON_RELEASE event after the GDK_2BUTTON_PRESS event */
167
865bb325 168extern "C" {
ff8bfdbb 169static gint
014b0d06
VZ
170gtk_listbox_button_release_callback( GtkWidget * WXUNUSED(widget),
171 GdkEventButton * WXUNUSED(gdk_event),
172 wxListBox *listbox )
caaa4cfd 173{
acfd422a
RR
174 if (g_isIdle) wxapp_install_idle_handler();
175
caaa4cfd
RR
176 if (g_blockEventsOnDrag) return FALSE;
177 if (g_blockEventsOnScroll) return FALSE;
178
a2053b27 179 if (!listbox->m_hasVMT) return FALSE;
caaa4cfd 180
7a30ee8f 181 if (!g_hasDoubleClicked) return FALSE;
ff8bfdbb 182
ce7fe42e 183 wxCommandEvent event( wxEVT_LISTBOX_DCLICK, listbox->GetId() );
6c8a980f 184 event.SetEventObject( listbox );
ff8bfdbb 185
6c8a980f
VZ
186 wxArrayInt aSelections;
187 int n, count = listbox->GetSelections(aSelections);
188 if ( count > 0 )
189 {
190 n = aSelections[0];
191 if ( listbox->HasClientObjectData() )
192 event.SetClientObject( listbox->GetClientObject(n) );
193 else if ( listbox->HasClientUntypedData() )
194 event.SetClientData( listbox->GetClientData(n) );
195 event.SetString( listbox->GetString(n) );
196 }
197 else
198 {
199 n = -1;
200 }
5b077d48 201
687706f5 202 event.SetInt(n);
6c8a980f 203
937013e0 204 listbox->HandleWindowEvent( event );
ff8bfdbb 205
7a30ee8f
RR
206 return FALSE;
207}
865bb325 208}
7a30ee8f
RR
209
210//-----------------------------------------------------------------------------
211// "button_press_event"
212//-----------------------------------------------------------------------------
213
865bb325 214extern "C" {
7a30ee8f 215static gint
014b0d06
VZ
216gtk_listbox_button_press_callback( GtkWidget *widget,
217 GdkEventButton *gdk_event,
218 wxListBox *listbox )
7a30ee8f
RR
219{
220 if (g_isIdle) wxapp_install_idle_handler();
221
222 if (g_blockEventsOnDrag) return FALSE;
223 if (g_blockEventsOnScroll) return FALSE;
224
225 if (!listbox->m_hasVMT) return FALSE;
226
11e1c70d 227 int sel = listbox->GtkGetIndex( widget );
7a30ee8f 228
88ac883a 229#if wxUSE_CHECKLISTBOX
7a30ee8f
RR
230 if ((listbox->m_hasCheckBoxes) && (gdk_event->x < 15) && (gdk_event->type != GDK_2BUTTON_PRESS))
231 {
232 wxCheckListBox *clb = (wxCheckListBox *)listbox;
233
234 clb->Check( sel, !clb->IsChecked(sel) );
235
ce7fe42e 236 wxCommandEvent event( wxEVT_CHECKLISTBOX, listbox->GetId() );
7a30ee8f
RR
237 event.SetEventObject( listbox );
238 event.SetInt( sel );
937013e0 239 listbox->HandleWindowEvent( event );
4f22cf8d 240 }
88ac883a 241#endif // wxUSE_CHECKLISTBOX
014b0d06 242
4fcfa27c
RR
243 if ((gdk_event->state == 0) &&
244 (((listbox->GetWindowStyleFlag() & wxLB_MULTIPLE) != 0) ||
245 ((listbox->GetWindowStyleFlag() & wxLB_EXTENDED) != 0)) )
246 {
caf6e6de 247 listbox->m_blockEvent = true;
376c0148
RR
248
249 int i;
250 for (i = 0; i < (int)listbox->GetCount(); i++)
251 if (i != sel)
252 gtk_list_unselect_item( GTK_LIST(listbox->m_list), i );
17a1ebd1 253
caf6e6de 254 listbox->m_blockEvent = false;
17a1ebd1 255
376c0148 256 return false;
4fcfa27c
RR
257 }
258
ce7fe42e 259 /* emit wxEVT_LISTBOX_DCLICK later */
7a30ee8f 260 g_hasDoubleClicked = (gdk_event->type == GDK_2BUTTON_PRESS);
4f22cf8d 261
caaa4cfd
RR
262 return FALSE;
263}
865bb325 264}
66bd6b93 265
1144d24d
RR
266//-----------------------------------------------------------------------------
267// "key_press_event"
268//-----------------------------------------------------------------------------
269
865bb325 270extern "C" {
ff8bfdbb 271static gint
1144d24d
RR
272gtk_listbox_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxListBox *listbox )
273{
9ef6ff8c 274 if (g_isIdle)
354aa1e3 275 wxapp_install_idle_handler();
acfd422a 276
9ef6ff8c 277 if (g_blockEventsOnDrag)
354aa1e3 278 return FALSE;
1144d24d 279
caf6e6de 280 bool ret = false;
1144d24d 281
354aa1e3
RR
282 if ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab))
283 {
284 wxNavigationKeyEvent new_event;
285 /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */
286 new_event.SetDirection( (gdk_event->keyval == GDK_Tab) );
287 /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
288 new_event.SetWindowChange( (gdk_event->state & GDK_CONTROL_MASK) );
289 new_event.SetCurrentFocus( listbox );
937013e0 290 ret = listbox->HandleWindowEvent( new_event );
354aa1e3 291 }
9ef6ff8c 292
4a82abc8
RR
293 if ((gdk_event->keyval == GDK_Return) && (!ret))
294 {
295 // eat return in all modes
caf6e6de 296 ret = true;
4a82abc8 297 }
f96b15a3 298
354aa1e3 299#if wxUSE_CHECKLISTBOX
1e8d2f69 300 if ((gdk_event->keyval == ' ') && (listbox->m_hasCheckBoxes) && (!ret))
354aa1e3
RR
301 {
302 int sel = listbox->GtkGetIndex( widget );
ff8bfdbb 303
354aa1e3 304 wxCheckListBox *clb = (wxCheckListBox *)listbox;
ff8bfdbb 305
354aa1e3 306 clb->Check( sel, !clb->IsChecked(sel) );
ff8bfdbb 307
ce7fe42e 308 wxCommandEvent new_event( wxEVT_CHECKLISTBOX, listbox->GetId() );
354aa1e3
RR
309 new_event.SetEventObject( listbox );
310 new_event.SetInt( sel );
937013e0 311 ret = listbox->HandleWindowEvent( new_event );
354aa1e3
RR
312 }
313#endif // wxUSE_CHECKLISTBOX
ff8bfdbb 314
fec195b2 315 // Check or uncheck item with SPACE
17a1ebd1 316 if ((gdk_event->keyval == ' ') && (!ret) &&
fec195b2
RR
317 (((listbox->GetWindowStyleFlag() & wxLB_MULTIPLE) != 0) ||
318 ((listbox->GetWindowStyleFlag() & wxLB_EXTENDED) != 0)) )
319 {
320 int sel = listbox->GtkGetIndex( widget );
17a1ebd1 321
fec195b2
RR
322 if (sel != -1)
323 {
caf6e6de 324 ret = true;
17a1ebd1 325
fec195b2
RR
326 if (listbox->IsSelected( sel ))
327 gtk_list_unselect_item( listbox->m_list, sel );
328 else
329 gtk_list_select_item( listbox->m_list, sel );
17a1ebd1 330
ce7fe42e 331 wxCommandEvent new_event(wxEVT_LISTBOX, listbox->GetId() );
fec195b2
RR
332 new_event.SetEventObject( listbox );
333 wxArrayInt aSelections;
334 int n, count = listbox->GetSelections(aSelections);
335 if ( count > 0 )
336 {
337 n = aSelections[0];
338 if ( listbox->HasClientObjectData() )
339 new_event.SetClientObject( listbox->GetClientObject(n) );
340 else if ( listbox->HasClientUntypedData() )
341 new_event.SetClientData( listbox->GetClientData(n) );
342 new_event.SetString( listbox->GetString(n) );
343 }
344 else
345 {
346 n = -1;
347 }
687706f5 348 new_event.SetInt(n);
937013e0 349 listbox->HandleWindowEvent( new_event );
fec195b2
RR
350 }
351 }
17a1ebd1 352
354aa1e3
RR
353 if (ret)
354 {
355 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
356 return TRUE;
357 }
ff8bfdbb 358
1144d24d
RR
359 return FALSE;
360}
865bb325 361}
1144d24d 362
c801d85f 363//-----------------------------------------------------------------------------
a60c99e6 364// "select" and "deselect"
c801d85f
KB
365//-----------------------------------------------------------------------------
366
9c9c3d7a
VZ
367static void gtk_listitem_select_cb( GtkWidget *widget,
368 wxListBox *listbox,
369 bool is_selection )
c801d85f 370{
acfd422a
RR
371 if (g_isIdle) wxapp_install_idle_handler();
372
a2053b27 373 if (!listbox->m_hasVMT) return;
fd0eed64 374 if (g_blockEventsOnDrag) return;
8161b5b9 375
bac95742 376 if (listbox->m_blockEvent) return;
9ef6ff8c 377
ce7fe42e 378 wxCommandEvent event(wxEVT_LISTBOX, listbox->GetId() );
6c8a980f 379 event.SetEventObject( listbox );
8161b5b9 380
9c9c3d7a
VZ
381 // indicate whether this is a selection or a deselection
382 event.SetExtraLong( is_selection );
159b66c0
RR
383
384 if ((listbox->GetWindowStyleFlag() & wxLB_SINGLE) != 0)
385 {
386 int sel = listbox->GtkGetIndex( widget );
387
388 if (listbox->m_prevSelection != sel)
389 gtk_list_unselect_item( listbox->m_list, listbox->m_prevSelection );
390
391 listbox->m_prevSelection = sel;
392 }
dcf40a56 393
09cf7c58 394 wxArrayInt aSelections;
2ee3ee1b 395 int n, count = listbox->GetSelections(aSelections);
09cf7c58
RR
396 if ( count > 0 )
397 {
2ee3ee1b 398 n = aSelections[0];
6c8a980f
VZ
399 if ( listbox->HasClientObjectData() )
400 event.SetClientObject( listbox->GetClientObject(n) );
401 else if ( listbox->HasClientUntypedData() )
402 event.SetClientData( listbox->GetClientData(n) );
403 event.SetString( listbox->GetString(n) );
09cf7c58
RR
404 }
405 else
406 {
2ee3ee1b 407 n = -1;
09cf7c58
RR
408 }
409
687706f5 410 event.SetInt(n);
2ee3ee1b 411
159b66c0
RR
412// No longer required with new code in wxLB_SINGLE
413// listbox->GetEventHandler()->AddPendingEvent( event );
937013e0 414 listbox->HandleWindowEvent( event );
6de97a3b 415}
c801d85f 416
865bb325
VZ
417extern "C" {
418static void gtk_listitem_select_callback( GtkWidget *widget, wxListBox *listbox )
419{
420 gtk_listitem_select_cb( widget, listbox, TRUE );
421}
422}
423
424extern "C" {
425static void gtk_listitem_deselect_callback( GtkWidget *widget, wxListBox *listbox )
426{
427 gtk_listitem_select_cb( widget, listbox, FALSE );
428}
429}
430
a60c99e6
RR
431//-----------------------------------------------------------------------------
432// wxListBox
c801d85f
KB
433//-----------------------------------------------------------------------------
434
865bb325 435extern "C" {
f2e93537 436static gint
89954433 437gtk_listbox_realized_callback( GtkWidget *WXUNUSED(widget), wxListBox *win )
f2e93537
RR
438{
439 if (g_isIdle)
440 wxapp_install_idle_handler();
17a1ebd1 441
f2e93537
RR
442 GList *child = win->m_list->children;
443 for (child = win->m_list->children; child != NULL; child = child->next)
444 gtk_widget_show( GTK_WIDGET(child->data) );
17a1ebd1 445
f2e93537
RR
446 return false;
447}
865bb325 448}
f2e93537
RR
449
450//-----------------------------------------------------------------------------
451// wxListBox
452//-----------------------------------------------------------------------------
453
2ee3ee1b
VZ
454// ----------------------------------------------------------------------------
455// construction
456// ----------------------------------------------------------------------------
457
fd0eed64 458wxListBox::wxListBox()
c801d85f 459{
d3b9f782 460 m_list = NULL;
88ac883a 461#if wxUSE_CHECKLISTBOX
caf6e6de 462 m_hasCheckBoxes = false;
88ac883a 463#endif // wxUSE_CHECKLISTBOX
6de97a3b 464}
c801d85f 465
584ad2a3
MB
466bool wxListBox::Create( wxWindow *parent, wxWindowID id,
467 const wxPoint &pos, const wxSize &size,
468 const wxArrayString& choices,
469 long style, const wxValidator& validator,
470 const wxString &name )
471{
472 wxCArrayString chs(choices);
473
474 return Create( parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
475 style, validator, name );
476}
477
dcf40a56 478bool wxListBox::Create( wxWindow *parent, wxWindowID id,
fd0eed64
RR
479 const wxPoint &pos, const wxSize &size,
480 int n, const wxString choices[],
2ee3ee1b
VZ
481 long style, const wxValidator& validator,
482 const wxString &name )
c801d85f 483{
caf6e6de
WS
484 m_needParent = true;
485 m_acceptsFocus = true;
159b66c0 486 m_prevSelection = 0; // or -1 ??
caf6e6de 487 m_blockEvent = false;
dcf40a56 488
4dcaf11a
RR
489 if (!PreCreation( parent, pos, size ) ||
490 !CreateBase( parent, id, pos, size, style, validator, name ))
491 {
223d09f6 492 wxFAIL_MSG( wxT("wxListBox creation failed") );
caf6e6de 493 return false;
4dcaf11a 494 }
6de97a3b 495
d3b9f782 496 m_widget = gtk_scrolled_window_new( NULL, NULL );
034be888
RR
497 if (style & wxLB_ALWAYS_SB)
498 {
499 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
500 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
501 }
502 else
503 {
504 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
505 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
506 }
ff8bfdbb 507
fd0eed64 508 m_list = GTK_LIST( gtk_list_new() );
dcf40a56 509
4a82abc8 510 GtkSelectionMode mode;
fd0eed64 511 if (style & wxLB_MULTIPLE)
4a82abc8 512 {
fd0eed64 513 mode = GTK_SELECTION_MULTIPLE;
4a82abc8 514 }
fd0eed64 515 else if (style & wxLB_EXTENDED)
4a82abc8 516 {
fd0eed64 517 mode = GTK_SELECTION_EXTENDED;
4a82abc8
RR
518 }
519 else
520 {
521 // if style was 0 set single mode
522 m_windowStyle |= wxLB_SINGLE;
4fcfa27c 523 mode = GTK_SELECTION_SINGLE;
4a82abc8 524 }
6a6d4eed 525
fd0eed64 526 gtk_list_set_selection_mode( GTK_LIST(m_list), mode );
dcf40a56 527
38c7b3d3 528 gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(m_widget), GTK_WIDGET(m_list) );
38c7b3d3 529
e115e771
RR
530 /* make list scroll when moving the focus down using cursor keys */
531 gtk_container_set_focus_vadjustment(
532 GTK_CONTAINER(m_list),
533 gtk_scrolled_window_get_vadjustment(
2ee3ee1b 534 GTK_SCROLLED_WINDOW(m_widget)));
e115e771 535
fd0eed64 536 gtk_widget_show( GTK_WIDGET(m_list) );
dcf40a56 537
f2e93537
RR
538 gtk_signal_connect( GTK_OBJECT(m_list), "realize",
539 GTK_SIGNAL_FUNC(gtk_listbox_realized_callback), (gpointer) this );
17a1ebd1 540
2ee3ee1b
VZ
541 if ( style & wxLB_SORT )
542 {
a236aa20 543 // this will change Append() behaviour
2ee3ee1b
VZ
544 m_strings = new wxSortedArrayString;
545 }
eb553cb2
VZ
546 else
547 {
d3b9f782 548 m_strings = NULL;
eb553cb2 549 }
2ee3ee1b 550
a236aa20 551 Append(n, choices);
dcf40a56 552
f03fc89f 553 m_parent->DoAddChild( this );
ff8bfdbb 554
abdeb9e7 555 PostCreation(size);
170acdc9 556 SetInitialSize(size); // need this too because this is a wxControlWithItems
dcf40a56 557
caf6e6de 558 return true;
6de97a3b 559}
c801d85f 560
fd0eed64 561wxListBox::~wxListBox()
c801d85f 562{
caf6e6de 563 m_hasVMT = false;
4a82abc8 564
caaa4cfd 565 Clear();
f96b15a3 566
89954433 567 delete m_strings;
6de97a3b 568}
c801d85f 569
8161b5b9
VZ
570// ----------------------------------------------------------------------------
571// adding items
572// ----------------------------------------------------------------------------
573
a236aa20
VZ
574int wxListBox::DoInsertItems(const wxArrayStringsAdapter& items,
575 unsigned int pos,
576 void **clientData,
577 wxClientDataType type)
bb0ca8df 578{
a236aa20 579 wxCHECK_MSG( m_list != NULL, wxNOT_FOUND, wxT("invalid listbox") );
bb0ca8df 580
a236aa20
VZ
581 const unsigned count = GetCount();
582 wxCHECK_MSG( pos <= count, wxNOT_FOUND,
583 wxT("invalid index in wxListBox::InsertItems") );
11e1c70d
RR
584
585 // code elsewhere supposes we have as many items in m_clientList as items
2ee3ee1b 586 // in the listbox
a236aa20
VZ
587 wxASSERT_MSG( m_clientList.GetCount() == count,
588 wxT("bug in client data management") );
2ee3ee1b 589
9f884528
RD
590 InvalidateBestSize();
591
a236aa20 592 const unsigned numItems = items.GetCount();
9ef6ff8c 593
a236aa20 594 for ( unsigned int n = 0; n < numItems; ++n, ++pos )
bb0ca8df 595 {
a236aa20 596 const wxString& item = items[n];
f96b15a3 597
a236aa20
VZ
598 const unsigned idx = m_strings ? m_strings->Add(item)
599 : pos;
0a07a7d8 600
934960d1 601 GtkAddItem(item, idx == GetCount() ? (unsigned) -1 : idx);
bb0ca8df 602
a236aa20
VZ
603 m_clientList.Insert(idx, NULL);
604
605 AssignNewItemClientData(idx, clientData, n, type);
bb0ca8df 606 }
2ee3ee1b 607
8228b893 608 wxASSERT_MSG( m_clientList.GetCount() == GetCount(),
11e1c70d 609 wxT("bug in client data management") );
9ef6ff8c 610
a236aa20 611 return pos - 1;
bb0ca8df
VZ
612}
613
11e1c70d 614void wxListBox::GtkAddItem( const wxString &item, int pos )
c801d85f 615{
223d09f6 616 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
ff8bfdbb 617
caaa4cfd 618 GtkWidget *list_item;
ff8bfdbb 619
bb0ca8df 620 wxString label(item);
88ac883a 621#if wxUSE_CHECKLISTBOX
caaa4cfd
RR
622 if (m_hasCheckBoxes)
623 {
d752a3c3 624 label.Prepend(wxCHECKLBOX_STRING);
caaa4cfd 625 }
88ac883a 626#endif // wxUSE_CHECKLISTBOX
fc54776e 627
fab591c5 628 list_item = gtk_list_item_new_with_label( wxGTK_CONV( label ) );
bb0ca8df 629
11e1c70d
RR
630 GList *gitem_list = g_list_alloc ();
631 gitem_list->data = list_item;
9ef6ff8c 632
11e1c70d
RR
633 if (pos == -1)
634 gtk_list_append_items( GTK_LIST (m_list), gitem_list );
635 else
636 gtk_list_insert_items( GTK_LIST (m_list), gitem_list, pos );
3502e687 637
c4526184 638 gtk_signal_connect_after( GTK_OBJECT(list_item), "select",
fd0eed64 639 GTK_SIGNAL_FUNC(gtk_listitem_select_callback), (gpointer)this );
dcf40a56 640
159b66c0 641 if (HasFlag(wxLB_MULTIPLE) || HasFlag(wxLB_EXTENDED))
c4526184 642 gtk_signal_connect_after( GTK_OBJECT(list_item), "deselect",
65045edd 643 GTK_SIGNAL_FUNC(gtk_listitem_deselect_callback), (gpointer)this );
dcf40a56 644
ff8bfdbb
VZ
645 gtk_signal_connect( GTK_OBJECT(list_item),
646 "button_press_event",
647 (GtkSignalFunc)gtk_listbox_button_press_callback,
648 (gpointer) this );
649
74505862
RR
650 gtk_signal_connect_after( GTK_OBJECT(list_item),
651 "button_release_event",
652 (GtkSignalFunc)gtk_listbox_button_release_callback,
653 (gpointer) this );
654
354aa1e3 655 gtk_signal_connect( GTK_OBJECT(list_item),
bb0ca8df
VZ
656 "key_press_event",
657 (GtkSignalFunc)gtk_listbox_key_press_callback,
658 (gpointer)this );
ff8bfdbb 659
c9433ea9
RR
660
661 gtk_signal_connect( GTK_OBJECT(list_item), "focus_in_event",
662 GTK_SIGNAL_FUNC(gtk_listitem_focus_in_callback), (gpointer)this );
663
664 gtk_signal_connect( GTK_OBJECT(list_item), "focus_out_event",
665 GTK_SIGNAL_FUNC(gtk_listitem_focus_out_callback), (gpointer)this );
666
fd0eed64
RR
667 ConnectWidget( list_item );
668
2b07d713
RR
669 if (GTK_WIDGET_REALIZED(m_widget))
670 {
f2e93537 671 gtk_widget_show( list_item );
17a1ebd1 672
2b07d713
RR
673 gtk_widget_realize( list_item );
674 gtk_widget_realize( GTK_BIN(list_item)->child );
014b0d06 675
291a8f20 676#if wxUSE_TOOLTIPS
f03fc89f 677 if (m_tooltip) m_tooltip->Apply( this );
291a8f20 678#endif
2b07d713 679 }
631a05be
RL
680
681 // Apply current widget style to the new list_item
682 GtkRcStyle *style = CreateWidgetStyle();
683 if (style)
684 {
685 gtk_widget_modify_style( GTK_WIDGET( list_item ), style );
686 GtkBin *bin = GTK_BIN( list_item );
17a1ebd1 687 gtk_widget_modify_style( GTK_WIDGET( bin->child ), style );
631a05be
RL
688 gtk_rc_style_unref( style );
689 }
fd0eed64
RR
690}
691
2ee3ee1b 692// ----------------------------------------------------------------------------
8161b5b9 693// deleting items
2ee3ee1b 694// ----------------------------------------------------------------------------
ff8bfdbb 695
a236aa20 696void wxListBox::DoClear()
fd0eed64 697{
223d09f6 698 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
fc54776e 699
8228b893 700 gtk_list_clear_items( m_list, 0, (int)GetCount() );
8161b5b9 701
780bb874
RR
702 if ( GTK_LIST(m_list)->last_focus_child != NULL )
703 {
704 // This should be NULL, I think.
705 GTK_LIST(m_list)->last_focus_child = NULL;
706 }
dcf40a56 707
11e1c70d 708 m_clientList.Clear();
ff8bfdbb 709
2ee3ee1b
VZ
710 if ( m_strings )
711 m_strings->Clear();
6de97a3b 712}
c801d85f 713
a236aa20 714void wxListBox::DoDeleteOneItem(unsigned int n)
c801d85f 715{
223d09f6 716 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
fc54776e 717
fd0eed64 718 GList *child = g_list_nth( m_list->children, n );
dcf40a56 719
223d09f6 720 wxCHECK_RET( child, wxT("wrong listbox index") );
dcf40a56 721
d3b9f782 722 GList *list = g_list_append( NULL, child->data );
fd0eed64
RR
723 gtk_list_remove_items( m_list, list );
724 g_list_free( list );
dcf40a56 725
222ed1d6 726 wxList::compatibility_iterator node = m_clientList.Item( n );
2ee3ee1b 727 if ( node )
fd0eed64 728 {
222ed1d6 729 m_clientList.Erase( node );
f5e27805 730 }
ff8bfdbb 731
2ee3ee1b 732 if ( m_strings )
ba8c1601 733 m_strings->RemoveAt(n);
2ee3ee1b
VZ
734}
735
8161b5b9
VZ
736// ----------------------------------------------------------------------------
737// client data
738// ----------------------------------------------------------------------------
739
aa61d352 740void wxListBox::DoSetItemClientData(unsigned int n, void* clientData)
8161b5b9
VZ
741{
742 wxCHECK_RET( m_widget != NULL, wxT("invalid listbox control") );
743
222ed1d6 744 wxList::compatibility_iterator node = m_clientList.Item( n );
8161b5b9
VZ
745 wxCHECK_RET( node, wxT("invalid index in wxListBox::DoSetItemClientData") );
746
747 node->SetData( (wxObject*) clientData );
748}
749
aa61d352 750void* wxListBox::DoGetItemClientData(unsigned int n) const
8161b5b9
VZ
751{
752 wxCHECK_MSG( m_widget != NULL, NULL, wxT("invalid listbox control") );
753
222ed1d6 754 wxList::compatibility_iterator node = m_clientList.Item( n );
8161b5b9
VZ
755 wxCHECK_MSG( node, NULL, wxT("invalid index in wxListBox::DoGetItemClientData") );
756
b1d4dd7a 757 return node->GetData();
8161b5b9
VZ
758}
759
2ee3ee1b
VZ
760// ----------------------------------------------------------------------------
761// string list access
762// ----------------------------------------------------------------------------
763
8161b5b9
VZ
764wxString wxListBox::GetRealLabel(GList *item) const
765{
766 GtkBin *bin = GTK_BIN( item->data );
767 GtkLabel *label = GTK_LABEL( bin->child );
768
769 wxString str;
770
8161b5b9 771 str = wxString( label->label );
8161b5b9
VZ
772
773#if wxUSE_CHECKLISTBOX
e9670814 774 // checklistboxes have "[±] " prepended to their lables, remove it
8161b5b9 775 //
3cbab641 776 // NB: 4 below is the length of wxCHECKLBOX_STRING from wx/gtk1/checklst.h
8161b5b9
VZ
777 if ( m_hasCheckBoxes )
778 str.erase(0, 4);
779#endif // wxUSE_CHECKLISTBOX
780
781 return str;
782}
783
aa61d352 784void wxListBox::SetString(unsigned int n, const wxString &string)
2ee3ee1b
VZ
785{
786 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
787
788 GList *child = g_list_nth( m_list->children, n );
789 if (child)
790 {
791 GtkBin *bin = GTK_BIN( child->data );
792 GtkLabel *label = GTK_LABEL( bin->child );
793
794 wxString str;
795#if wxUSE_CHECKLISTBOX
796 if (m_hasCheckBoxes)
d752a3c3 797 str += wxCHECKLBOX_STRING;
2ee3ee1b
VZ
798#endif // wxUSE_CHECKLISTBOX
799 str += string;
800
fab591c5 801 gtk_label_set( label, wxGTK_CONV( str ) );
2ee3ee1b
VZ
802 }
803 else
f5e27805 804 {
2ee3ee1b 805 wxFAIL_MSG(wxT("wrong listbox index"));
fd0eed64 806 }
6de97a3b 807}
c801d85f 808
aa61d352 809wxString wxListBox::GetString(unsigned int n) const
c801d85f 810{
11e62fe6 811 wxCHECK_MSG( m_list != NULL, wxEmptyString, wxT("invalid listbox") );
fc54776e 812
2ee3ee1b
VZ
813 GList *child = g_list_nth( m_list->children, n );
814 if (child)
815 {
8161b5b9 816 return GetRealLabel(child);
2ee3ee1b
VZ
817 }
818
819 wxFAIL_MSG(wxT("wrong listbox index"));
820
11e62fe6 821 return wxEmptyString;
2ee3ee1b
VZ
822}
823
aa61d352 824unsigned int wxListBox::GetCount() const
2ee3ee1b 825{
8228b893 826 wxCHECK_MSG( m_list != NULL, 0, wxT("invalid listbox") );
2ee3ee1b 827
11e1c70d
RR
828 GList *children = m_list->children;
829 return g_list_length(children);
6de97a3b 830}
c801d85f 831
11e62fe6 832int wxListBox::FindString( const wxString &item, bool bCase ) const
c801d85f 833{
11e62fe6 834 wxCHECK_MSG( m_list != NULL, wxNOT_FOUND, wxT("invalid listbox") );
ff8bfdbb 835
fd0eed64
RR
836 GList *child = m_list->children;
837 int count = 0;
838 while (child)
839 {
11e62fe6 840 if ( item.IsSameAs( GetRealLabel(child), bCase ) )
bb0ca8df 841 return count;
ff8bfdbb 842
fd0eed64
RR
843 count++;
844 child = child->next;
845 }
846
2ee3ee1b 847 // it's not an error if the string is not found -> no wxCHECK
dcf40a56 848
2ee3ee1b 849 return wxNOT_FOUND;
6de97a3b 850}
c801d85f 851
2ee3ee1b
VZ
852// ----------------------------------------------------------------------------
853// selection
854// ----------------------------------------------------------------------------
855
fd0eed64 856int wxListBox::GetSelection() const
c801d85f 857{
e6e83933 858 wxCHECK_MSG( m_list != NULL, wxNOT_FOUND, wxT("invalid listbox") );
ff8bfdbb 859
fd0eed64
RR
860 GList *child = m_list->children;
861 int count = 0;
862 while (child)
863 {
864 if (GTK_WIDGET(child->data)->state == GTK_STATE_SELECTED) return count;
865 count++;
866 child = child->next;
867 }
e6e83933 868 return wxNOT_FOUND;
6de97a3b 869}
c801d85f 870
fd0eed64 871int wxListBox::GetSelections( wxArrayInt& aSelections ) const
c801d85f 872{
e6e83933 873 wxCHECK_MSG( m_list != NULL, wxNOT_FOUND, wxT("invalid listbox") );
ff8bfdbb 874
fd0eed64
RR
875 // get the number of selected items first
876 GList *child = m_list->children;
877 int count = 0;
878 for (child = m_list->children; child != NULL; child = child->next)
879 {
880 if (GTK_WIDGET(child->data)->state == GTK_STATE_SELECTED)
881 count++;
882 }
c801d85f 883
fd0eed64
RR
884 aSelections.Empty();
885
ff8bfdbb 886 if (count > 0)
868a2826 887 {
fd0eed64
RR
888 // now fill the list
889 aSelections.Alloc(count); // optimization attempt
890 int i = 0;
891 for (child = m_list->children; child != NULL; child = child->next, i++)
892 {
893 if (GTK_WIDGET(child->data)->state == GTK_STATE_SELECTED)
894 aSelections.Add(i);
895 }
6a6d4eed 896 }
dcf40a56 897
fd0eed64 898 return count;
6de97a3b 899}
c801d85f 900
2ee3ee1b 901bool wxListBox::IsSelected( int n ) const
c801d85f 902{
caf6e6de 903 wxCHECK_MSG( m_list != NULL, false, wxT("invalid listbox") );
ff8bfdbb 904
fd0eed64 905 GList *target = g_list_nth( m_list->children, n );
9ef6ff8c 906
caf6e6de 907 wxCHECK_MSG( target, false, wxT("invalid listbox index") );
9ef6ff8c 908
c72b150f 909 return (GTK_WIDGET(target->data)->state == GTK_STATE_SELECTED) ;
6de97a3b 910}
c801d85f 911
c6179a84 912void wxListBox::DoSetSelection( int n, bool select )
c801d85f 913{
223d09f6 914 wxCHECK_RET( m_list != NULL, wxT("invalid listbox") );
ff8bfdbb 915
caf6e6de 916 m_blockEvent = true;
953704c1 917
fd0eed64 918 if (select)
159b66c0
RR
919 {
920 if ((m_windowStyle & wxLB_SINGLE) != 0)
921 gtk_list_unselect_item( m_list, m_prevSelection );
fd0eed64 922 gtk_list_select_item( m_list, n );
159b66c0
RR
923 m_prevSelection = n;
924 }
fd0eed64
RR
925 else
926 gtk_list_unselect_item( m_list, n );
014b0d06 927
caf6e6de 928 m_blockEvent = false;
6de97a3b 929}
c801d85f 930
9ef6ff8c 931void wxListBox::DoSetFirstItem( int n )
c801d85f 932{
9ef6ff8c
VZ
933 wxCHECK_RET( m_list, wxT("invalid listbox") );
934
935 if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (m_list))
936 return;
f96b15a3
RD
937
938 // terribly efficient
9ef6ff8c
VZ
939 const gchar *vadjustment_key = "gtk-vadjustment";
940 guint vadjustment_key_id = g_quark_from_static_string (vadjustment_key);
f96b15a3
RD
941
942 GtkAdjustment *adjustment =
9ef6ff8c
VZ
943 (GtkAdjustment*) gtk_object_get_data_by_id (GTK_OBJECT (m_list), vadjustment_key_id);
944 wxCHECK_RET( adjustment, wxT("invalid listbox code") );
945
946 GList *target = g_list_nth( m_list->children, n );
947 wxCHECK_RET( target, wxT("invalid listbox index") );
f96b15a3 948
9ef6ff8c
VZ
949 GtkWidget *item = GTK_WIDGET(target->data);
950 wxCHECK_RET( item, wxT("invalid listbox code") );
951
4a82abc8 952 if (item->allocation.y == -1)
9ef6ff8c 953 {
4a82abc8
RR
954 wxlistbox_idle_struct* data = new wxlistbox_idle_struct;
955 data->m_listbox = this;
956 data->m_item = n;
957 data->m_tag = gtk_idle_add_priority( 800, wxlistbox_idle_callback, (gpointer) data );
f96b15a3 958
4a82abc8 959 return;
9ef6ff8c
VZ
960 }
961
4a82abc8
RR
962 float y = item->allocation.y;
963 if (y > adjustment->upper - adjustment->page_size)
964 y = adjustment->upper - adjustment->page_size;
8161b5b9 965 gtk_adjustment_set_value( adjustment, y );
6de97a3b 966}
c801d85f 967
2ee3ee1b
VZ
968// ----------------------------------------------------------------------------
969// helpers
970// ----------------------------------------------------------------------------
c801d85f 971
11e1c70d 972int wxListBox::GtkGetIndex( GtkWidget *item ) const
c801d85f 973{
fd0eed64 974 if (item)
c801d85f 975 {
fd0eed64
RR
976 GList *child = m_list->children;
977 int count = 0;
978 while (child)
979 {
980 if (GTK_WIDGET(child->data) == item) return count;
981 count++;
982 child = child->next;
983 }
6de97a3b 984 }
fd0eed64 985 return -1;
6de97a3b 986}
c801d85f 987
ff8bfdbb 988#if wxUSE_TOOLTIPS
b5be07d4 989void wxListBox::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
b1170810 990{
b1170810
RR
991 GList *child = m_list->children;
992 while (child)
993 {
d3b9f782 994 gtk_tooltips_set_tip( tips, GTK_WIDGET( child->data ), wxConvCurrent->cWX2MB(tip), NULL );
b1170810
RR
995 child = child->next;
996 }
997}
ff8bfdbb 998#endif // wxUSE_TOOLTIPS
b1170810 999
fd0eed64 1000GtkWidget *wxListBox::GetConnectWidget()
e3e65dac 1001{
fec195b2
RR
1002 // return GTK_WIDGET(m_list);
1003 return m_widget;
6de97a3b 1004}
e3e65dac 1005
f96aa4d9 1006bool wxListBox::IsOwnGtkWindow( GdkWindow *window )
868a2826 1007{
89954433
VZ
1008 return m_widget->window == window ||
1009 GTK_WIDGET(m_list)->window == window;
868a2826 1010}
e3e65dac 1011
f40fdaa3 1012void wxListBox::DoApplyWidgetStyle(GtkRcStyle *style)
c058d771 1013{
a1b806b9 1014 if (m_hasBgCol && m_backgroundColour.IsOk())
e380f72b
RR
1015 {
1016 GdkWindow *window = GTK_WIDGET(m_list)->window;
f03fc89f
VZ
1017 if ( window )
1018 {
1019 m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) );
1020 gdk_window_set_background( window, m_backgroundColour.GetColor() );
1021 gdk_window_clear( window );
1022 }
e380f72b 1023 }
ff8bfdbb 1024
fd0eed64
RR
1025 GList *child = m_list->children;
1026 while (child)
1027 {
f40fdaa3 1028 gtk_widget_modify_style( GTK_WIDGET(child->data), style );
ff8bfdbb 1029
caaa4cfd
RR
1030 GtkBin *bin = GTK_BIN( child->data );
1031 GtkWidget *label = GTK_WIDGET( bin->child );
f40fdaa3 1032 gtk_widget_modify_style( label, style );
ff8bfdbb 1033
fd0eed64
RR
1034 child = child->next;
1035 }
68dda785 1036}
dcf924a3 1037
5e014a0c
RR
1038void wxListBox::OnInternalIdle()
1039{
1040 wxCursor cursor = m_cursor;
a1b806b9 1041 if (g_globalCursor.IsOk()) cursor = g_globalCursor;
5e014a0c 1042
a1b806b9 1043 if (GTK_WIDGET(m_list)->window && cursor.IsOk())
5e014a0c 1044 {
f7a11f8c 1045 /* I now set the cursor the anew in every OnInternalIdle call
2ee3ee1b
VZ
1046 as setting the cursor in a parent window also effects the
1047 windows above so that checking for the current cursor is
1048 not possible. */
9ef6ff8c 1049
2ee3ee1b 1050 gdk_window_set_cursor( GTK_WIDGET(m_list)->window, cursor.GetCursor() );
5e014a0c
RR
1051
1052 GList *child = m_list->children;
1053 while (child)
1054 {
1055 GtkBin *bin = GTK_BIN( child->data );
1056 GtkWidget *label = GTK_WIDGET( bin->child );
9ef6ff8c 1057
2ee3ee1b
VZ
1058 if (!label->window)
1059 break;
1060 else
1061 gdk_window_set_cursor( label->window, cursor.GetCursor() );
5e014a0c
RR
1062
1063 child = child->next;
1064 }
1065 }
1066
d7fa7eaa
RR
1067 if (g_delayedFocus == this)
1068 {
1069 if (GTK_WIDGET_REALIZED(m_widget))
1070 {
1071 gtk_widget_grab_focus( m_widget );
1072 g_delayedFocus = NULL;
1073 }
1074 }
1075
e39af974
JS
1076 if (wxUpdateUIEvent::CanUpdate(this))
1077 UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
5e014a0c
RR
1078}
1079
f68586e5
VZ
1080wxSize wxListBox::DoGetBestSize() const
1081{
f96b15a3
RD
1082 int lbWidth = 100; // some defaults
1083 int lbHeight = 110;
1084 int wLine;
1085
1086 // Find the widest line
aa61d352 1087 for(unsigned int i = 0; i < GetCount(); i++) {
f96b15a3
RD
1088 wxString str(GetString(i));
1089 GetTextExtent(str, &wLine, NULL);
1090 lbWidth = wxMax(lbWidth, wLine);
1091 }
1092
1093 // Add room for the scrollbar
a756f210 1094 lbWidth += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
f96b15a3
RD
1095
1096 // And just a bit more
1097 int cx, cy;
2b5f62a0 1098 GetTextExtent( wxT("X"), &cx, &cy);
f96b15a3
RD
1099 lbWidth += 3 * cx;
1100
1101 // don't make the listbox too tall (limit height to around 10 items) but don't
1102 // make it too small neither
1103 lbHeight = (cy+4) * wxMin(wxMax(GetCount(), 3), 10);
1104
9f884528 1105 wxSize best(lbWidth, lbHeight);
17a1ebd1 1106 CacheBestSize(best);
9f884528 1107 return best;
f68586e5
VZ
1108}
1109
3ae4c570
VZ
1110void wxListBox::FixUpMouseEvent(GtkWidget *widget, wxCoord& x, wxCoord& y)
1111{
1112 // the mouse event coords are relative to the listbox items, we need to
1113 // translate them to the normal client coords
1114 x += widget->allocation.x;
1115 y += widget->allocation.y;
1116}
1117
9d522606
RD
1118
1119// static
1120wxVisualAttributes
1121wxListBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
1122{
1123 return GetDefaultAttributesFromGTKWidget(gtk_list_new, true);
1124}
1125
3ae4c570 1126#endif // wxUSE_LISTBOX