]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/listbox.cpp
Disable wxUSE_ENH_METAFILE for wxGTK builds.
[wxWidgets.git] / src / gtk / listbox.cpp
CommitLineData
c801d85f 1/////////////////////////////////////////////////////////////////////////////
11e62fe6 2// Name: src/gtk/listbox.cpp
c801d85f
KB
3// Purpose:
4// Author: Robert Roebling
4a46cbe8 5// Modified By: Ryan Norton (GtkTreeView implementation)
f96aa4d9
RR
6// Id: $Id$
7// Copyright: (c) 1998 Robert Roebling
65571936 8// Licence: wxWindows licence
c801d85f
KB
9/////////////////////////////////////////////////////////////////////////////
10
14f355c2
VS
11// For compilers that support precompilation, includes "wx.h".
12#include "wx/wxprec.h"
13
dcf924a3
RR
14#if wxUSE_LISTBOX
15
88a7a4e1
WS
16#include "wx/listbox.h"
17
ad9835c9
WS
18#ifndef WX_PRECOMP
19 #include "wx/dynarray.h"
88a7a4e1 20 #include "wx/intl.h"
e4db172a 21 #include "wx/log.h"
de6185e2 22 #include "wx/utils.h"
9eddec69 23 #include "wx/settings.h"
e6e83933 24 #include "wx/checklst.h"
aaa6d89a 25 #include "wx/arrstr.h"
ad9835c9
WS
26#endif
27
291a8f20 28#if wxUSE_TOOLTIPS
ad9835c9 29 #include "wx/tooltip.h"
291a8f20 30#endif
c801d85f 31
4a46cbe8 32#include <gtk/gtk.h>
9dc44eff
PC
33#include "wx/gtk/private.h"
34#include "wx/gtk/private/gtk2-compat.h"
35#include "wx/gtk/private/object.h"
79bca169 36#include "wx/gtk/private/treeentry_gtk.h"
14b44999 37
a625c5b6 38#include <gdk/gdkkeysyms.h>
1897abe1 39#ifdef __WXGTK3__
14b44999
VS
40#include <gdk/gdkkeysyms-compat.h>
41#endif
83624f79 42
66bd6b93
RR
43//-----------------------------------------------------------------------------
44// data
45//-----------------------------------------------------------------------------
46
d7fa7eaa
RR
47extern bool g_blockEventsOnDrag;
48extern bool g_blockEventsOnScroll;
caaa4cfd 49
7a30ee8f 50
4a82abc8 51
c9433ea9 52//-----------------------------------------------------------------------------
4a46cbe8 53// Macro to tell which row the strings are in (1 if native checklist, 0 if not)
c9433ea9
RR
54//-----------------------------------------------------------------------------
55
470402b9 56#if wxUSE_CHECKLISTBOX
4a46cbe8
RR
57# define WXLISTBOX_DATACOLUMN_ARG(x) (x->m_hasCheckBoxes ? 1 : 0)
58#else
59# define WXLISTBOX_DATACOLUMN_ARG(x) (0)
470402b9 60#endif // wxUSE_CHECKLISTBOX
17a1ebd1 61
4a46cbe8 62#define WXLISTBOX_DATACOLUMN WXLISTBOX_DATACOLUMN_ARG(this)
c9433ea9 63
00d45d04
VZ
64// ----------------------------------------------------------------------------
65// helper functions
66// ----------------------------------------------------------------------------
67
68namespace
69{
70
71// Return the entry for the given listbox item.
11f1e38e 72wxTreeEntry *
00d45d04
VZ
73GetEntry(GtkListStore *store, GtkTreeIter *iter, const wxListBox *listbox)
74{
11f1e38e 75 wxTreeEntry* entry;
00d45d04
VZ
76 gtk_tree_model_get(GTK_TREE_MODEL(store),
77 iter,
78 WXLISTBOX_DATACOLUMN_ARG(listbox),
79 &entry,
80 -1);
6ee00008 81 g_object_unref(entry);
00d45d04
VZ
82 return entry;
83}
84
85} // anonymous namespace
86
c9433ea9 87//-----------------------------------------------------------------------------
4a46cbe8 88// "row-activated"
c9433ea9
RR
89//-----------------------------------------------------------------------------
90
865bb325 91extern "C" {
4a46cbe8 92static void
e4161a2a 93gtk_listbox_row_activated_callback(GtkTreeView * WXUNUSED(treeview),
4a46cbe8 94 GtkTreePath *path,
e4161a2a 95 GtkTreeViewColumn * WXUNUSED(col),
4a46cbe8 96 wxListBox *listbox)
c9433ea9 97{
4a46cbe8
RR
98 if (g_blockEventsOnDrag) return;
99 if (g_blockEventsOnScroll) return;
c9433ea9 100
470402b9 101 // This is triggered by either a double-click or a space press
c9433ea9 102
4a46cbe8 103 int sel = gtk_tree_path_get_indices(path)[0];
c9433ea9 104
09e744f5 105 listbox->GTKOnActivated(sel);
caaa4cfd 106}
865bb325 107}
66bd6b93 108
c801d85f 109//-----------------------------------------------------------------------------
470402b9 110// "changed"
c801d85f
KB
111//-----------------------------------------------------------------------------
112
4a46cbe8 113extern "C" {
470402b9 114static void
e4161a2a
VZ
115gtk_listitem_changed_callback(GtkTreeSelection * WXUNUSED(selection),
116 wxListBox *listbox )
c801d85f 117{
470402b9 118 if (g_blockEventsOnDrag) return;
f4322df6 119
24ee1bef 120 listbox->GTKOnSelectionChanged();
4a46cbe8 121}
24ee1bef 122
6de97a3b 123}
c801d85f 124
a625c5b6
RR
125//-----------------------------------------------------------------------------
126// "key_press_event"
127//-----------------------------------------------------------------------------
128
129extern "C" {
9ff9d30c 130static gboolean
a625c5b6
RR
131gtk_listbox_key_press_callback( GtkWidget *WXUNUSED(widget),
132 GdkEventKey *gdk_event,
133 wxListBox *listbox )
134{
03647350 135 if ((gdk_event->keyval == GDK_Return) ||
a625c5b6
RR
136 (gdk_event->keyval == GDK_ISO_Enter) ||
137 (gdk_event->keyval == GDK_KP_Enter))
138 {
f796e8f0
RR
139 int index = -1;
140 if (!listbox->HasMultipleSelection())
141 index = listbox->GetSelection();
142 else
a625c5b6 143 {
f796e8f0
RR
144 wxArrayInt sels;
145 if (listbox->GetSelections( sels ) < 1)
146 return FALSE;
147 index = sels[0];
148 }
03647350 149
f796e8f0
RR
150 if (index != wxNOT_FOUND)
151 {
09e744f5 152 listbox->GTKOnActivated(index);
03647350 153
9f400412
RR
154// wxMac and wxMSW always invoke default action
155// if (!ret)
a625c5b6
RR
156 {
157 // DClick not handled -> invoke default action
158 wxWindow *tlw = wxGetTopLevelParent( listbox );
159 if (tlw)
160 {
161 GtkWindow *gtk_window = GTK_WINDOW( tlw->GetHandle() );
162 if (gtk_window)
163 gtk_window_activate_default( gtk_window );
164 }
165 }
03647350 166
a625c5b6
RR
167 // Always intercept, otherwise we'd get another dclick
168 // event from row_activated
169 return TRUE;
170 }
171 }
03647350 172
a625c5b6
RR
173 return FALSE;
174}
175}
176
4a46cbe8
RR
177//-----------------------------------------------------------------------------
178// GtkTreeEntry destruction (to destroy client data)
179//-----------------------------------------------------------------------------
180
865bb325 181extern "C" {
11f1e38e 182static void tree_entry_destroy_cb(wxTreeEntry* entry,
4a46cbe8 183 wxListBox* listbox)
865bb325 184{
470402b9 185 if (listbox->HasClientObjectData())
4a46cbe8 186 {
11f1e38e 187 void* userdata = wx_tree_entry_get_userdata(entry);
470402b9 188 if (userdata)
4a46cbe8
RR
189 delete (wxClientData *)userdata;
190 }
865bb325
VZ
191}
192}
193
4a46cbe8
RR
194//-----------------------------------------------------------------------------
195// Sorting callback (standard CmpNoCase return value)
196//-----------------------------------------------------------------------------
197
865bb325 198extern "C" {
e4161a2a 199static gint gtk_listbox_sort_callback(GtkTreeModel * WXUNUSED(model),
4a46cbe8
RR
200 GtkTreeIter *a,
201 GtkTreeIter *b,
202 wxListBox *listbox)
865bb325 203{
11f1e38e 204 wxTreeEntry* entry1 = GetEntry(listbox->m_liststore, a, listbox);
00d45d04
VZ
205 wxCHECK_MSG(entry1, 0, wxT("Could not get first entry"));
206
11f1e38e 207 wxTreeEntry* entry2 = GetEntry(listbox->m_liststore, b, listbox);
00d45d04 208 wxCHECK_MSG(entry2, 0, wxT("Could not get second entry"));
4a46cbe8
RR
209
210 //We compare collate keys here instead of calling g_utf8_collate
eccace04 211 //as it is rather slow (and even the docs recommend this)
11f1e38e
PC
212 return strcmp(wx_tree_entry_get_collate_key(entry1),
213 wx_tree_entry_get_collate_key(entry2)) >= 0;
865bb325
VZ
214}
215}
216
a60c99e6 217//-----------------------------------------------------------------------------
4a46cbe8 218// Searching callback (TRUE == not equal, FALSE == equal)
c801d85f
KB
219//-----------------------------------------------------------------------------
220
865bb325 221extern "C" {
e4161a2a
VZ
222static gboolean gtk_listbox_searchequal_callback(GtkTreeModel * WXUNUSED(model),
223 gint WXUNUSED(column),
4a46cbe8
RR
224 const gchar* key,
225 GtkTreeIter* iter,
226 wxListBox* listbox)
f2e93537 227{
11f1e38e 228 wxTreeEntry* entry = GetEntry(listbox->m_liststore, iter, listbox);
4a46cbe8 229 wxCHECK_MSG(entry, 0, wxT("Could not get entry"));
17a1ebd1 230
00d45d04 231 wxGtkString keycollatekey(g_utf8_collate_key(key, -1));
4a46cbe8 232
11f1e38e 233 return strcmp(keycollatekey, wx_tree_entry_get_collate_key(entry)) != 0;
f2e93537 234}
865bb325 235}
f2e93537
RR
236
237//-----------------------------------------------------------------------------
238// wxListBox
239//-----------------------------------------------------------------------------
240
2ee3ee1b
VZ
241// ----------------------------------------------------------------------------
242// construction
243// ----------------------------------------------------------------------------
244
b6e5dfef 245void wxListBox::Init()
c801d85f 246{
d3b9f782 247 m_treeview = NULL;
88ac883a 248#if wxUSE_CHECKLISTBOX
caf6e6de 249 m_hasCheckBoxes = false;
88ac883a 250#endif // wxUSE_CHECKLISTBOX
6de97a3b 251}
c801d85f 252
584ad2a3
MB
253bool wxListBox::Create( wxWindow *parent, wxWindowID id,
254 const wxPoint &pos, const wxSize &size,
255 const wxArrayString& choices,
256 long style, const wxValidator& validator,
257 const wxString &name )
258{
259 wxCArrayString chs(choices);
260
261 return Create( parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
262 style, validator, name );
263}
264
dcf40a56 265bool wxListBox::Create( wxWindow *parent, wxWindowID id,
fd0eed64
RR
266 const wxPoint &pos, const wxSize &size,
267 int n, const wxString choices[],
2ee3ee1b
VZ
268 long style, const wxValidator& validator,
269 const wxString &name )
c801d85f 270{
4dcaf11a
RR
271 if (!PreCreation( parent, pos, size ) ||
272 !CreateBase( parent, id, pos, size, style, validator, name ))
273 {
223d09f6 274 wxFAIL_MSG( wxT("wxListBox creation failed") );
caf6e6de 275 return false;
4dcaf11a 276 }
6de97a3b 277
d3b9f782 278 m_widget = gtk_scrolled_window_new( NULL, NULL );
9ff9d30c 279 g_object_ref(m_widget);
034be888
RR
280 if (style & wxLB_ALWAYS_SB)
281 {
282 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
283 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
284 }
285 else
286 {
287 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
288 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
289 }
ff8bfdbb 290
6493aaca 291
496e7ec6 292 GTKScrolledWindowSetBorder(m_widget, style);
6493aaca 293
4a46cbe8
RR
294 m_treeview = GTK_TREE_VIEW( gtk_tree_view_new( ) );
295
296 //wxListBox doesn't have a header :)
297 //NB: If enabled SetFirstItem doesn't work correctly
298 gtk_tree_view_set_headers_visible(m_treeview, FALSE);
299
470402b9 300#if wxUSE_CHECKLISTBOX
4a46cbe8
RR
301 if(m_hasCheckBoxes)
302 ((wxCheckListBox*)this)->DoCreateCheckList();
470402b9 303#endif // wxUSE_CHECKLISTBOX
4a46cbe8
RR
304
305 // Create the data column
caf6e6de 306 gtk_tree_view_insert_column_with_attributes(m_treeview, -1, "",
4a46cbe8 307 gtk_cell_renderer_text_new(),
caf6e6de 308 "text",
4a46cbe8
RR
309 WXLISTBOX_DATACOLUMN, NULL);
310
311 // Now create+set the model (GtkListStore) - first argument # of columns
470402b9 312#if wxUSE_CHECKLISTBOX
4a46cbe8
RR
313 if(m_hasCheckBoxes)
314 m_liststore = gtk_list_store_new(2, G_TYPE_BOOLEAN,
11f1e38e 315 WX_TYPE_TREE_ENTRY);
4a46cbe8
RR
316 else
317#endif
11f1e38e 318 m_liststore = gtk_list_store_new(1, WX_TYPE_TREE_ENTRY);
4a46cbe8
RR
319
320 gtk_tree_view_set_model(m_treeview, GTK_TREE_MODEL(m_liststore));
caf6e6de 321
3fe39b0c 322 g_object_unref (m_liststore); //free on treeview destruction
caf6e6de 323
4a46cbe8
RR
324 // Disable the pop-up textctrl that enables searching - note that
325 // the docs specify that even if this disabled (which we are doing)
326 // the user can still have it through the start-interactive-search
327 // key binding...either way we want to provide a searchequal callback
caf6e6de 328 // NB: If this is enabled a doubleclick event (activate) gets sent
4a46cbe8
RR
329 // on a successful search
330 gtk_tree_view_set_search_column(m_treeview, WXLISTBOX_DATACOLUMN);
caf6e6de 331 gtk_tree_view_set_search_equal_func(m_treeview,
4a46cbe8
RR
332 (GtkTreeViewSearchEqualFunc) gtk_listbox_searchequal_callback,
333 this,
334 NULL);
335
336 gtk_tree_view_set_enable_search(m_treeview, FALSE);
337
4a82abc8 338 GtkSelectionMode mode;
b4de6e0d
VZ
339 // GTK_SELECTION_EXTENDED is a deprecated synonym for GTK_SELECTION_MULTIPLE
340 if ( style & (wxLB_MULTIPLE | wxLB_EXTENDED) )
4a82abc8 341 {
fd0eed64 342 mode = GTK_SELECTION_MULTIPLE;
4a82abc8 343 }
b4de6e0d 344 else // no multi-selection flags specified
4a82abc8 345 {
4a82abc8 346 m_windowStyle |= wxLB_SINGLE;
1d919083
VZ
347
348 // Notice that we must use BROWSE and not GTK_SELECTION_SINGLE because
349 // the latter allows to not select any items at all while a single
350 // selection listbox is supposed to always have a selection (at least
351 // once the user selected something, it might not have any initially).
352 mode = GTK_SELECTION_BROWSE;
4a82abc8 353 }
6a6d4eed 354
1e6ffd66 355 GtkTreeSelection* selection = gtk_tree_view_get_selection( m_treeview );
4a46cbe8
RR
356 gtk_tree_selection_set_mode( selection, mode );
357
470402b9 358 // Handle sortable stuff
a236aa20 359 if(HasFlag(wxLB_SORT))
4a46cbe8 360 {
470402b9 361 // Setup sorting in ascending (wx) order
caf6e6de
WS
362 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(m_liststore),
363 WXLISTBOX_DATACOLUMN,
4a46cbe8
RR
364 GTK_SORT_ASCENDING);
365
470402b9 366 // Set the sort callback
4a46cbe8
RR
367 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(m_liststore),
368 WXLISTBOX_DATACOLUMN,
369 (GtkTreeIterCompareFunc) gtk_listbox_sort_callback,
370 this, //userdata
371 NULL //"destroy notifier"
372 );
373 }
374
dcf40a56 375
dcd3c79c 376 gtk_container_add (GTK_CONTAINER (m_widget), GTK_WIDGET(m_treeview) );
caf6e6de 377
4a46cbe8 378 gtk_widget_show( GTK_WIDGET(m_treeview) );
0bb3fc29 379 m_focusWidget = GTK_WIDGET(m_treeview);
dcf40a56 380
a236aa20 381 Append(n, choices); // insert initial items
17a1ebd1 382
470402b9
RR
383 // generate dclick events
384 g_signal_connect_after(m_treeview, "row-activated",
4a46cbe8 385 G_CALLBACK(gtk_listbox_row_activated_callback), this);
2ee3ee1b 386
a625c5b6
RR
387 // for intercepting dclick generation by <ENTER>
388 g_signal_connect (m_treeview, "key_press_event",
389 G_CALLBACK (gtk_listbox_key_press_callback),
390 this);
f03fc89f 391 m_parent->DoAddChild( this );
ff8bfdbb 392
abdeb9e7 393 PostCreation(size);
170acdc9 394 SetInitialSize(size); // need this too because this is a wxControlWithItems
dcf40a56 395
1e6ffd66
RR
396 g_signal_connect_after (selection, "changed",
397 G_CALLBACK (gtk_listitem_changed_callback), this);
03647350 398
caf6e6de 399 return true;
6de97a3b 400}
c801d85f 401
fd0eed64 402wxListBox::~wxListBox()
c801d85f 403{
8ab75332
PC
404 if (m_treeview)
405 {
406 GTKDisconnect(m_treeview);
407 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
408 if (selection)
409 GTKDisconnect(selection);
410 }
4a82abc8 411
caaa4cfd 412 Clear();
6de97a3b 413}
c801d85f 414
dec7b5a8 415void wxListBox::GTKDisableEvents()
1e6ffd66
RR
416{
417 GtkTreeSelection* selection = gtk_tree_view_get_selection( m_treeview );
418
419 g_signal_handlers_block_by_func(selection,
420 (gpointer) gtk_listitem_changed_callback, this);
421}
422
dec7b5a8 423void wxListBox::GTKEnableEvents()
1e6ffd66
RR
424{
425 GtkTreeSelection* selection = gtk_tree_view_get_selection( m_treeview );
426
427 g_signal_handlers_unblock_by_func(selection,
428 (gpointer) gtk_listitem_changed_callback, this);
03647350 429
05d790f8 430 UpdateOldSelections();
1e6ffd66
RR
431}
432
937fc7db
RR
433
434void wxListBox::Update()
435{
436 wxWindow::Update();
03647350 437
937fc7db 438 if (m_treeview)
385e8575 439 gdk_window_process_updates(gtk_widget_get_window(GTK_WIDGET(m_treeview)), true);
937fc7db
RR
440}
441
8161b5b9
VZ
442// ----------------------------------------------------------------------------
443// adding items
444// ----------------------------------------------------------------------------
445
a236aa20
VZ
446int wxListBox::DoInsertItems(const wxArrayStringsAdapter& items,
447 unsigned int pos,
448 void **clientData,
cfe8a907 449 wxClientDataType type)
bb0ca8df 450{
a236aa20 451 wxCHECK_MSG( m_treeview != NULL, wxNOT_FOUND, wxT("invalid listbox") );
2ee3ee1b 452
9f884528 453 InvalidateBestSize();
8e125ff1
PC
454 int n = DoInsertItemsInLoop(items, pos, clientData, type);
455 UpdateOldSelections();
456 return n;
457}
9f884528 458
8e125ff1
PC
459int wxListBox::DoInsertOneItem(const wxString& item, unsigned int pos)
460{
11f1e38e
PC
461 wxTreeEntry* entry = wx_tree_entry_new();
462 wx_tree_entry_set_label(entry, wxGTK_CONV(item));
463 wx_tree_entry_set_destroy_func(entry, (wxTreeEntryDestroy)tree_entry_destroy_cb, this);
17a1ebd1 464
8e125ff1
PC
465#if wxUSE_CHECKLISTBOX
466 int entryCol = int(m_hasCheckBoxes);
467#else
468 int entryCol = 0;
469#endif
470 gtk_list_store_insert_with_values(m_liststore, NULL, pos, entryCol, entry, -1);
471 g_object_unref(entry);
bc60925f 472
8e125ff1 473 return pos;
fd0eed64 474}
dcf40a56 475
2ee3ee1b 476// ----------------------------------------------------------------------------
8161b5b9 477// deleting items
2ee3ee1b 478// ----------------------------------------------------------------------------
ff8bfdbb 479
a236aa20 480void wxListBox::DoClear()
fd0eed64 481{
4a46cbe8 482 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
fc54776e 483
dec7b5a8 484 GTKDisableEvents(); // just in case
2154130c 485
cdff1318
RD
486 InvalidateBestSize();
487
4a46cbe8 488 gtk_list_store_clear( m_liststore ); /* well, THAT was easy :) */
03647350 489
dec7b5a8 490 GTKEnableEvents();
fb040ec1
VZ
491
492 UpdateOldSelections();
6de97a3b 493}
c801d85f 494
a236aa20 495void wxListBox::DoDeleteOneItem(unsigned int n)
c801d85f 496{
4a46cbe8 497 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
fc54776e 498
cdff1318
RD
499 InvalidateBestSize();
500
dec7b5a8 501 GTKDisableEvents(); // just in case
2154130c 502
4a46cbe8 503 GtkTreeIter iter;
dec7b5a8 504 wxCHECK_RET( GTKGetIteratorFor(n, &iter), wxT("wrong listbox index") );
dcf40a56 505
0d5f4ba3
VZ
506 // this returns false if iter is invalid (e.g. deleting item at end) but
507 // since we don't use iter, we ignore the return value
4a46cbe8 508 gtk_list_store_remove(m_liststore, &iter);
03647350 509
dec7b5a8 510 GTKEnableEvents();
4a46cbe8 511}
dcf40a56 512
4a46cbe8 513// ----------------------------------------------------------------------------
0d5f4ba3 514// helper functions for working with iterators
4a46cbe8 515// ----------------------------------------------------------------------------
2ee3ee1b 516
dec7b5a8 517bool wxListBox::GTKGetIteratorFor(unsigned pos, GtkTreeIter *iter) const
4a46cbe8 518{
0d5f4ba3
VZ
519 if ( !gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(m_liststore),
520 iter, NULL, pos) )
4a46cbe8 521 {
0d5f4ba3
VZ
522 wxLogDebug(wxT("gtk_tree_model_iter_nth_child(%u) failed"), pos);
523 return false;
f5e27805 524 }
ff8bfdbb 525
0d5f4ba3
VZ
526 return true;
527}
528
dec7b5a8 529int wxListBox::GTKGetIndexFor(GtkTreeIter& iter) const
0d5f4ba3
VZ
530{
531 GtkTreePath *path =
532 gtk_tree_model_get_path(GTK_TREE_MODEL(m_liststore), &iter);
533
534 gint* pIntPath = gtk_tree_path_get_indices(path);
535
9a83f860 536 wxCHECK_MSG( pIntPath, wxNOT_FOUND, wxT("failed to get iterator path") );
0d5f4ba3
VZ
537
538 int idx = pIntPath[0];
539
540 gtk_tree_path_free( path );
541
542 return idx;
543}
544
545// get GtkTreeEntry from position (note: you need to g_unref it if valid)
11f1e38e 546wxTreeEntry* wxListBox::GTKGetEntry(unsigned n) const
0d5f4ba3
VZ
547{
548 GtkTreeIter iter;
dec7b5a8 549 if ( !GTKGetIteratorFor(n, &iter) )
0d5f4ba3
VZ
550 return NULL;
551
00d45d04 552 return GetEntry(m_liststore, &iter, this);
2ee3ee1b
VZ
553}
554
8161b5b9
VZ
555// ----------------------------------------------------------------------------
556// client data
557// ----------------------------------------------------------------------------
558
aa61d352 559void* wxListBox::DoGetItemClientData(unsigned int n) const
8161b5b9 560{
11f1e38e 561 wxTreeEntry* entry = GTKGetEntry(n);
4a46cbe8 562 wxCHECK_MSG(entry, NULL, wxT("could not get entry"));
8161b5b9 563
11f1e38e 564 return wx_tree_entry_get_userdata(entry);
8161b5b9
VZ
565}
566
aa61d352 567void wxListBox::DoSetItemClientData(unsigned int n, void* clientData)
8161b5b9 568{
11f1e38e 569 wxTreeEntry* entry = GTKGetEntry(n);
4a46cbe8 570 wxCHECK_RET(entry, wxT("could not get entry"));
8161b5b9 571
11f1e38e 572 wx_tree_entry_set_userdata(entry, clientData);
8161b5b9
VZ
573}
574
2ee3ee1b
VZ
575// ----------------------------------------------------------------------------
576// string list access
577// ----------------------------------------------------------------------------
578
33c6e437 579void wxListBox::SetString(unsigned int n, const wxString& label)
2ee3ee1b 580{
4a46cbe8 581 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
2ee3ee1b 582
185ac7cb
PC
583 GtkTreeIter iter;
584 wxCHECK_RET(GTKGetIteratorFor(n, &iter), "invalid index");
11f1e38e 585 wxTreeEntry* entry = GetEntry(m_liststore, &iter, this);
2ee3ee1b 586
33c6e437 587 // update the item itself
11f1e38e 588 wx_tree_entry_set_label(entry, wxGTK_CONV(label));
4a46cbe8 589
185ac7cb
PC
590 // signal row changed
591 GtkTreeModel* tree_model = GTK_TREE_MODEL(m_liststore);
592 GtkTreePath* path = gtk_tree_model_get_path(tree_model, &iter);
593 gtk_tree_model_row_changed(tree_model, path, &iter);
594 gtk_tree_path_free(path);
6de97a3b 595}
c801d85f 596
aa61d352 597wxString wxListBox::GetString(unsigned int n) const
c801d85f 598{
4a46cbe8 599 wxCHECK_MSG( m_treeview != NULL, wxEmptyString, wxT("invalid listbox") );
fc54776e 600
11f1e38e 601 wxTreeEntry* entry = GTKGetEntry(n);
4a46cbe8
RR
602 wxCHECK_MSG( entry, wxEmptyString, wxT("wrong listbox index") );
603
11f1e38e 604 return wxGTK_CONV_BACK(wx_tree_entry_get_label(entry));
2ee3ee1b
VZ
605}
606
aa61d352 607unsigned int wxListBox::GetCount() const
2ee3ee1b 608{
8228b893 609 wxCHECK_MSG( m_treeview != NULL, 0, wxT("invalid listbox") );
2ee3ee1b 610
aa61d352 611 return (unsigned int)gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_liststore), NULL);
6de97a3b 612}
c801d85f 613
11e62fe6 614int wxListBox::FindString( const wxString &item, bool bCase ) const
c801d85f 615{
4a46cbe8 616 wxCHECK_MSG( m_treeview != NULL, wxNOT_FOUND, wxT("invalid listbox") );
ff8bfdbb 617
4a46cbe8 618 //Sort of hackish - maybe there is a faster way
aa61d352 619 unsigned int nCount = wxListBox::GetCount();
ff8bfdbb 620
aa61d352 621 for(unsigned int i = 0; i < nCount; ++i)
4a46cbe8
RR
622 {
623 if( item.IsSameAs( wxListBox::GetString(i), bCase ) )
8228b893 624 return (int)i;
fd0eed64
RR
625 }
626
dcf40a56 627
4a46cbe8 628 // it's not an error if the string is not found -> no wxCHECK
2ee3ee1b 629 return wxNOT_FOUND;
6de97a3b 630}
c801d85f 631
2ee3ee1b
VZ
632// ----------------------------------------------------------------------------
633// selection
634// ----------------------------------------------------------------------------
635
09e744f5
VZ
636void wxListBox::GTKOnActivated(int item)
637{
ce7fe42e 638 SendEvent(wxEVT_LISTBOX_DCLICK, item, IsSelected(item));
09e744f5
VZ
639}
640
24ee1bef
VZ
641void wxListBox::GTKOnSelectionChanged()
642{
643 if ( HasFlag(wxLB_MULTIPLE | wxLB_EXTENDED) )
644 {
645 CalcAndSendEvent();
646 }
647 else // single selection
648 {
09e744f5
VZ
649 const int item = GetSelection();
650 if ( DoChangeSingleSelection(item) )
ce7fe42e 651 SendEvent(wxEVT_LISTBOX, item, true);
24ee1bef
VZ
652 }
653}
654
fd0eed64 655int wxListBox::GetSelection() const
c801d85f 656{
e6e83933
WS
657 wxCHECK_MSG( m_treeview != NULL, wxNOT_FOUND, wxT("invalid listbox"));
658 wxCHECK_MSG( HasFlag(wxLB_SINGLE), wxNOT_FOUND,
4a46cbe8 659 wxT("must be single selection listbox"));
ff8bfdbb 660
caf6e6de 661 GtkTreeIter iter;
4a46cbe8
RR
662 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
663
caf6e6de 664 // only works on single-sel
4a46cbe8 665 if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
e6e83933 666 return wxNOT_FOUND;
4a46cbe8 667
dec7b5a8 668 return GTKGetIndexFor(iter);
6de97a3b 669}
c801d85f 670
fd0eed64 671int wxListBox::GetSelections( wxArrayInt& aSelections ) const
c801d85f 672{
e6e83933 673 wxCHECK_MSG( m_treeview != NULL, wxNOT_FOUND, wxT("invalid listbox") );
c801d85f 674
fd0eed64
RR
675 aSelections.Empty();
676
05d790f8 677 int i = 0;
caf6e6de 678 GtkTreeIter iter;
4a46cbe8
RR
679 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
680
681 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(m_liststore), &iter))
682 { //gtk_tree_selection_get_selected_rows is GTK 2.2+ so iter instead
683 do
fd0eed64 684 {
4a46cbe8
RR
685 if (gtk_tree_selection_iter_is_selected(selection, &iter))
686 aSelections.Add(i);
687
688 i++;
689 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(m_liststore), &iter));
6a6d4eed 690 }
dcf40a56 691
4a46cbe8 692 return aSelections.GetCount();
6de97a3b 693}
c801d85f 694
2ee3ee1b 695bool wxListBox::IsSelected( int n ) const
c801d85f 696{
caf6e6de 697 wxCHECK_MSG( m_treeview != NULL, false, wxT("invalid listbox") );
ff8bfdbb 698
4a46cbe8 699 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
9ef6ff8c 700
4a46cbe8 701 GtkTreeIter iter;
dec7b5a8 702 wxCHECK_MSG( GTKGetIteratorFor(n, &iter), false, wxT("Invalid index") );
9ef6ff8c 703
d5027818 704 return gtk_tree_selection_iter_is_selected(selection, &iter) != 0;
6de97a3b 705}
c801d85f 706
c6179a84 707void wxListBox::DoSetSelection( int n, bool select )
c801d85f 708{
1e6ffd66
RR
709 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
710
dec7b5a8 711 GTKDisableEvents();
03647350 712
1e6ffd66
RR
713 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
714
ef33382e
VZ
715 // passing -1 to SetSelection() is documented to deselect all items
716 if ( n == wxNOT_FOUND )
717 {
1e6ffd66 718 gtk_tree_selection_unselect_all(selection);
dec7b5a8 719 GTKEnableEvents();
470402b9 720 return;
ef33382e
VZ
721 }
722
723 wxCHECK_RET( IsValid(n), wxT("invalid index in wxListBox::SetSelection") );
724
03647350 725
4a46cbe8 726 GtkTreeIter iter;
dec7b5a8 727 wxCHECK_RET( GTKGetIteratorFor(n, &iter), wxT("Invalid index") );
ff8bfdbb 728
fd0eed64 729 if (select)
4a46cbe8 730 gtk_tree_selection_select_iter(selection, &iter);
fd0eed64 731 else
4a46cbe8 732 gtk_tree_selection_unselect_iter(selection, &iter);
e4161a2a 733
0b2e06f9
RR
734 GtkTreePath* path = gtk_tree_model_get_path(
735 GTK_TREE_MODEL(m_liststore), &iter);
736
737 gtk_tree_view_scroll_to_cell(m_treeview, path, NULL, FALSE, 0.0f, 0.0f);
738
739 gtk_tree_path_free(path);
014b0d06 740
dec7b5a8 741 GTKEnableEvents();
1e6ffd66
RR
742}
743
0d29a482 744void wxListBox::DoScrollToCell(int n, float alignY, float alignX)
c801d85f 745{
4a46cbe8 746 wxCHECK_RET( m_treeview, wxT("invalid listbox") );
8228b893 747 wxCHECK_RET( IsValid(n), wxT("invalid index"));
9ef6ff8c 748
4a46cbe8 749 //RN: I have no idea why this line is needed...
1897abe1 750 if (gtk_widget_has_grab(GTK_WIDGET(m_treeview)))
9ef6ff8c 751 return;
f96b15a3 752
28354d90 753 GtkTreeIter iter;
dec7b5a8 754 if ( !GTKGetIteratorFor(n, &iter) )
0d5f4ba3 755 return;
4a46cbe8 756
28354d90
VZ
757 GtkTreePath* path = gtk_tree_model_get_path(
758 GTK_TREE_MODEL(m_liststore), &iter);
9ef6ff8c 759
28354d90
VZ
760 // Scroll to the desired cell (0.0 == topleft alignment)
761 gtk_tree_view_scroll_to_cell(m_treeview, path, NULL,
0d29a482 762 TRUE, alignY, alignX);
4a46cbe8 763
28354d90 764 gtk_tree_path_free(path);
6de97a3b 765}
c801d85f 766
0d29a482
VZ
767void wxListBox::DoSetFirstItem(int n)
768{
769 DoScrollToCell(n, 0, 0);
770}
771
772void wxListBox::EnsureVisible(int n)
773{
774 DoScrollToCell(n, 0.5, 0);
775}
776
c00fed0e
VZ
777// ----------------------------------------------------------------------------
778// hittest
779// ----------------------------------------------------------------------------
780
57772185 781int wxListBox::DoListHitTest(const wxPoint& point) const
c00fed0e 782{
a183ddba
VZ
783 // gtk_tree_view_get_path_at_pos() also gets items that are not visible and
784 // we only want visible items we need to check for it manually here
22a35096 785 if ( !GetClientRect().Contains(point) )
a183ddba
VZ
786 return wxNOT_FOUND;
787
57772185 788 // need to translate from master window since it is in client coords
c00fed0e 789 gint binx, biny;
caf6e6de 790 gdk_window_get_geometry(gtk_tree_view_get_bin_window(m_treeview),
9dc44eff 791 &binx, &biny, NULL, NULL);
c00fed0e
VZ
792
793 GtkTreePath* path;
57772185
VZ
794 if ( !gtk_tree_view_get_path_at_pos
795 (
caf6e6de 796 m_treeview,
57772185
VZ
797 point.x - binx,
798 point.y - biny,
799 &path,
800 NULL, // [out] column (always 0 here)
801 NULL, // [out] x-coord relative to the cell (not interested)
802 NULL // [out] y-coord relative to the cell
803 ) )
c00fed0e
VZ
804 {
805 return wxNOT_FOUND;
806 }
807
808 int index = gtk_tree_path_get_indices(path)[0];
809 gtk_tree_path_free(path);
810
811 return index;
812}
4a46cbe8 813
2ee3ee1b
VZ
814// ----------------------------------------------------------------------------
815// helpers
816// ----------------------------------------------------------------------------
c801d85f 817
fd0eed64 818GtkWidget *wxListBox::GetConnectWidget()
e3e65dac 819{
e5e41cfe
VZ
820 // the correct widget for listbox events (such as mouse clicks for example)
821 // is m_treeview, not the parent scrolled window
822 return GTK_WIDGET(m_treeview);
6de97a3b 823}
e3e65dac 824
1c2b98d6 825GdkWindow *wxListBox::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
868a2826 826{
1c2b98d6 827 return gtk_tree_view_get_bin_window(m_treeview);
868a2826 828}
e3e65dac 829
f40fdaa3 830void wxListBox::DoApplyWidgetStyle(GtkRcStyle *style)
c058d771 831{
9dc44eff
PC
832#ifdef __WXGTK3__
833 // don't know if this is even necessary, or how to do it
834#else
a1b806b9 835 if (m_hasBgCol && m_backgroundColour.IsOk())
e380f72b 836 {
04e044c4 837 GdkWindow *window = gtk_tree_view_get_bin_window(m_treeview);
4a46cbe8 838 if (window)
f03fc89f 839 {
08f60019 840 m_backgroundColour.CalcPixel( gdk_drawable_get_colormap( window ) );
f03fc89f
VZ
841 gdk_window_set_background( window, m_backgroundColour.GetColor() );
842 gdk_window_clear( window );
843 }
e380f72b 844 }
9dc44eff 845#endif
ff8bfdbb 846
9dc44eff 847 GTKApplyStyle(GTK_WIDGET(m_treeview), style);
68dda785 848}
dcf924a3 849
f68586e5
VZ
850wxSize wxListBox::DoGetBestSize() const
851{
4a46cbe8
RR
852 wxCHECK_MSG(m_treeview, wxDefaultSize, wxT("invalid tree view"));
853
cdff1318 854 // Start with a minimum size that's not too small
f96b15a3 855 int cx, cy;
2b5f62a0 856 GetTextExtent( wxT("X"), &cx, &cy);
47179317 857 int lbWidth = 0;
c64371de 858 int lbHeight = 10;
cdff1318 859
47179317
VZ
860 // Find the widest string.
861 const unsigned int count = GetCount();
862 if ( count )
cdff1318
RD
863 {
864 int wLine;
47179317
VZ
865 for ( unsigned int i = 0; i < count; i++ )
866 {
867 GetTextExtent(GetString(i), &wLine, NULL);
868 if ( wLine > lbWidth )
869 lbWidth = wLine;
cdff1318 870 }
47179317 871 }
cdff1318 872
47179317 873 lbWidth += 3 * cx;
cdff1318 874
47179317
VZ
875 // And just a bit more for the checkbox if present and then some
876 // (these are rough guesses)
470402b9 877#if wxUSE_CHECKLISTBOX
47179317
VZ
878 if ( m_hasCheckBoxes )
879 {
880 lbWidth += 35;
881 cy = cy > 25 ? cy : 25; // rough height of checkbox
cdff1318 882 }
47179317 883#endif
caf6e6de 884
cdff1318
RD
885 // Add room for the scrollbar
886 lbWidth += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
f96b15a3 887
47179317
VZ
888 // Don't make the listbox too tall but don't make it too small neither
889 lbHeight = (cy+4) * wxMin(wxMax(count, 3), 10);
890
9f884528 891 wxSize best(lbWidth, lbHeight);
17a1ebd1 892 CacheBestSize(best);
9f884528 893 return best;
f68586e5
VZ
894}
895
9d522606
RD
896// static
897wxVisualAttributes
898wxListBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
899{
7fff16b8 900 return GetDefaultAttributesFromGTKWidget(gtk_tree_view_new(), true);
9d522606
RD
901}
902
3ae4c570 903#endif // wxUSE_LISTBOX