]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/listbox.cpp
corrected off by 1 error in cMB2WC() call (thanks valgrind)
[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
fab591c5 28#include "wx/gtk/private.h"
4a46cbe8 29#include "wx/gtk/treeentry_gtk.h"
291a8f20
RR
30
31#if wxUSE_TOOLTIPS
ad9835c9 32 #include "wx/tooltip.h"
291a8f20 33#endif
c801d85f 34
4a46cbe8 35#include <gtk/gtk.h>
16c1f79c 36#include <gdk/gdkkeysyms.h>
83624f79 37
66bd6b93
RR
38//-----------------------------------------------------------------------------
39// data
40//-----------------------------------------------------------------------------
41
d7fa7eaa
RR
42extern bool g_blockEventsOnDrag;
43extern bool g_blockEventsOnScroll;
caaa4cfd 44
7a30ee8f 45
4a82abc8 46
c9433ea9 47//-----------------------------------------------------------------------------
4a46cbe8 48// Macro to tell which row the strings are in (1 if native checklist, 0 if not)
c9433ea9
RR
49//-----------------------------------------------------------------------------
50
470402b9 51#if wxUSE_CHECKLISTBOX
4a46cbe8
RR
52# define WXLISTBOX_DATACOLUMN_ARG(x) (x->m_hasCheckBoxes ? 1 : 0)
53#else
54# define WXLISTBOX_DATACOLUMN_ARG(x) (0)
470402b9 55#endif // wxUSE_CHECKLISTBOX
17a1ebd1 56
4a46cbe8 57#define WXLISTBOX_DATACOLUMN WXLISTBOX_DATACOLUMN_ARG(this)
c9433ea9
RR
58
59//-----------------------------------------------------------------------------
4a46cbe8 60// "row-activated"
c9433ea9
RR
61//-----------------------------------------------------------------------------
62
865bb325 63extern "C" {
4a46cbe8
RR
64static void
65gtk_listbox_row_activated_callback(GtkTreeView *treeview,
66 GtkTreePath *path,
67 GtkTreeViewColumn *col,
68 wxListBox *listbox)
c9433ea9 69{
4a46cbe8
RR
70 if (g_blockEventsOnDrag) return;
71 if (g_blockEventsOnScroll) return;
c9433ea9 72
470402b9 73 // This is triggered by either a double-click or a space press
c9433ea9 74
4a46cbe8 75 int sel = gtk_tree_path_get_indices(path)[0];
c9433ea9 76
470402b9
RR
77 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, listbox->GetId() );
78 event.SetEventObject( listbox );
acfd422a 79
470402b9 80 if (listbox->IsSelected(sel))
4a46cbe8 81 {
470402b9 82 GtkTreeEntry* entry = listbox->GtkGetEntry(sel);
f4322df6 83
470402b9 84 if (entry)
4a46cbe8 85 {
470402b9
RR
86 event.SetInt(sel);
87 event.SetString(wxConvUTF8.cMB2WX(gtk_tree_entry_get_label(entry)));
88
89 if ( listbox->HasClientObjectData() )
90 event.SetClientObject( (wxClientData*) gtk_tree_entry_get_userdata(entry) );
91 else if ( listbox->HasClientUntypedData() )
92 event.SetClientData( gtk_tree_entry_get_userdata(entry) );
f4322df6 93
470402b9 94 g_object_unref (entry);
4a46cbe8 95 }
470402b9 96 else
c64371de 97 {
470402b9
RR
98 wxLogSysError(wxT("Internal error - could not get entry for double-click"));
99 event.SetInt(-1);
4a46cbe8 100 }
6c8a980f 101 }
470402b9 102 else
7a30ee8f 103 {
470402b9 104 event.SetInt(-1);
4f22cf8d
RR
105 }
106
470402b9 107 listbox->GetEventHandler()->ProcessEvent( event );
caaa4cfd 108}
865bb325 109}
66bd6b93 110
c801d85f 111//-----------------------------------------------------------------------------
470402b9 112// "changed"
c801d85f
KB
113//-----------------------------------------------------------------------------
114
4a46cbe8 115extern "C" {
470402b9
RR
116static void
117gtk_listitem_changed_callback( GtkTreeSelection* selection, wxListBox *listbox )
c801d85f 118{
470402b9 119 if (g_blockEventsOnDrag) return;
f4322df6 120
470402b9 121 if (listbox->m_blockEvent) return;
f4322df6 122
470402b9
RR
123 wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, listbox->GetId() );
124 event.SetEventObject( listbox );
159b66c0 125
470402b9 126 if (listbox->HasFlag(wxLB_MULTIPLE) || listbox->HasFlag(wxLB_EXTENDED))
159b66c0 127 {
470402b9
RR
128 wxArrayInt selections;
129 listbox->GetSelections( selections );
f4322df6 130
470402b9
RR
131 if (selections.GetCount() == 0)
132 {
133 // indicate that this is a deselection
134 event.SetExtraLong( 0 );
135 event.SetInt( -1 );
f4322df6 136
470402b9 137 listbox->GetEventHandler()->ProcessEvent( event );
f4322df6 138
470402b9
RR
139 return;
140 }
141 else
142 {
143 // indicate that this is a selection
144 event.SetExtraLong( 1 );
145 event.SetInt( selections[0] );
f4322df6 146
470402b9
RR
147 listbox->GetEventHandler()->ProcessEvent( event );
148 }
149 }
150 else
151 {
152 int index = listbox->GetSelection();
153 if (index == wxNOT_FOUND)
154 {
155 // indicate that this is a deselection
156 event.SetExtraLong( 0 );
157 event.SetInt( -1 );
f4322df6 158
470402b9 159 listbox->GetEventHandler()->ProcessEvent( event );
f4322df6 160
470402b9
RR
161 return;
162 }
163 else
164 {
165 GtkTreeEntry* entry = listbox->GtkGetEntry( index );
4a46cbe8 166
470402b9
RR
167 // indicate that this is a selection
168 event.SetExtraLong( 1 );
4a46cbe8 169
470402b9
RR
170 event.SetInt( index );
171 event.SetString(wxConvUTF8.cMB2WX(gtk_tree_entry_get_label(entry)));
dcf40a56 172
470402b9
RR
173 if ( listbox->HasClientObjectData() )
174 event.SetClientObject(
caf6e6de 175 (wxClientData*) gtk_tree_entry_get_userdata(entry)
4a46cbe8 176 );
470402b9
RR
177 else if ( listbox->HasClientUntypedData() )
178 event.SetClientData( gtk_tree_entry_get_userdata(entry) );
09cf7c58 179
470402b9 180 listbox->GetEventHandler()->ProcessEvent( event );
4a46cbe8 181
470402b9
RR
182 g_object_unref (entry);
183 }
4a46cbe8 184 }
4a46cbe8 185}
6de97a3b 186}
c801d85f 187
4a46cbe8
RR
188//-----------------------------------------------------------------------------
189// GtkTreeEntry destruction (to destroy client data)
190//-----------------------------------------------------------------------------
191
865bb325 192extern "C" {
caf6e6de 193static void gtk_tree_entry_destroy_cb(GtkTreeEntry* entry,
4a46cbe8 194 wxListBox* listbox)
865bb325 195{
470402b9 196 if (listbox->HasClientObjectData())
4a46cbe8
RR
197 {
198 gpointer userdata = gtk_tree_entry_get_userdata(entry);
470402b9 199 if (userdata)
4a46cbe8
RR
200 delete (wxClientData *)userdata;
201 }
865bb325
VZ
202}
203}
204
4a46cbe8
RR
205//-----------------------------------------------------------------------------
206// Sorting callback (standard CmpNoCase return value)
207//-----------------------------------------------------------------------------
208
865bb325 209extern "C" {
4a46cbe8
RR
210static gint gtk_listbox_sort_callback(GtkTreeModel *model,
211 GtkTreeIter *a,
212 GtkTreeIter *b,
213 wxListBox *listbox)
865bb325 214{
4a46cbe8
RR
215 GtkTreeEntry* entry;
216 GtkTreeEntry* entry2;
217
218 gtk_tree_model_get(GTK_TREE_MODEL(listbox->m_liststore),
219 a,
caf6e6de 220 WXLISTBOX_DATACOLUMN_ARG(listbox),
4a46cbe8
RR
221 &entry, -1);
222 gtk_tree_model_get(GTK_TREE_MODEL(listbox->m_liststore),
223 b,
caf6e6de 224 WXLISTBOX_DATACOLUMN_ARG(listbox),
4a46cbe8
RR
225 &entry2, -1);
226 wxCHECK_MSG(entry, 0, wxT("Could not get entry"));
227 wxCHECK_MSG(entry2, 0, wxT("Could not get entry2"));
228
229 //We compare collate keys here instead of calling g_utf8_collate
230 //as it is rather slow (and even the docs reccommend this)
231 int ret = strcasecmp(gtk_tree_entry_get_collate_key(entry),
232 gtk_tree_entry_get_collate_key(entry2));
233
3fe39b0c
MR
234 g_object_unref (entry);
235 g_object_unref (entry2);
4a46cbe8
RR
236
237 return ret;
865bb325
VZ
238}
239}
240
a60c99e6 241//-----------------------------------------------------------------------------
4a46cbe8 242// Searching callback (TRUE == not equal, FALSE == equal)
c801d85f
KB
243//-----------------------------------------------------------------------------
244
865bb325 245extern "C" {
4a46cbe8
RR
246static gboolean gtk_listbox_searchequal_callback(GtkTreeModel* model,
247 gint column,
248 const gchar* key,
249 GtkTreeIter* iter,
250 wxListBox* listbox)
f2e93537 251{
4a46cbe8
RR
252 GtkTreeEntry* entry;
253
254 gtk_tree_model_get(GTK_TREE_MODEL(listbox->m_liststore),
255 iter,
caf6e6de 256 WXLISTBOX_DATACOLUMN_ARG(listbox),
4a46cbe8
RR
257 &entry, -1);
258 wxCHECK_MSG(entry, 0, wxT("Could not get entry"));
e808cf8a 259 wxGtkString keycollatekey(g_utf8_collate_key(key, -1));
17a1ebd1 260
4a46cbe8
RR
261 int ret = strcasecmp(keycollatekey,
262 gtk_tree_entry_get_collate_key(entry));
17a1ebd1 263
3fe39b0c 264 g_object_unref (entry);
4a46cbe8
RR
265
266 return ret != 0;
f2e93537 267}
865bb325 268}
f2e93537
RR
269
270//-----------------------------------------------------------------------------
271// wxListBox
272//-----------------------------------------------------------------------------
273
4a46cbe8 274IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControl)
c801d85f 275
2ee3ee1b
VZ
276// ----------------------------------------------------------------------------
277// construction
278// ----------------------------------------------------------------------------
279
b6e5dfef 280void wxListBox::Init()
c801d85f 281{
4a46cbe8 282 m_treeview = (GtkTreeView*) NULL;
88ac883a 283#if wxUSE_CHECKLISTBOX
caf6e6de 284 m_hasCheckBoxes = false;
88ac883a 285#endif // wxUSE_CHECKLISTBOX
6de97a3b 286}
c801d85f 287
584ad2a3
MB
288bool wxListBox::Create( wxWindow *parent, wxWindowID id,
289 const wxPoint &pos, const wxSize &size,
290 const wxArrayString& choices,
291 long style, const wxValidator& validator,
292 const wxString &name )
293{
294 wxCArrayString chs(choices);
295
296 return Create( parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
297 style, validator, name );
298}
299
dcf40a56 300bool wxListBox::Create( wxWindow *parent, wxWindowID id,
fd0eed64
RR
301 const wxPoint &pos, const wxSize &size,
302 int n, const wxString choices[],
2ee3ee1b
VZ
303 long style, const wxValidator& validator,
304 const wxString &name )
c801d85f 305{
caf6e6de 306 m_blockEvent = false;
dcf40a56 307
4dcaf11a
RR
308 if (!PreCreation( parent, pos, size ) ||
309 !CreateBase( parent, id, pos, size, style, validator, name ))
310 {
223d09f6 311 wxFAIL_MSG( wxT("wxListBox creation failed") );
caf6e6de 312 return false;
4dcaf11a 313 }
6de97a3b 314
fd0eed64 315 m_widget = gtk_scrolled_window_new( (GtkAdjustment*) NULL, (GtkAdjustment*) NULL );
034be888
RR
316 if (style & wxLB_ALWAYS_SB)
317 {
318 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
319 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
320 }
321 else
322 {
323 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
324 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
325 }
ff8bfdbb 326
6493aaca
VZ
327
328 GtkScrolledWindowSetBorder(m_widget, style);
329
4a46cbe8
RR
330 m_treeview = GTK_TREE_VIEW( gtk_tree_view_new( ) );
331
332 //wxListBox doesn't have a header :)
333 //NB: If enabled SetFirstItem doesn't work correctly
334 gtk_tree_view_set_headers_visible(m_treeview, FALSE);
335
470402b9 336#if wxUSE_CHECKLISTBOX
4a46cbe8
RR
337 if(m_hasCheckBoxes)
338 ((wxCheckListBox*)this)->DoCreateCheckList();
470402b9 339#endif // wxUSE_CHECKLISTBOX
4a46cbe8
RR
340
341 // Create the data column
caf6e6de 342 gtk_tree_view_insert_column_with_attributes(m_treeview, -1, "",
4a46cbe8 343 gtk_cell_renderer_text_new(),
caf6e6de 344 "text",
4a46cbe8
RR
345 WXLISTBOX_DATACOLUMN, NULL);
346
347 // Now create+set the model (GtkListStore) - first argument # of columns
470402b9 348#if wxUSE_CHECKLISTBOX
4a46cbe8
RR
349 if(m_hasCheckBoxes)
350 m_liststore = gtk_list_store_new(2, G_TYPE_BOOLEAN,
351 GTK_TYPE_TREE_ENTRY);
352 else
353#endif
354 m_liststore = gtk_list_store_new(1, GTK_TYPE_TREE_ENTRY);
355
356 gtk_tree_view_set_model(m_treeview, GTK_TREE_MODEL(m_liststore));
caf6e6de 357
3fe39b0c 358 g_object_unref (m_liststore); //free on treeview destruction
caf6e6de 359
4a46cbe8
RR
360 // Disable the pop-up textctrl that enables searching - note that
361 // the docs specify that even if this disabled (which we are doing)
362 // the user can still have it through the start-interactive-search
363 // key binding...either way we want to provide a searchequal callback
caf6e6de 364 // NB: If this is enabled a doubleclick event (activate) gets sent
4a46cbe8
RR
365 // on a successful search
366 gtk_tree_view_set_search_column(m_treeview, WXLISTBOX_DATACOLUMN);
caf6e6de 367 gtk_tree_view_set_search_equal_func(m_treeview,
4a46cbe8
RR
368 (GtkTreeViewSearchEqualFunc) gtk_listbox_searchequal_callback,
369 this,
370 NULL);
371
372 gtk_tree_view_set_enable_search(m_treeview, FALSE);
373
374
375 GtkTreeSelection* selection = gtk_tree_view_get_selection( m_treeview );
f4322df6 376
470402b9
RR
377 g_signal_connect_after (selection, "changed",
378 G_CALLBACK (gtk_listitem_changed_callback), this);
dcf40a56 379
4a82abc8 380 GtkSelectionMode mode;
fd0eed64 381 if (style & wxLB_MULTIPLE)
4a82abc8 382 {
fd0eed64 383 mode = GTK_SELECTION_MULTIPLE;
4a82abc8 384 }
fd0eed64 385 else if (style & wxLB_EXTENDED)
4a82abc8 386 {
fd0eed64 387 mode = GTK_SELECTION_EXTENDED;
4a82abc8
RR
388 }
389 else
390 {
391 // if style was 0 set single mode
392 m_windowStyle |= wxLB_SINGLE;
4fcfa27c 393 mode = GTK_SELECTION_SINGLE;
4a82abc8 394 }
6a6d4eed 395
4a46cbe8
RR
396 gtk_tree_selection_set_mode( selection, mode );
397
470402b9 398 // Handle sortable stuff
4a46cbe8
RR
399 if(style & wxLB_SORT)
400 {
470402b9 401 // Setup sorting in ascending (wx) order
caf6e6de
WS
402 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(m_liststore),
403 WXLISTBOX_DATACOLUMN,
4a46cbe8
RR
404 GTK_SORT_ASCENDING);
405
470402b9 406 // Set the sort callback
4a46cbe8
RR
407 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(m_liststore),
408 WXLISTBOX_DATACOLUMN,
409 (GtkTreeIterCompareFunc) gtk_listbox_sort_callback,
410 this, //userdata
411 NULL //"destroy notifier"
412 );
413 }
414
dcf40a56 415
dcd3c79c 416 gtk_container_add (GTK_CONTAINER (m_widget), GTK_WIDGET(m_treeview) );
caf6e6de 417
4a46cbe8 418 gtk_widget_show( GTK_WIDGET(m_treeview) );
0bb3fc29 419 m_focusWidget = GTK_WIDGET(m_treeview);
dcf40a56 420
4a46cbe8 421 wxListBox::DoInsertItems(wxArrayString(n, choices), 0); // insert initial items
17a1ebd1 422
470402b9
RR
423 // generate dclick events
424 g_signal_connect_after(m_treeview, "row-activated",
4a46cbe8 425 G_CALLBACK(gtk_listbox_row_activated_callback), this);
2ee3ee1b 426
f03fc89f 427 m_parent->DoAddChild( this );
ff8bfdbb 428
abdeb9e7 429 PostCreation(size);
170acdc9 430 SetInitialSize(size); // need this too because this is a wxControlWithItems
dcf40a56 431
caf6e6de 432 return true;
6de97a3b 433}
c801d85f 434
fd0eed64 435wxListBox::~wxListBox()
c801d85f 436{
caf6e6de 437 m_hasVMT = false;
4a82abc8 438
caaa4cfd 439 Clear();
6de97a3b 440}
c801d85f 441
8161b5b9
VZ
442// ----------------------------------------------------------------------------
443// adding items
444// ----------------------------------------------------------------------------
445
caf6e6de 446void wxListBox::GtkInsertItems(const wxArrayString& items,
aa61d352 447 void** clientData, unsigned int pos)
bb0ca8df 448{
4a46cbe8 449 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
2ee3ee1b 450
9f884528
RD
451 InvalidateBestSize();
452
4a46cbe8 453 // Create and set column ids and GValues
9ef6ff8c 454
aa61d352
VZ
455 unsigned int nNum = items.GetCount();
456 unsigned int nCurCount = wxListBox::GetCount();
4a46cbe8 457 wxASSERT_MSG(pos <= nCurCount, wxT("Invalid index passed to wxListBox"));
bb0ca8df 458
2eb1512a
MR
459 GtkTreeIter* pIter = NULL; // append by default
460 GtkTreeIter iter;
461 if (pos != nCurCount)
bb0ca8df 462 {
0d5f4ba3
VZ
463 wxCHECK_RET( GtkGetIteratorFor(pos, &iter),
464 wxT("internal wxListBox error in insertion") );
bb0ca8df 465
4a46cbe8 466 pIter = &iter;
bb0ca8df 467 }
2ee3ee1b 468
aa61d352 469 for (unsigned int i = 0; i < nNum; ++i)
2ee3ee1b 470 {
4a46cbe8 471 wxString label = items[i];
ff8bfdbb 472
4a46cbe8 473 GtkTreeEntry* entry = gtk_tree_entry_new();
d723a1ba 474 gtk_tree_entry_set_label(entry, wxGTK_CONV(label));
caf6e6de 475 gtk_tree_entry_set_destroy_func(entry,
4a46cbe8 476 (GtkTreeEntryDestroy)gtk_tree_entry_destroy_cb,
9fa72bd2 477 this);
dcf40a56 478
4a46cbe8
RR
479 if (clientData)
480 gtk_tree_entry_set_userdata(entry, clientData[i]);
9fa72bd2 481
4a46cbe8
RR
482 GtkTreeIter itercur;
483 gtk_list_store_insert_before(m_liststore, &itercur, pIter);
c9433ea9 484
0d5f4ba3 485 GtkSetItem(itercur, entry);
fd0eed64 486
0d5f4ba3 487 g_object_unref (entry);
4a46cbe8
RR
488 }
489}
17a1ebd1 490
aa61d352 491void wxListBox::DoInsertItems(const wxArrayString& items, unsigned int pos)
4a46cbe8 492{
8228b893
WS
493 wxCHECK_RET( IsValidInsert(pos), wxT("invalid index in wxListBox::InsertItems") );
494
aa61d352 495 GtkInsertItems(items, NULL, pos);
4a46cbe8 496}
014b0d06 497
4a46cbe8
RR
498int wxListBox::DoAppend( const wxString& item )
499{
be4bb86e
RR
500 wxCHECK_MSG( m_treeview != NULL, -1, wxT("invalid listbox") );
501
502 InvalidateBestSize();
503
504 GtkTreeEntry* entry = gtk_tree_entry_new();
505 gtk_tree_entry_set_label( entry, wxGTK_CONV(item) );
506 gtk_tree_entry_set_destroy_func(entry,
507 (GtkTreeEntryDestroy)gtk_tree_entry_destroy_cb,
508 this);
509
510 GtkTreeIter itercur;
511 gtk_list_store_insert_before( m_liststore, &itercur, NULL );
512
0d5f4ba3 513 GtkSetItem(itercur, entry);
be4bb86e 514
0d5f4ba3 515 g_object_unref (entry);
be4bb86e 516
0d5f4ba3 517 return GtkGetIndexFor(itercur);
fd0eed64
RR
518}
519
2ee3ee1b
VZ
520void wxListBox::DoSetItems( const wxArrayString& items,
521 void **clientData)
fd0eed64 522{
2ee3ee1b 523 Clear();
4a46cbe8 524 GtkInsertItems(items, clientData, 0);
fd0eed64 525}
dcf40a56 526
2ee3ee1b 527// ----------------------------------------------------------------------------
8161b5b9 528// deleting items
2ee3ee1b 529// ----------------------------------------------------------------------------
ff8bfdbb 530
fd0eed64
RR
531void wxListBox::Clear()
532{
4a46cbe8 533 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
fc54776e 534
cdff1318
RD
535 InvalidateBestSize();
536
4a46cbe8 537 gtk_list_store_clear( m_liststore ); /* well, THAT was easy :) */
6de97a3b 538}
c801d85f 539
aa61d352 540void wxListBox::Delete(unsigned int n)
c801d85f 541{
4a46cbe8 542 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
fc54776e 543
cdff1318
RD
544 InvalidateBestSize();
545
4a46cbe8 546 GtkTreeIter iter;
0d5f4ba3 547 wxCHECK_RET( GtkGetIteratorFor(n, &iter), wxT("wrong listbox index") );
dcf40a56 548
0d5f4ba3
VZ
549 // this returns false if iter is invalid (e.g. deleting item at end) but
550 // since we don't use iter, we ignore the return value
4a46cbe8
RR
551 gtk_list_store_remove(m_liststore, &iter);
552}
dcf40a56 553
4a46cbe8 554// ----------------------------------------------------------------------------
0d5f4ba3 555// helper functions for working with iterators
4a46cbe8 556// ----------------------------------------------------------------------------
2ee3ee1b 557
0d5f4ba3 558bool wxListBox::GtkGetIteratorFor(unsigned pos, GtkTreeIter *iter) const
4a46cbe8 559{
0d5f4ba3
VZ
560 if ( !gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(m_liststore),
561 iter, NULL, pos) )
4a46cbe8 562 {
0d5f4ba3
VZ
563 wxLogDebug(wxT("gtk_tree_model_iter_nth_child(%u) failed"), pos);
564 return false;
f5e27805 565 }
ff8bfdbb 566
0d5f4ba3
VZ
567 return true;
568}
569
570int wxListBox::GtkGetIndexFor(GtkTreeIter& iter) const
571{
572 GtkTreePath *path =
573 gtk_tree_model_get_path(GTK_TREE_MODEL(m_liststore), &iter);
574
575 gint* pIntPath = gtk_tree_path_get_indices(path);
576
577 wxCHECK_MSG( pIntPath, wxNOT_FOUND, _T("failed to get iterator path") );
578
579 int idx = pIntPath[0];
580
581 gtk_tree_path_free( path );
582
583 return idx;
584}
585
586// get GtkTreeEntry from position (note: you need to g_unref it if valid)
587GtkTreeEntry *wxListBox::GtkGetEntry(unsigned n) const
588{
589 GtkTreeIter iter;
590 if ( !GtkGetIteratorFor(n, &iter) )
591 return NULL;
592
4a46cbe8
RR
593
594 GtkTreeEntry* entry = NULL;
595 gtk_tree_model_get(GTK_TREE_MODEL(m_liststore), &iter,
596 WXLISTBOX_DATACOLUMN, &entry, -1);
597
598 return entry;
2ee3ee1b
VZ
599}
600
0d5f4ba3
VZ
601void wxListBox::GtkSetItem(GtkTreeIter& iter, const GtkTreeEntry *entry)
602{
603#if wxUSE_CHECKLISTBOX
604 if ( m_hasCheckBoxes )
605 {
606 gtk_list_store_set(m_liststore, &iter,
607 0, FALSE, // FALSE == not toggled
608 1, entry,
609 -1);
610 }
611 else
612#endif // wxUSE_CHECKLISTBOX
613 {
614 gtk_list_store_set(m_liststore, &iter, 0, entry, -1);
615 }
616}
617
8161b5b9
VZ
618// ----------------------------------------------------------------------------
619// client data
620// ----------------------------------------------------------------------------
621
aa61d352 622void* wxListBox::DoGetItemClientData(unsigned int n) const
8161b5b9 623{
aa61d352 624 wxCHECK_MSG( IsValid(n), NULL,
4a46cbe8 625 wxT("Invalid index passed to GetItemClientData") );
8161b5b9 626
4a46cbe8
RR
627 GtkTreeEntry* entry = GtkGetEntry(n);
628 wxCHECK_MSG(entry, NULL, wxT("could not get entry"));
8161b5b9 629
4a46cbe8 630 void* userdata = gtk_tree_entry_get_userdata( entry );
3fe39b0c 631 g_object_unref (entry);
4a46cbe8 632 return userdata;
8161b5b9
VZ
633}
634
aa61d352 635wxClientData* wxListBox::DoGetItemClientObject(unsigned int n) const
8161b5b9 636{
4a46cbe8 637 return (wxClientData*) wxListBox::DoGetItemClientData(n);
8161b5b9
VZ
638}
639
aa61d352 640void wxListBox::DoSetItemClientData(unsigned int n, void* clientData)
8161b5b9 641{
aa61d352 642 wxCHECK_RET( IsValid(n),
4a46cbe8 643 wxT("Invalid index passed to SetItemClientData") );
8161b5b9 644
4a46cbe8
RR
645 GtkTreeEntry* entry = GtkGetEntry(n);
646 wxCHECK_RET(entry, wxT("could not get entry"));
8161b5b9 647
4a46cbe8 648 gtk_tree_entry_set_userdata( entry, clientData );
3fe39b0c 649 g_object_unref (entry);
8161b5b9
VZ
650}
651
aa61d352 652void wxListBox::DoSetItemClientObject(unsigned int n, wxClientData* clientData)
8161b5b9 653{
4a46cbe8
RR
654 // wxItemContainer already deletes data for us
655 wxListBox::DoSetItemClientData(n, (void*) clientData);
8161b5b9
VZ
656}
657
2ee3ee1b
VZ
658// ----------------------------------------------------------------------------
659// string list access
660// ----------------------------------------------------------------------------
661
33c6e437 662void wxListBox::SetString(unsigned int n, const wxString& label)
2ee3ee1b 663{
8228b893 664 wxCHECK_RET( IsValid(n), wxT("invalid index in wxListBox::SetString") );
4a46cbe8 665 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
2ee3ee1b 666
4a46cbe8
RR
667 GtkTreeEntry* entry = GtkGetEntry(n);
668 wxCHECK_RET( entry, wxT("wrong listbox index") );
2ee3ee1b 669
33c6e437
VZ
670 // update the item itself
671 gtk_tree_entry_set_label(entry, wxGTK_CONV(label));
4a46cbe8 672
33c6e437
VZ
673 // and update the model which will refresh the tree too
674 GtkTreeIter iter;
0d5f4ba3 675 wxCHECK_RET( GtkGetIteratorFor(n, &iter), _T("failed to get iterator") );
4a46cbe8 676
0d5f4ba3
VZ
677 // FIXME: this resets the checked status of a wxCheckListBox item
678
679 GtkSetItem(iter, entry);
6de97a3b 680}
c801d85f 681
aa61d352 682wxString wxListBox::GetString(unsigned int n) const
c801d85f 683{
4a46cbe8 684 wxCHECK_MSG( m_treeview != NULL, wxEmptyString, wxT("invalid listbox") );
fc54776e 685
4a46cbe8
RR
686 GtkTreeEntry* entry = GtkGetEntry(n);
687 wxCHECK_MSG( entry, wxEmptyString, wxT("wrong listbox index") );
688
689 wxString label = wxGTK_CONV_BACK( gtk_tree_entry_get_label(entry) );
2ee3ee1b 690
3fe39b0c 691 g_object_unref (entry);
4a46cbe8 692 return label;
2ee3ee1b
VZ
693}
694
aa61d352 695unsigned int wxListBox::GetCount() const
2ee3ee1b 696{
8228b893 697 wxCHECK_MSG( m_treeview != NULL, 0, wxT("invalid listbox") );
2ee3ee1b 698
aa61d352 699 return (unsigned int)gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_liststore), NULL);
6de97a3b 700}
c801d85f 701
11e62fe6 702int wxListBox::FindString( const wxString &item, bool bCase ) const
c801d85f 703{
4a46cbe8 704 wxCHECK_MSG( m_treeview != NULL, wxNOT_FOUND, wxT("invalid listbox") );
ff8bfdbb 705
4a46cbe8 706 //Sort of hackish - maybe there is a faster way
aa61d352 707 unsigned int nCount = wxListBox::GetCount();
ff8bfdbb 708
aa61d352 709 for(unsigned int i = 0; i < nCount; ++i)
4a46cbe8
RR
710 {
711 if( item.IsSameAs( wxListBox::GetString(i), bCase ) )
8228b893 712 return (int)i;
fd0eed64
RR
713 }
714
dcf40a56 715
4a46cbe8 716 // it's not an error if the string is not found -> no wxCHECK
2ee3ee1b 717 return wxNOT_FOUND;
6de97a3b 718}
c801d85f 719
2ee3ee1b
VZ
720// ----------------------------------------------------------------------------
721// selection
722// ----------------------------------------------------------------------------
723
fd0eed64 724int wxListBox::GetSelection() const
c801d85f 725{
e6e83933
WS
726 wxCHECK_MSG( m_treeview != NULL, wxNOT_FOUND, wxT("invalid listbox"));
727 wxCHECK_MSG( HasFlag(wxLB_SINGLE), wxNOT_FOUND,
4a46cbe8 728 wxT("must be single selection listbox"));
ff8bfdbb 729
caf6e6de 730 GtkTreeIter iter;
4a46cbe8
RR
731 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
732
caf6e6de 733 // only works on single-sel
4a46cbe8 734 if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
e6e83933 735 return wxNOT_FOUND;
4a46cbe8 736
0d5f4ba3 737 return GtkGetIndexFor(iter);
6de97a3b 738}
c801d85f 739
fd0eed64 740int wxListBox::GetSelections( wxArrayInt& aSelections ) const
c801d85f 741{
e6e83933 742 wxCHECK_MSG( m_treeview != NULL, wxNOT_FOUND, wxT("invalid listbox") );
c801d85f 743
fd0eed64
RR
744 aSelections.Empty();
745
fd0eed64 746 int i = 0;
caf6e6de 747 GtkTreeIter iter;
4a46cbe8
RR
748 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
749
750 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(m_liststore), &iter))
751 { //gtk_tree_selection_get_selected_rows is GTK 2.2+ so iter instead
752 do
fd0eed64 753 {
4a46cbe8
RR
754 if (gtk_tree_selection_iter_is_selected(selection, &iter))
755 aSelections.Add(i);
756
757 i++;
758 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(m_liststore), &iter));
6a6d4eed 759 }
dcf40a56 760
4a46cbe8 761 return aSelections.GetCount();
6de97a3b 762}
c801d85f 763
2ee3ee1b 764bool wxListBox::IsSelected( int n ) const
c801d85f 765{
caf6e6de 766 wxCHECK_MSG( m_treeview != NULL, false, wxT("invalid listbox") );
ff8bfdbb 767
4a46cbe8 768 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
9ef6ff8c 769
4a46cbe8 770 GtkTreeIter iter;
0d5f4ba3 771 wxCHECK_MSG( GtkGetIteratorFor(n, &iter), false, wxT("Invalid index") );
9ef6ff8c 772
4a46cbe8 773 return gtk_tree_selection_iter_is_selected(selection, &iter);
6de97a3b 774}
c801d85f 775
c6179a84 776void wxListBox::DoSetSelection( int n, bool select )
c801d85f 777{
ef33382e
VZ
778 // passing -1 to SetSelection() is documented to deselect all items
779 if ( n == wxNOT_FOUND )
780 {
781 // ... and not generate any events in the process
782 GtkDeselectAll();
470402b9 783 return;
ef33382e
VZ
784 }
785
786 wxCHECK_RET( IsValid(n), wxT("invalid index in wxListBox::SetSelection") );
787
788 // don't generate the selection event
789 GtkSetSelection(n, select, true);
790}
791
792void wxListBox::GtkDeselectAll()
793{
794 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
795
796 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
797
798 m_blockEvent = true;
799
800 gtk_tree_selection_unselect_all(selection);
801
802 m_blockEvent = false;
4a46cbe8
RR
803}
804
805void wxListBox::GtkSetSelection(int n, const bool select, const bool blockEvent)
806{
807 wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") );
808
809 GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview);
810
811 GtkTreeIter iter;
0d5f4ba3 812 wxCHECK_RET( GtkGetIteratorFor(n, &iter), wxT("Invalid index") );
ff8bfdbb 813
4a46cbe8 814 m_blockEvent = blockEvent;
953704c1 815
fd0eed64 816 if (select)
4a46cbe8 817 gtk_tree_selection_select_iter(selection, &iter);
fd0eed64 818 else
4a46cbe8 819 gtk_tree_selection_unselect_iter(selection, &iter);
014b0d06 820
caf6e6de 821 m_blockEvent = false;
6de97a3b 822}
c801d85f 823
9ef6ff8c 824void wxListBox::DoSetFirstItem( int n )
c801d85f 825{
4a46cbe8 826 wxCHECK_RET( m_treeview, wxT("invalid listbox") );
8228b893 827 wxCHECK_RET( IsValid(n), wxT("invalid index"));
9ef6ff8c 828
4a46cbe8
RR
829 //RN: I have no idea why this line is needed...
830 if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (m_treeview))
9ef6ff8c 831 return;
f96b15a3 832
28354d90 833 GtkTreeIter iter;
0d5f4ba3
VZ
834 if ( !GtkGetIteratorFor(n, &iter) )
835 return;
4a46cbe8 836
28354d90
VZ
837 GtkTreePath* path = gtk_tree_model_get_path(
838 GTK_TREE_MODEL(m_liststore), &iter);
9ef6ff8c 839
28354d90
VZ
840 // Scroll to the desired cell (0.0 == topleft alignment)
841 gtk_tree_view_scroll_to_cell(m_treeview, path, NULL,
842 TRUE, 0.0f, 0.0f);
4a46cbe8 843
28354d90 844 gtk_tree_path_free(path);
6de97a3b 845}
c801d85f 846
c00fed0e
VZ
847// ----------------------------------------------------------------------------
848// hittest
849// ----------------------------------------------------------------------------
850
57772185 851int wxListBox::DoListHitTest(const wxPoint& point) const
c00fed0e 852{
a183ddba
VZ
853 // gtk_tree_view_get_path_at_pos() also gets items that are not visible and
854 // we only want visible items we need to check for it manually here
22a35096 855 if ( !GetClientRect().Contains(point) )
a183ddba
VZ
856 return wxNOT_FOUND;
857
57772185 858 // need to translate from master window since it is in client coords
c00fed0e 859 gint binx, biny;
caf6e6de 860 gdk_window_get_geometry(gtk_tree_view_get_bin_window(m_treeview),
c00fed0e
VZ
861 &binx, &biny, NULL, NULL, NULL);
862
863 GtkTreePath* path;
57772185
VZ
864 if ( !gtk_tree_view_get_path_at_pos
865 (
caf6e6de 866 m_treeview,
57772185
VZ
867 point.x - binx,
868 point.y - biny,
869 &path,
870 NULL, // [out] column (always 0 here)
871 NULL, // [out] x-coord relative to the cell (not interested)
872 NULL // [out] y-coord relative to the cell
873 ) )
c00fed0e
VZ
874 {
875 return wxNOT_FOUND;
876 }
877
878 int index = gtk_tree_path_get_indices(path)[0];
879 gtk_tree_path_free(path);
880
881 return index;
882}
4a46cbe8 883
2ee3ee1b
VZ
884// ----------------------------------------------------------------------------
885// helpers
886// ----------------------------------------------------------------------------
c801d85f 887
ff8bfdbb 888#if wxUSE_TOOLTIPS
6e5f6c7c 889void wxListBox::ApplyToolTip( GtkTooltips *tips, const gchar *tip )
b1170810 890{
4a46cbe8 891 // RN: Is this needed anymore?
6e5f6c7c 892 gtk_tooltips_set_tip( tips, GTK_WIDGET( m_treeview ), tip, (gchar*) NULL );
b1170810 893}
ff8bfdbb 894#endif // wxUSE_TOOLTIPS
b1170810 895
fd0eed64 896GtkWidget *wxListBox::GetConnectWidget()
e3e65dac 897{
e5e41cfe
VZ
898 // the correct widget for listbox events (such as mouse clicks for example)
899 // is m_treeview, not the parent scrolled window
900 return GTK_WIDGET(m_treeview);
6de97a3b 901}
e3e65dac 902
1c2b98d6 903GdkWindow *wxListBox::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
868a2826 904{
1c2b98d6 905 return gtk_tree_view_get_bin_window(m_treeview);
868a2826 906}
e3e65dac 907
f40fdaa3 908void wxListBox::DoApplyWidgetStyle(GtkRcStyle *style)
c058d771 909{
f40fdaa3 910 if (m_hasBgCol && m_backgroundColour.Ok())
e380f72b 911 {
04e044c4 912 GdkWindow *window = gtk_tree_view_get_bin_window(m_treeview);
4a46cbe8 913 if (window)
f03fc89f 914 {
08f60019 915 m_backgroundColour.CalcPixel( gdk_drawable_get_colormap( window ) );
f03fc89f
VZ
916 gdk_window_set_background( window, m_backgroundColour.GetColor() );
917 gdk_window_clear( window );
918 }
e380f72b 919 }
ff8bfdbb 920
4a46cbe8 921 gtk_widget_modify_style( GTK_WIDGET(m_treeview), style );
68dda785 922}
dcf924a3 923
f68586e5
VZ
924wxSize wxListBox::DoGetBestSize() const
925{
4a46cbe8
RR
926 wxCHECK_MSG(m_treeview, wxDefaultSize, wxT("invalid tree view"));
927
cdff1318 928 // Start with a minimum size that's not too small
f96b15a3 929 int cx, cy;
2b5f62a0 930 GetTextExtent( wxT("X"), &cx, &cy);
c64371de
RD
931 int lbWidth = 3 * cx;
932 int lbHeight = 10;
cdff1318 933
caf6e6de 934 // Get the visible area of the tree view (limit to the 10th item
cdff1318 935 // so that it isn't too big)
aa61d352 936 unsigned int count = GetCount();
cdff1318
RD
937 if (count)
938 {
939 int wLine;
940
941 // Find the widest line
aa61d352 942 for(unsigned int i = 0; i < count; i++) {
cdff1318
RD
943 wxString str(GetString(i));
944 GetTextExtent(str, &wLine, NULL);
945 lbWidth = wxMax(lbWidth, wLine);
946 }
947
948 lbWidth += 3 * cx;
949
950 // And just a bit more for the checkbox if present and then some
951 // (these are rough guesses)
470402b9 952#if wxUSE_CHECKLISTBOX
cdff1318
RD
953 if ( m_hasCheckBoxes )
954 {
955 lbWidth += 35;
956 cy = cy > 25 ? cy : 25; // rough height of checkbox
957 }
958#endif
f96b15a3 959
aa61d352
VZ
960 // don't make the listbox too tall (limit height to around 10 items) but don't
961 // make it too small neither
cdff1318
RD
962 lbHeight = (cy+4) * wxMin(wxMax(count, 3), 10);
963 }
caf6e6de 964
cdff1318
RD
965 // Add room for the scrollbar
966 lbWidth += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
f96b15a3 967
9f884528 968 wxSize best(lbWidth, lbHeight);
17a1ebd1 969 CacheBestSize(best);
9f884528 970 return best;
f68586e5
VZ
971}
972
9d522606
RD
973// static
974wxVisualAttributes
975wxListBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
976{
4a46cbe8 977 return GetDefaultAttributesFromGTKWidget(gtk_tree_view_new, true);
9d522606
RD
978}
979
3ae4c570 980#endif // wxUSE_LISTBOX