1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/dataview.cpp
3 // Purpose: wxDataViewCtrl GTK+2 implementation
4 // Author: Robert Roebling
5 // Copyright: (c) 1998 Robert Roebling
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11
12 #if wxUSE_DATAVIEWCTRL
13
14 #include "wx/dataview.h"
15
16 #ifndef wxHAS_GENERIC_DATAVIEWCTRL
17
18 #ifndef WX_PRECOMP
19 #include "wx/log.h"
20 #include "wx/dcclient.h"
21 #include "wx/sizer.h"
22 #include "wx/settings.h"
23 #include "wx/crt.h"
24 #endif
25
26 #include "wx/stockitem.h"
27 #include "wx/popupwin.h"
28 #include "wx/listimpl.cpp"
29
30 #include "wx/gtk/dc.h"
31 #ifndef __WXGTK3__
32 #include "wx/gtk/dcclient.h"
33 #endif
34
35 #include "wx/gtk/private.h"
36 #include "wx/gtk/private/event.h"
37 #include "wx/gtk/private/gdkconv.h"
38 #include "wx/gtk/private/image.h"
39 #include "wx/gtk/private/list.h"
40 #include "wx/gtk/private/treeview.h"
41 #include "wx/gtk/private/value.h"
42 using namespace wxGTKImpl;
43
44 class wxGtkDataViewModelNotifier;
45
46 #ifdef __WXGTK3__
47 #define wxConstGdkRect const GdkRectangle
48 #else
49 #define wxConstGdkRect GdkRectangle
50 #endif
51
52 //-----------------------------------------------------------------------------
53 //-----------------------------------------------------------------------------
54
55 static wxDataViewCtrlInternal *gs_internal = NULL;
56
57 class wxGtkTreeModelNode;
58
59 extern "C" {
60 typedef struct _GtkWxTreeModel GtkWxTreeModel;
61 }
62
63 // ----------------------------------------------------------------------------
64 // wxGtkTreePathList: self-destroying list of GtkTreePath objects.
65 // ----------------------------------------------------------------------------
66
67 class wxGtkTreePathList : public wxGtkList
68 {
69 public:
70 // Ctor takes ownership of the list.
wxGtkTreePathList(GList * list)71 explicit wxGtkTreePathList(GList* list)
72 : wxGtkList(list)
73 {
74 }
75
~wxGtkTreePathList()76 ~wxGtkTreePathList()
77 {
78 // Delete the list contents, wxGtkList will delete the list itself.
79 for (GList* p = m_list; p; p = p->next)
80 gtk_tree_path_free(static_cast<GtkTreePath*>(p->data));
81 }
82 };
83
84 // ----------------------------------------------------------------------------
85 // wxGtkTreeSelectionLock: prevent selection from changing during the
86 // lifetime of this object
87 // ----------------------------------------------------------------------------
88
89 // Implementation note: it could be expected that setting the selection
90 // function in this class ctor and resetting it back to the old value in its
91 // dtor would work, However in GTK+2 gtk_tree_selection_get_select_function()
92 // can't be passed NULL (see https://bugzilla.gnome.org/show_bug.cgi?id=626276
93 // which was only fixed in 2.90.5-304-g316b9da) so we can't do this.
94 //
95 // Instead, we always use the selection function (which
96 // imposes extra overhead, albeit minimal one, on all selection operations) and
97 // just set/reset the flag telling it whether it should allow or forbid the
98 // selection.
99 //
100 // Also notice that currently only a single object of this class may exist at
101 // any given moment. It's just simpler like this and we don't need anything
102 // more for now.
103
104 extern "C" {
105 static
wxdataview_selection_func(GtkTreeSelection * WXUNUSED (selection),GtkTreeModel * WXUNUSED (model),GtkTreePath * WXUNUSED (path),gboolean WXUNUSED (path_currently_selected),gpointer data)106 gboolean wxdataview_selection_func(GtkTreeSelection * WXUNUSED(selection),
107 GtkTreeModel * WXUNUSED(model),
108 GtkTreePath * WXUNUSED(path),
109 gboolean WXUNUSED(path_currently_selected),
110 gpointer data)
111 {
112 return data == NULL;
113 }
114 }
115
116 class wxGtkTreeSelectionLock
117 {
118 public:
wxGtkTreeSelectionLock(GtkTreeSelection * selection,bool & alreadySet)119 wxGtkTreeSelectionLock(GtkTreeSelection *selection, bool& alreadySet)
120 : m_selection(selection)
121 {
122 wxASSERT_MSG( !ms_instance, "this class is not reentrant currently" );
123
124 ms_instance = this;
125
126 if ( !alreadySet )
127 {
128 alreadySet = true;
129 CheckCurrentSelectionFunc(NULL);
130 }
131 else
132 {
133 CheckCurrentSelectionFunc(wxdataview_selection_func);
134 }
135
136 // Pass some non-NULL pointer as "data" for the callback, it doesn't
137 // matter what it is as long as it's non-NULL.
138 gtk_tree_selection_set_select_function(selection,
139 wxdataview_selection_func,
140 this,
141 NULL);
142 }
143
~wxGtkTreeSelectionLock()144 ~wxGtkTreeSelectionLock()
145 {
146 CheckCurrentSelectionFunc(wxdataview_selection_func);
147
148 gtk_tree_selection_set_select_function(m_selection,
149 wxdataview_selection_func,
150 NULL,
151 NULL);
152
153 ms_instance = NULL;
154 }
155
156 private:
CheckCurrentSelectionFunc(GtkTreeSelectionFunc func)157 void CheckCurrentSelectionFunc(GtkTreeSelectionFunc func)
158 {
159 // We can only use gtk_tree_selection_get_select_function() with 2.14+
160 // so check for its availability both during compile- and run-time.
161 #if GTK_CHECK_VERSION(2, 14, 0)
162 if ( !wx_is_at_least_gtk2(14) )
163 return;
164
165 // If this assert is triggered, it means the code elsewhere has called
166 // gtk_tree_selection_set_select_function() but currently doing this
167 // breaks this class so the code here needs to be changed.
168 wxASSERT_MSG
169 (
170 gtk_tree_selection_get_select_function(m_selection) == func,
171 "selection function has changed unexpectedly, review this code!"
172 );
173 #endif // GTK+ 2.14+
174
175 wxUnusedVar(func);
176 }
177
178 static wxGtkTreeSelectionLock *ms_instance;
179
180 GtkTreeSelection * const m_selection;
181
182 wxDECLARE_NO_COPY_CLASS(wxGtkTreeSelectionLock);
183 };
184
185 wxGtkTreeSelectionLock *wxGtkTreeSelectionLock::ms_instance = NULL;
186
187 //-----------------------------------------------------------------------------
188 // wxDataViewCtrlInternal
189 //-----------------------------------------------------------------------------
190
191 WX_DECLARE_LIST(wxDataViewItem, ItemList);
192 WX_DEFINE_LIST(ItemList)
193
194 class wxDataViewCtrlInternal
195 {
196 public:
197 wxDataViewCtrlInternal( wxDataViewCtrl *owner, wxDataViewModel *wx_model );
198 ~wxDataViewCtrlInternal();
199
200 // model iface
201 GtkTreeModelFlags get_flags();
202 gboolean get_iter( GtkTreeIter *iter, GtkTreePath *path );
203 GtkTreePath *get_path( GtkTreeIter *iter);
204 gboolean iter_next( GtkTreeIter *iter );
205 gboolean iter_children( GtkTreeIter *iter, GtkTreeIter *parent);
206 gboolean iter_has_child( GtkTreeIter *iter );
207 gint iter_n_children( GtkTreeIter *iter );
208 gboolean iter_nth_child( GtkTreeIter *iter, GtkTreeIter *parent, gint n );
209 gboolean iter_parent( GtkTreeIter *iter, GtkTreeIter *child );
210
211 // dnd iface
212
213 bool EnableDragSource( const wxDataFormat &format );
214 bool EnableDropTarget( const wxDataFormat &format );
215
216 gboolean row_draggable( GtkTreeDragSource *drag_source, GtkTreePath *path );
217 gboolean drag_data_delete( GtkTreeDragSource *drag_source, GtkTreePath* path );
218 gboolean drag_data_get( GtkTreeDragSource *drag_source, GtkTreePath *path,
219 GtkSelectionData *selection_data );
220 gboolean drag_data_received( GtkTreeDragDest *drag_dest, GtkTreePath *dest,
221 GtkSelectionData *selection_data );
222 gboolean row_drop_possible( GtkTreeDragDest *drag_dest, GtkTreePath *dest_path,
223 GtkSelectionData *selection_data );
224
225 // notifactions from wxDataViewModel
226 bool ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item );
227 bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item );
228 bool ItemChanged( const wxDataViewItem &item );
229 bool ValueChanged( const wxDataViewItem &item, unsigned int model_column );
230 bool Cleared();
231 bool BeforeReset();
232 bool AfterReset();
233 void Resort();
234
235 // sorting interface
SetSortOrder(GtkSortType sort_order)236 void SetSortOrder( GtkSortType sort_order ) { m_sort_order = sort_order; }
GetSortOrder() const237 GtkSortType GetSortOrder() const { return m_sort_order; }
238
SetSortColumn(int column)239 void SetSortColumn( int column ) { m_sort_column = column; }
GetSortColumn() const240 int GetSortColumn() const { return m_sort_column; }
241
SetDataViewSortColumn(wxDataViewColumn * column)242 void SetDataViewSortColumn( wxDataViewColumn *column ) { m_dataview_sort_column = column; }
GetDataViewSortColumn()243 wxDataViewColumn *GetDataViewSortColumn() { return m_dataview_sort_column; }
244
IsSorted() const245 bool IsSorted() const { return m_sort_column >= 0; }
246
247 // Freezing sort temporarily disables sorting when inserting the items into
248 // the tree as an optimization when inserting many items. Sort must be
249 // thawed, by calling FreezeSort(false), before inserting the last item to
250 // ensure that the items still end up in the right order.
FreezeSort(bool freeze)251 void FreezeSort(bool freeze) { m_sort_frozen = freeze; }
IsSortFrozen() const252 bool IsSortFrozen() const { return m_sort_frozen; }
253
254 // Should we be sorted either because we have a configured sort column or
255 // because we have a default sort order?
ShouldBeSorted() const256 bool ShouldBeSorted() const
257 {
258 return IsSorted() || GetDataViewModel()->HasDefaultCompare();
259 }
260
261
262 // Associate our model with the tree view or disassociate it from it
263 // without generating any selection changed events, unlike
264 // gtk_tree_view_set_model() that this function wraps.
265 void UseModel(bool use);
266
267 // accessors
GetDataViewModel()268 wxDataViewModel* GetDataViewModel() { return m_wx_model; }
GetDataViewModel() const269 const wxDataViewModel* GetDataViewModel() const { return m_wx_model; }
GetOwner()270 wxDataViewCtrl* GetOwner() { return m_owner; }
GetGtkModel()271 GtkWxTreeModel* GetGtkModel() { return m_gtk_model; }
272
273 // item can be deleted already in the model
274 int GetIndexOf( const wxDataViewItem &parent, const wxDataViewItem &item );
275
276 void OnInternalIdle();
277
278 protected:
279 void InitTree();
280 void ScheduleRefresh();
281
282 wxGtkTreeModelNode *FindNode( const wxDataViewItem &item );
283 wxGtkTreeModelNode *FindNode( GtkTreeIter *iter );
284 wxGtkTreeModelNode *FindParentNode( const wxDataViewItem &item );
285 wxGtkTreeModelNode *FindParentNode( GtkTreeIter *iter );
286 void BuildBranch( wxGtkTreeModelNode *branch );
287
288 private:
289 wxGtkTreeModelNode *m_root;
290 wxDataViewModel *m_wx_model;
291 GtkWxTreeModel *m_gtk_model;
292 wxDataViewCtrl *m_owner;
293 GtkSortType m_sort_order;
294 wxDataViewColumn *m_dataview_sort_column;
295 int m_sort_column;
296 bool m_sort_frozen;
297
298 GtkTargetEntry m_dragSourceTargetEntry;
299 wxCharBuffer m_dragSourceTargetEntryTarget;
300 wxDataObject *m_dragDataObject;
301
302 GtkTargetEntry m_dropTargetTargetEntry;
303 wxCharBuffer m_dropTargetTargetEntryTarget;
304 wxDataObject *m_dropDataObject;
305
306 wxGtkDataViewModelNotifier *m_notifier;
307
308 bool m_dirty;
309
310 public:
311 // Allow direct access to this one from wxDataViewCtrl as it's just a
312 // simple flag and it doesn't make much sense to encapsulate it.
313 bool m_selectionFuncSet;
314 };
315
316
317 //-----------------------------------------------------------------------------
318 // wxGtkTreeModelNode
319 //-----------------------------------------------------------------------------
320
321 static
wxGtkTreeModelChildCmp(void ** id1,void ** id2)322 int LINKAGEMODE wxGtkTreeModelChildCmp( void** id1, void** id2 )
323 {
324 int ret = gs_internal->GetDataViewModel()->Compare( wxDataViewItem(*id1), wxDataViewItem(*id2),
325 gs_internal->GetSortColumn(), (gs_internal->GetSortOrder() == GTK_SORT_ASCENDING) );
326
327 return ret;
328 }
329
330 WX_DEFINE_ARRAY_PTR( wxGtkTreeModelNode*, wxGtkTreeModelNodes );
331 WX_DEFINE_ARRAY_PTR( void*, wxGtkTreeModelChildren );
332
333 class wxGtkTreeModelNode
334 {
335 public:
wxGtkTreeModelNode(wxGtkTreeModelNode * parent,const wxDataViewItem & item,wxDataViewCtrlInternal * internal)336 wxGtkTreeModelNode( wxGtkTreeModelNode* parent, const wxDataViewItem &item,
337 wxDataViewCtrlInternal *internal )
338 : m_item(item)
339 {
340 m_parent = parent;
341 m_internal = internal;
342 }
343
~wxGtkTreeModelNode()344 ~wxGtkTreeModelNode()
345 {
346 size_t count = m_nodes.GetCount();
347 size_t i;
348 for (i = 0; i < count; i++)
349 {
350 wxGtkTreeModelNode *child = m_nodes.Item( i );
351 delete child;
352 }
353 }
354
AddNode(wxGtkTreeModelNode * child)355 void AddNode( wxGtkTreeModelNode* child )
356 {
357 m_nodes.Add( child );
358
359 void *id = child->GetItem().GetID();
360
361 m_children.Add( id );
362
363 if ( !m_internal->IsSortFrozen() && m_internal->ShouldBeSorted() )
364 {
365 gs_internal = m_internal;
366 m_children.Sort( &wxGtkTreeModelChildCmp );
367 }
368 }
369
InsertNode(wxGtkTreeModelNode * child,unsigned pos)370 void InsertNode( wxGtkTreeModelNode* child, unsigned pos )
371 {
372 if (m_internal->ShouldBeSorted())
373 {
374 AddNode(child);
375 return;
376 }
377
378 void *id = child->GetItem().GetID();
379
380 // Insert into m_nodes so that the order of nodes in m_nodes is the
381 // same as the order of their corresponding IDs in m_children:
382 const unsigned int count = m_nodes.GetCount();
383 bool inserted = false;
384 for (unsigned i = 0; i < count; i++)
385 {
386 wxGtkTreeModelNode *node = m_nodes[i];
387 int posInChildren = m_children.Index(node->GetItem().GetID());
388 if ( (unsigned)posInChildren >= pos )
389 {
390 m_nodes.Insert(child, i);
391 inserted = true;
392 break;
393 }
394 }
395 if ( !inserted )
396 m_nodes.Add(child);
397
398 m_children.Insert( id, pos );
399 }
400
AddLeaf(void * id)401 void AddLeaf( void* id )
402 {
403 InsertLeaf(id, m_children.size());
404 }
405
InsertLeaf(void * id,unsigned pos)406 void InsertLeaf( void* id, unsigned pos )
407 {
408 m_children.Insert( id, pos );
409
410 if ( !m_internal->IsSortFrozen() && m_internal->ShouldBeSorted() )
411 {
412 gs_internal = m_internal;
413 m_children.Sort( &wxGtkTreeModelChildCmp );
414 }
415 }
416
DeleteChild(void * id)417 void DeleteChild( void* id )
418 {
419 m_children.Remove( id );
420
421 unsigned int count = m_nodes.GetCount();
422 unsigned int pos;
423 for (pos = 0; pos < count; pos++)
424 {
425 wxGtkTreeModelNode *node = m_nodes.Item( pos );
426 if (node->GetItem().GetID() == id)
427 {
428 m_nodes.RemoveAt( pos );
429 delete node;
430 break;
431 }
432 }
433 }
434
435 // returns position of child node for given item in children list or wxNOT_FOUND
FindChildByItem(const wxDataViewItem & item) const436 int FindChildByItem(const wxDataViewItem& item) const
437 {
438 const void* itemId = item.GetID();
439 const wxGtkTreeModelChildren& nodes = m_children;
440 const int len = nodes.size();
441 for ( int i = 0; i < len; i++ )
442 {
443 if ( nodes[i] == itemId )
444 return i;
445 }
446 return wxNOT_FOUND;
447 }
448
GetParent()449 wxGtkTreeModelNode* GetParent()
450 { return m_parent; }
GetNodes()451 wxGtkTreeModelNodes &GetNodes()
452 { return m_nodes; }
GetChildren()453 wxGtkTreeModelChildren &GetChildren()
454 { return m_children; }
455
GetChildCount() const456 unsigned int GetChildCount() const { return m_children.GetCount(); }
GetNodesCount() const457 unsigned int GetNodesCount() const { return m_nodes.GetCount(); }
458
GetItem()459 wxDataViewItem &GetItem() { return m_item; }
GetInternal()460 wxDataViewCtrlInternal *GetInternal() { return m_internal; }
461
FreezeSort(bool freeze)462 void FreezeSort(bool freeze) { m_internal->FreezeSort(freeze); }
463
464 void Resort();
465
466 private:
467 wxGtkTreeModelNode *m_parent;
468 wxGtkTreeModelNodes m_nodes;
469 wxGtkTreeModelChildren m_children;
470 wxDataViewItem m_item;
471 wxDataViewCtrlInternal *m_internal;
472 };
473
474
475 //-----------------------------------------------------------------------------
476 // data
477 //-----------------------------------------------------------------------------
478
479 extern bool g_blockEventsOnDrag;
480
481 //-----------------------------------------------------------------------------
482 // define new GTK+ class wxGtkTreeModel
483 //-----------------------------------------------------------------------------
484
485 extern "C" {
486
487 #define GTK_TYPE_WX_TREE_MODEL (gtk_wx_tree_model_get_type ())
488 #define GTK_WX_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_WX_TREE_MODEL, GtkWxTreeModel))
489 #define GTK_IS_WX_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_WX_TREE_MODEL))
490 #define GTK_IS_WX_TREE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_WX_TREE_MODEL))
491
492 struct _GtkWxTreeModel
493 {
494 GObject parent;
495
496 /*< private >*/
497 gint stamp;
498 wxDataViewCtrlInternal *internal;
499 };
500
501 static GtkWxTreeModel *wxgtk_tree_model_new (void);
502 static void wxgtk_tree_model_init (GTypeInstance* instance, void*);
503
504 static void wxgtk_tree_model_tree_model_init (void* g_iface, void*);
505 static void wxgtk_tree_model_sortable_init (void* g_iface, void*);
506 static void wxgtk_tree_model_drag_source_init(void* g_iface, void*);
507 static void wxgtk_tree_model_drag_dest_init (void* g_iface, void*);
508
509 static GtkTreeModelFlags wxgtk_tree_model_get_flags (GtkTreeModel *tree_model);
510 static gint wxgtk_tree_model_get_n_columns (GtkTreeModel *tree_model);
511 static GType wxgtk_tree_model_get_column_type (GtkTreeModel *tree_model,
512 gint index);
513 static gboolean wxgtk_tree_model_get_iter (GtkTreeModel *tree_model,
514 GtkTreeIter *iter,
515 GtkTreePath *path);
516 static GtkTreePath *wxgtk_tree_model_get_path (GtkTreeModel *tree_model,
517 GtkTreeIter *iter);
518 static void wxgtk_tree_model_get_value (GtkTreeModel *tree_model,
519 GtkTreeIter *iter,
520 gint column,
521 GValue *value);
522 static gboolean wxgtk_tree_model_iter_next (GtkTreeModel *tree_model,
523 GtkTreeIter *iter);
524 static gboolean wxgtk_tree_model_iter_children (GtkTreeModel *tree_model,
525 GtkTreeIter *iter,
526 GtkTreeIter *parent);
527 static gboolean wxgtk_tree_model_iter_has_child (GtkTreeModel *tree_model,
528 GtkTreeIter *iter);
529 static gint wxgtk_tree_model_iter_n_children (GtkTreeModel *tree_model,
530 GtkTreeIter *iter);
531 static gboolean wxgtk_tree_model_iter_nth_child (GtkTreeModel *tree_model,
532 GtkTreeIter *iter,
533 GtkTreeIter *parent,
534 gint n);
535 static gboolean wxgtk_tree_model_iter_parent (GtkTreeModel *tree_model,
536 GtkTreeIter *iter,
537 GtkTreeIter *child);
538
539 /* sortable */
540 static gboolean wxgtk_tree_model_get_sort_column_id (GtkTreeSortable *sortable,
541 gint *sort_column_id,
542 GtkSortType *order);
543 static void wxgtk_tree_model_set_sort_column_id (GtkTreeSortable *sortable,
544 gint sort_column_id,
545 GtkSortType order);
546 static void wxgtk_tree_model_set_sort_func (GtkTreeSortable *sortable,
547 gint sort_column_id,
548 GtkTreeIterCompareFunc func,
549 gpointer data,
550 GDestroyNotify destroy);
551 static void wxgtk_tree_model_set_default_sort_func (GtkTreeSortable *sortable,
552 GtkTreeIterCompareFunc func,
553 gpointer data,
554 GDestroyNotify destroy);
555 static gboolean wxgtk_tree_model_has_default_sort_func (GtkTreeSortable *sortable);
556
557 /* drag'n'drop */
558 static gboolean wxgtk_tree_model_row_draggable (GtkTreeDragSource *drag_source,
559 GtkTreePath *path);
560 static gboolean wxgtk_tree_model_drag_data_delete (GtkTreeDragSource *drag_source,
561 GtkTreePath *path);
562 static gboolean wxgtk_tree_model_drag_data_get (GtkTreeDragSource *drag_source,
563 GtkTreePath *path,
564 GtkSelectionData *selection_data);
565 static gboolean wxgtk_tree_model_drag_data_received (GtkTreeDragDest *drag_dest,
566 GtkTreePath *dest,
567 GtkSelectionData *selection_data);
568 static gboolean wxgtk_tree_model_row_drop_possible (GtkTreeDragDest *drag_dest,
569 GtkTreePath *dest_path,
570 GtkSelectionData *selection_data);
571
572 static GType
gtk_wx_tree_model_get_type(void)573 gtk_wx_tree_model_get_type (void)
574 {
575 static GType tree_model_type = 0;
576
577 if (!tree_model_type)
578 {
579 const GTypeInfo tree_model_info =
580 {
581 sizeof (GObjectClass),
582 NULL, /* base_init */
583 NULL, /* base_finalize */
584 NULL,
585 NULL, /* class_finalize */
586 NULL, /* class_data */
587 sizeof (GtkWxTreeModel),
588 0,
589 wxgtk_tree_model_init,
590 NULL
591 };
592
593 static const GInterfaceInfo tree_model_iface_info =
594 {
595 wxgtk_tree_model_tree_model_init,
596 NULL,
597 NULL
598 };
599
600 static const GInterfaceInfo sortable_iface_info =
601 {
602 wxgtk_tree_model_sortable_init,
603 NULL,
604 NULL
605 };
606
607 static const GInterfaceInfo drag_source_iface_info =
608 {
609 wxgtk_tree_model_drag_source_init,
610 NULL,
611 NULL
612 };
613
614 static const GInterfaceInfo drag_dest_iface_info =
615 {
616 wxgtk_tree_model_drag_dest_init,
617 NULL,
618 NULL
619 };
620
621 tree_model_type = g_type_register_static (G_TYPE_OBJECT, "GtkWxTreeModel",
622 &tree_model_info, (GTypeFlags)0 );
623
624 g_type_add_interface_static (tree_model_type,
625 GTK_TYPE_TREE_MODEL,
626 &tree_model_iface_info);
627 g_type_add_interface_static (tree_model_type,
628 GTK_TYPE_TREE_SORTABLE,
629 &sortable_iface_info);
630 g_type_add_interface_static (tree_model_type,
631 GTK_TYPE_TREE_DRAG_DEST,
632 &drag_dest_iface_info);
633 g_type_add_interface_static (tree_model_type,
634 GTK_TYPE_TREE_DRAG_SOURCE,
635 &drag_source_iface_info);
636 }
637
638 return tree_model_type;
639 }
640
641 static GtkWxTreeModel *
wxgtk_tree_model_new(void)642 wxgtk_tree_model_new(void)
643 {
644 GtkWxTreeModel *retval = (GtkWxTreeModel *) g_object_new (GTK_TYPE_WX_TREE_MODEL, NULL);
645 return retval;
646 }
647
648 static void
wxgtk_tree_model_tree_model_init(void * g_iface,void *)649 wxgtk_tree_model_tree_model_init(void* g_iface, void*)
650 {
651 GtkTreeModelIface* iface = static_cast<GtkTreeModelIface*>(g_iface);
652 iface->get_flags = wxgtk_tree_model_get_flags;
653 iface->get_n_columns = wxgtk_tree_model_get_n_columns;
654 iface->get_column_type = wxgtk_tree_model_get_column_type;
655 iface->get_iter = wxgtk_tree_model_get_iter;
656 iface->get_path = wxgtk_tree_model_get_path;
657 iface->get_value = wxgtk_tree_model_get_value;
658 iface->iter_next = wxgtk_tree_model_iter_next;
659 iface->iter_children = wxgtk_tree_model_iter_children;
660 iface->iter_has_child = wxgtk_tree_model_iter_has_child;
661 iface->iter_n_children = wxgtk_tree_model_iter_n_children;
662 iface->iter_nth_child = wxgtk_tree_model_iter_nth_child;
663 iface->iter_parent = wxgtk_tree_model_iter_parent;
664 }
665
666 static void
wxgtk_tree_model_sortable_init(void * g_iface,void *)667 wxgtk_tree_model_sortable_init(void* g_iface, void*)
668 {
669 GtkTreeSortableIface* iface = static_cast<GtkTreeSortableIface*>(g_iface);
670 iface->get_sort_column_id = wxgtk_tree_model_get_sort_column_id;
671 iface->set_sort_column_id = wxgtk_tree_model_set_sort_column_id;
672 iface->set_sort_func = wxgtk_tree_model_set_sort_func;
673 iface->set_default_sort_func = wxgtk_tree_model_set_default_sort_func;
674 iface->has_default_sort_func = wxgtk_tree_model_has_default_sort_func;
675 }
676
677 static void
wxgtk_tree_model_drag_source_init(void * g_iface,void *)678 wxgtk_tree_model_drag_source_init(void* g_iface, void*)
679 {
680 GtkTreeDragSourceIface* iface = static_cast<GtkTreeDragSourceIface*>(g_iface);
681 iface->row_draggable = wxgtk_tree_model_row_draggable;
682 iface->drag_data_delete = wxgtk_tree_model_drag_data_delete;
683 iface->drag_data_get = wxgtk_tree_model_drag_data_get;
684 }
685
686 static void
wxgtk_tree_model_drag_dest_init(void * g_iface,void *)687 wxgtk_tree_model_drag_dest_init(void* g_iface, void*)
688 {
689 GtkTreeDragDestIface* iface = static_cast<GtkTreeDragDestIface*>(g_iface);
690 iface->drag_data_received = wxgtk_tree_model_drag_data_received;
691 iface->row_drop_possible = wxgtk_tree_model_row_drop_possible;
692 }
693
694 static void
wxgtk_tree_model_init(GTypeInstance * instance,void *)695 wxgtk_tree_model_init(GTypeInstance* instance, void*)
696 {
697 GtkWxTreeModel* tree_model = GTK_WX_TREE_MODEL(instance);
698 tree_model->internal = NULL;
699
700 // 0 is handled specially in wxGtkTreeCellDataFunc, so don't use it as the
701 // stamp.
702 do
703 {
704 tree_model->stamp = g_random_int();
705 } while ( tree_model->stamp == 0 );
706 }
707
708 } // extern "C"
709
710 //-----------------------------------------------------------------------------
711 // implement callbacks from wxGtkTreeModel class by letting
712 // them call the methods of wxWidgets' wxDataViewModel
713 //-----------------------------------------------------------------------------
714
715 // Note that all the model methods check if its stamp is valid because we want
716 // to avoid using the model being dissociated from the tree: its items may have
717 // been already deleted and referencing them can result in a crash. And while
718 // it's probably unnecessary to perform this check in all the methods, as only
719 // a couple of them can be really called from inside gtk_tree_view_set_model(),
720 // it does no harm to check this everywhere and this should be more robust.
721
722 static GtkTreeModelFlags
wxgtk_tree_model_get_flags(GtkTreeModel * tree_model)723 wxgtk_tree_model_get_flags (GtkTreeModel *tree_model)
724 {
725 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
726 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), (GtkTreeModelFlags)0 );
727
728 if ( wxtree_model->stamp == 0 )
729 return (GtkTreeModelFlags)0;
730
731 return wxtree_model->internal->get_flags();
732 }
733
734 static gint
wxgtk_tree_model_get_n_columns(GtkTreeModel * tree_model)735 wxgtk_tree_model_get_n_columns (GtkTreeModel *tree_model)
736 {
737 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
738 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), 0);
739
740 if ( wxtree_model->stamp == 0 )
741 return 0;
742
743 return wxtree_model->internal->GetDataViewModel()->GetColumnCount();
744 }
745
746 static GType
wxgtk_tree_model_get_column_type(GtkTreeModel * tree_model,gint index)747 wxgtk_tree_model_get_column_type (GtkTreeModel *tree_model,
748 gint index)
749 {
750 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
751 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), G_TYPE_INVALID);
752
753 if ( wxtree_model->stamp == 0 )
754 return G_TYPE_INVALID;
755
756 GType gtype = G_TYPE_INVALID;
757
758 wxString wxtype = wxtree_model->internal->GetDataViewModel()->GetColumnType( (unsigned int) index );
759
760 if (wxtype == wxT("string"))
761 gtype = G_TYPE_STRING;
762 else
763 {
764 gtype = G_TYPE_POINTER;
765 // wxFAIL_MSG( wxT("non-string columns not supported for searching yet") );
766 }
767
768 return gtype;
769 }
770
771 static gboolean
wxgtk_tree_model_get_iter(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreePath * path)772 wxgtk_tree_model_get_iter (GtkTreeModel *tree_model,
773 GtkTreeIter *iter,
774 GtkTreePath *path)
775 {
776 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
777 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
778 g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
779
780 if ( wxtree_model->stamp == 0 )
781 return FALSE;
782
783 return wxtree_model->internal->get_iter( iter, path );
784 }
785
786 static GtkTreePath *
wxgtk_tree_model_get_path(GtkTreeModel * tree_model,GtkTreeIter * iter)787 wxgtk_tree_model_get_path (GtkTreeModel *tree_model,
788 GtkTreeIter *iter)
789 {
790 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (tree_model), NULL);
791
792 GtkWxTreeModel *wxtree_model = GTK_WX_TREE_MODEL (tree_model);
793
794 if ( wxtree_model->stamp == 0 )
795 return gtk_tree_path_new();
796
797 g_return_val_if_fail (iter->stamp == wxtree_model->stamp, NULL);
798
799 return wxtree_model->internal->get_path( iter );
800 }
801
802 static void
wxgtk_tree_model_get_value(GtkTreeModel * tree_model,GtkTreeIter * iter,gint column,GValue * value)803 wxgtk_tree_model_get_value (GtkTreeModel *tree_model,
804 GtkTreeIter *iter,
805 gint column,
806 GValue *value)
807 {
808 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
809 g_return_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model) );
810
811 if ( wxtree_model->stamp == 0 )
812 return;
813
814 wxDataViewModel *model = wxtree_model->internal->GetDataViewModel();
815 wxString mtype = model->GetColumnType( (unsigned int) column );
816 if (mtype == wxT("string"))
817 {
818 wxVariant variant;
819 g_value_init( value, G_TYPE_STRING );
820 wxDataViewItem item( (void*) iter->user_data );
821 model->GetValue( variant, item, (unsigned int) column );
822
823 g_value_set_string( value, variant.GetString().utf8_str() );
824 }
825 else
826 {
827 wxFAIL_MSG( wxT("non-string columns not supported yet") );
828 }
829 }
830
831 static gboolean
wxgtk_tree_model_iter_next(GtkTreeModel * tree_model,GtkTreeIter * iter)832 wxgtk_tree_model_iter_next (GtkTreeModel *tree_model,
833 GtkTreeIter *iter)
834 {
835 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
836
837 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
838 g_return_val_if_fail (wxtree_model->stamp == iter->stamp, FALSE);
839
840 if ( wxtree_model->stamp == 0 )
841 return FALSE;
842
843 return wxtree_model->internal->iter_next( iter );
844 }
845
846 static gboolean
wxgtk_tree_model_iter_children(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent)847 wxgtk_tree_model_iter_children (GtkTreeModel *tree_model,
848 GtkTreeIter *iter,
849 GtkTreeIter *parent)
850 {
851 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
852
853 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
854 if (parent)
855 {
856 g_return_val_if_fail (wxtree_model->stamp == parent->stamp, FALSE);
857 }
858
859 if ( wxtree_model->stamp == 0 )
860 return FALSE;
861
862 return wxtree_model->internal->iter_children( iter, parent );
863 }
864
865 static gboolean
wxgtk_tree_model_iter_has_child(GtkTreeModel * tree_model,GtkTreeIter * iter)866 wxgtk_tree_model_iter_has_child (GtkTreeModel *tree_model,
867 GtkTreeIter *iter)
868 {
869 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
870 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
871 g_return_val_if_fail (wxtree_model->stamp == iter->stamp, FALSE);
872
873 return wxtree_model->internal->iter_has_child( iter );
874 }
875
876 static gint
wxgtk_tree_model_iter_n_children(GtkTreeModel * tree_model,GtkTreeIter * iter)877 wxgtk_tree_model_iter_n_children (GtkTreeModel *tree_model,
878 GtkTreeIter *iter)
879 {
880 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
881 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), 0);
882 g_return_val_if_fail ( !iter || wxtree_model->stamp == iter->stamp, 0);
883
884 if ( wxtree_model->stamp == 0 )
885 return 0;
886
887 return wxtree_model->internal->iter_n_children( iter );
888 }
889
890 static gboolean
wxgtk_tree_model_iter_nth_child(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent,gint n)891 wxgtk_tree_model_iter_nth_child (GtkTreeModel *tree_model,
892 GtkTreeIter *iter,
893 GtkTreeIter *parent,
894 gint n)
895 {
896 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
897 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
898
899 if ( wxtree_model->stamp == 0 )
900 return FALSE;
901
902 return wxtree_model->internal->iter_nth_child( iter, parent, n );
903 }
904
905 static gboolean
wxgtk_tree_model_iter_parent(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * child)906 wxgtk_tree_model_iter_parent (GtkTreeModel *tree_model,
907 GtkTreeIter *iter,
908 GtkTreeIter *child)
909 {
910 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
911 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
912 g_return_val_if_fail (wxtree_model->stamp == child->stamp, FALSE);
913
914 if ( wxtree_model->stamp == 0 )
915 return FALSE;
916
917 return wxtree_model->internal->iter_parent( iter, child );
918 }
919
920 /* drag'n'drop iface */
921 static gboolean
wxgtk_tree_model_row_draggable(GtkTreeDragSource * drag_source,GtkTreePath * path)922 wxgtk_tree_model_row_draggable (GtkTreeDragSource *drag_source,
923 GtkTreePath *path)
924 {
925 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) drag_source;
926 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
927
928 if ( wxtree_model->stamp == 0 )
929 return FALSE;
930
931 return wxtree_model->internal->row_draggable( drag_source, path );
932 }
933
934 static gboolean
wxgtk_tree_model_drag_data_delete(GtkTreeDragSource * drag_source,GtkTreePath * path)935 wxgtk_tree_model_drag_data_delete (GtkTreeDragSource *drag_source,
936 GtkTreePath *path)
937 {
938 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) drag_source;
939 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
940
941 if ( wxtree_model->stamp == 0 )
942 return FALSE;
943
944 return wxtree_model->internal->drag_data_delete( drag_source, path );
945 }
946
947 static gboolean
wxgtk_tree_model_drag_data_get(GtkTreeDragSource * drag_source,GtkTreePath * path,GtkSelectionData * selection_data)948 wxgtk_tree_model_drag_data_get (GtkTreeDragSource *drag_source,
949 GtkTreePath *path,
950 GtkSelectionData *selection_data)
951 {
952 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) drag_source;
953 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
954
955 #if 0
956 wxPrintf( "drag_get_data\n");
957
958 wxGtkString atom_selection(gdk_atom_name(selection_data->selection));
959 wxPrintf( "selection %s\n", wxString::FromAscii(atom_selection) );
960
961 wxGtkString atom_target(gdk_atom_name(selection_data->target));
962 wxPrintf( "target %s\n", wxString::FromAscii(atom_target) );
963
964 wxGtkString atom_type(gdk_atom_name(selection_data->type));
965 wxPrintf( "type %s\n", wxString::FromAscii(atom_type) );
966
967 wxPrintf( "format %d\n", selection_data->format );
968 #endif
969
970 if ( wxtree_model->stamp == 0 )
971 return FALSE;
972
973 return wxtree_model->internal->drag_data_get( drag_source, path, selection_data );
974 }
975
976 static gboolean
wxgtk_tree_model_drag_data_received(GtkTreeDragDest * drag_dest,GtkTreePath * dest,GtkSelectionData * selection_data)977 wxgtk_tree_model_drag_data_received (GtkTreeDragDest *drag_dest,
978 GtkTreePath *dest,
979 GtkSelectionData *selection_data)
980 {
981 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) drag_dest;
982 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
983
984 if ( wxtree_model->stamp == 0 )
985 return FALSE;
986
987 return wxtree_model->internal->drag_data_received( drag_dest, dest, selection_data );
988 }
989
990 static gboolean
wxgtk_tree_model_row_drop_possible(GtkTreeDragDest * drag_dest,GtkTreePath * dest_path,GtkSelectionData * selection_data)991 wxgtk_tree_model_row_drop_possible (GtkTreeDragDest *drag_dest,
992 GtkTreePath *dest_path,
993 GtkSelectionData *selection_data)
994 {
995 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) drag_dest;
996 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
997
998 if ( wxtree_model->stamp == 0 )
999 return FALSE;
1000
1001 return wxtree_model->internal->row_drop_possible( drag_dest, dest_path, selection_data );
1002 }
1003
1004 /* sortable iface */
1005 static gboolean
wxgtk_tree_model_get_sort_column_id(GtkTreeSortable * sortable,gint * sort_column_id,GtkSortType * order)1006 wxgtk_tree_model_get_sort_column_id (GtkTreeSortable *sortable,
1007 gint *sort_column_id,
1008 GtkSortType *order)
1009 {
1010 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) sortable;
1011
1012 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (sortable), FALSE);
1013
1014 if ( wxtree_model->stamp == 0 )
1015 return FALSE;
1016
1017 if (!wxtree_model->internal->IsSorted())
1018 {
1019 if (sort_column_id)
1020 *sort_column_id = -1;
1021
1022 return TRUE;
1023 }
1024
1025
1026 if (sort_column_id)
1027 *sort_column_id = wxtree_model->internal->GetSortColumn();
1028
1029 if (order)
1030 *order = wxtree_model->internal->GetSortOrder();
1031
1032 return TRUE;
1033 }
1034
1035 static
1036 wxDataViewColumn *gs_lastLeftClickHeader = NULL;
1037
1038 static void
wxgtk_tree_model_set_sort_column_id(GtkTreeSortable * sortable,gint sort_column_id,GtkSortType order)1039 wxgtk_tree_model_set_sort_column_id (GtkTreeSortable *sortable,
1040 gint sort_column_id,
1041 GtkSortType order)
1042 {
1043 GtkWxTreeModel *tree_model = (GtkWxTreeModel *) sortable;
1044 g_return_if_fail (GTK_IS_WX_TREE_MODEL (sortable) );
1045
1046 if ( tree_model->stamp == 0 )
1047 return;
1048
1049 tree_model->internal->SetDataViewSortColumn( gs_lastLeftClickHeader );
1050
1051 if ((sort_column_id != (gint) tree_model->internal->GetSortColumn()) ||
1052 (order != tree_model->internal->GetSortOrder()))
1053 {
1054 tree_model->internal->SetSortColumn( sort_column_id );
1055 tree_model->internal->SetSortOrder( order );
1056
1057 gtk_tree_sortable_sort_column_changed (sortable);
1058
1059 tree_model->internal->GetDataViewModel()->Resort();
1060 }
1061
1062 if (gs_lastLeftClickHeader)
1063 {
1064 wxDataViewCtrl *dv = tree_model->internal->GetOwner();
1065 wxDataViewEvent
1066 event(wxEVT_DATAVIEW_COLUMN_SORTED, dv, gs_lastLeftClickHeader);
1067 dv->HandleWindowEvent( event );
1068 }
1069
1070 gs_lastLeftClickHeader = NULL;
1071 }
1072
1073 static void
wxgtk_tree_model_set_sort_func(GtkTreeSortable * sortable,gint WXUNUSED (sort_column_id),GtkTreeIterCompareFunc func,gpointer WXUNUSED (data),GDestroyNotify WXUNUSED (destroy))1074 wxgtk_tree_model_set_sort_func (GtkTreeSortable *sortable,
1075 gint WXUNUSED(sort_column_id),
1076 GtkTreeIterCompareFunc func,
1077 gpointer WXUNUSED(data),
1078 GDestroyNotify WXUNUSED(destroy))
1079 {
1080 g_return_if_fail (GTK_IS_WX_TREE_MODEL (sortable) );
1081 g_return_if_fail (func != NULL);
1082 }
1083
1084 static void
wxgtk_tree_model_set_default_sort_func(GtkTreeSortable * sortable,GtkTreeIterCompareFunc func,gpointer WXUNUSED (data),GDestroyNotify WXUNUSED (destroy))1085 wxgtk_tree_model_set_default_sort_func (GtkTreeSortable *sortable,
1086 GtkTreeIterCompareFunc func,
1087 gpointer WXUNUSED(data),
1088 GDestroyNotify WXUNUSED(destroy))
1089 {
1090 g_return_if_fail (GTK_IS_WX_TREE_MODEL (sortable) );
1091 g_return_if_fail (func != NULL);
1092
1093 //wxPrintf( "wxgtk_tree_model_set_default_sort_func\n" );
1094 // TODO: remove this code
1095 }
1096
1097 static gboolean
wxgtk_tree_model_has_default_sort_func(GtkTreeSortable * sortable)1098 wxgtk_tree_model_has_default_sort_func (GtkTreeSortable *sortable)
1099 {
1100 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (sortable), FALSE );
1101
1102 return FALSE;
1103 }
1104
1105 //-----------------------------------------------------------------------------
1106 // define new GTK+ class GtkWxRendererText
1107 //-----------------------------------------------------------------------------
1108
1109 extern "C" {
1110
1111 #define GTK_TYPE_WX_CELL_RENDERER_TEXT (gtk_wx_cell_renderer_text_get_type ())
1112 #define GTK_WX_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_WX_CELL_RENDERER_TEXT, GtkWxCellRendererText))
1113 #define GTK_IS_WX_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_WX_CELL_RENDERER_TEXT))
1114 #define GTK_IS_WX_CELL_RENDERER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_WX_CELL_RENDERER_TEXT))
1115
1116 typedef struct _GtkWxCellRendererText GtkWxCellRendererText;
1117
1118 struct _GtkWxCellRendererText
1119 {
1120 GtkCellRendererText parent;
1121
1122 wxDataViewRenderer *wx_renderer;
1123 };
1124
1125 static GtkWxCellRendererText *gtk_wx_cell_renderer_text_new (void);
1126 static void gtk_wx_cell_renderer_text_init (
1127 GTypeInstance* instance, void*);
1128 static void gtk_wx_cell_renderer_text_class_init(
1129 void* klass, void*);
1130 static GtkCellEditable *gtk_wx_cell_renderer_text_start_editing(
1131 GtkCellRenderer *cell,
1132 GdkEvent *event,
1133 GtkWidget *widget,
1134 const gchar *path,
1135 wxConstGdkRect *background_area,
1136 wxConstGdkRect *cell_area,
1137 GtkCellRendererState flags );
1138
1139
1140 static GObjectClass *text_cell_parent_class = NULL;
1141
1142 } // extern "C"
1143
1144 static GType
gtk_wx_cell_renderer_text_get_type(void)1145 gtk_wx_cell_renderer_text_get_type (void)
1146 {
1147 static GType cell_wx_type = 0;
1148
1149 if (!cell_wx_type)
1150 {
1151 const GTypeInfo cell_wx_info =
1152 {
1153 sizeof (GtkCellRendererTextClass),
1154 NULL, /* base_init */
1155 NULL, /* base_finalize */
1156 gtk_wx_cell_renderer_text_class_init,
1157 NULL, /* class_finalize */
1158 NULL, /* class_data */
1159 sizeof (GtkWxCellRendererText),
1160 0, /* n_preallocs */
1161 gtk_wx_cell_renderer_text_init,
1162 NULL
1163 };
1164
1165 cell_wx_type = g_type_register_static( GTK_TYPE_CELL_RENDERER_TEXT,
1166 "GtkWxCellRendererText", &cell_wx_info, (GTypeFlags)0 );
1167 }
1168
1169 return cell_wx_type;
1170 }
1171
1172 static void
gtk_wx_cell_renderer_text_init(GTypeInstance * instance,void *)1173 gtk_wx_cell_renderer_text_init(GTypeInstance* instance, void*)
1174 {
1175 GtkWxCellRendererText* cell = GTK_WX_CELL_RENDERER_TEXT(instance);
1176 cell->wx_renderer = NULL;
1177 }
1178
1179 static void
gtk_wx_cell_renderer_text_class_init(void * klass,void *)1180 gtk_wx_cell_renderer_text_class_init(void* klass, void*)
1181 {
1182 GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
1183
1184 text_cell_parent_class = (GObjectClass*) g_type_class_peek_parent (klass);
1185
1186 cell_class->start_editing = gtk_wx_cell_renderer_text_start_editing;
1187 }
1188
1189 static GtkWxCellRendererText*
gtk_wx_cell_renderer_text_new(void)1190 gtk_wx_cell_renderer_text_new (void)
1191 {
1192 return (GtkWxCellRendererText*) g_object_new (GTK_TYPE_WX_CELL_RENDERER_TEXT, NULL);
1193 }
1194
gtk_wx_cell_renderer_text_start_editing(GtkCellRenderer * gtk_renderer,GdkEvent * gdk_event,GtkWidget * widget,const gchar * path,wxConstGdkRect * background_area,wxConstGdkRect * cell_area,GtkCellRendererState flags)1195 static GtkCellEditable *gtk_wx_cell_renderer_text_start_editing(
1196 GtkCellRenderer *gtk_renderer,
1197 GdkEvent *gdk_event,
1198 GtkWidget *widget,
1199 const gchar *path,
1200 wxConstGdkRect *background_area,
1201 wxConstGdkRect *cell_area,
1202 GtkCellRendererState flags )
1203 {
1204 GtkWxCellRendererText *wxgtk_renderer = (GtkWxCellRendererText *) gtk_renderer;
1205 wxDataViewRenderer *wx_renderer = wxgtk_renderer->wx_renderer;
1206 wxDataViewColumn *column = wx_renderer->GetOwner();
1207
1208 wxDataViewItem
1209 item(column->GetOwner()->GTKPathToItem(wxGtkTreePath(path)));
1210
1211 wxDataViewCtrl *dv = column->GetOwner();
1212 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_START_EDITING, dv, column, item);
1213 dv->HandleWindowEvent( event );
1214
1215 if (event.IsAllowed())
1216 return GTK_CELL_RENDERER_CLASS(text_cell_parent_class)->
1217 start_editing( gtk_renderer, gdk_event, widget, path, background_area, cell_area, flags );
1218 else
1219 return NULL;
1220 }
1221
1222 // ----------------------------------------------------------------------------
1223 // GTK+ class GtkWxCellEditorBin: needed only to implement GtkCellEditable for
1224 // our editor widgets which don't necessarily implement it on their own.
1225 // ----------------------------------------------------------------------------
1226
1227 enum
1228 {
1229 CELL_EDITOR_BIN_PROP_0,
1230 CELL_EDITOR_BIN_PROP_EDITING_CANCELED
1231 };
1232
1233 extern "C" {
1234
1235 #define GTK_TYPE_WX_CELL_EDITOR_BIN (gtk_wx_cell_editor_bin_get_type ())
1236
1237 // In GTK+ < 3.8 GtkBin can't be used as widget base type without defining our
1238 // own size_allocate and related (either size_request for GTK+ 2 or
1239 // get_preferred_height for GTK+ 3) vfuncs, so we use GtkHBox instead. But in
1240 // GTK+ 4, GtkHBox is removed, so we do use GtkBin with it.
1241 #ifdef __WXGTK4__
1242 typedef GtkBin GtkWxCellEditorBinBase;
1243 typedef GtkBinClass GtkWxCellEditorBinBaseClass;
1244
1245 // Notice that this can't be just a (const) variable as GTK+ type constants
1246 // are actually macros expanding into function calls, which shouldn't be
1247 // performed before the library is initialized, so we need to use either an
1248 // inline function or a define, which is simpler.
1249 #define GtkWxCellEditorBinBaseType GTK_TYPE_BIN
1250 #else // GTK+ < 4
1251 // GtkHBox is deprecated since 3.2, so avoid warnings about using it.
1252 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
1253
1254 typedef GtkHBox GtkWxCellEditorBinBase;
1255 typedef GtkHBoxClass GtkWxCellEditorBinBaseClass;
1256 #define GtkWxCellEditorBinBaseType GTK_TYPE_HBOX
1257
1258 wxGCC_WARNING_RESTORE(deprecated-declarations)
1259 #endif // GTK+ version
1260
1261 struct GtkWxCellEditorBin
1262 {
1263 GtkWxCellEditorBinBase parent;
1264
1265 // The actual user-created editor.
1266 wxWindow* editor;
1267 };
1268
1269 static GtkWidget* gtk_wx_cell_editor_bin_new(wxWindow* editor);
1270 static void gtk_wx_cell_editor_bin_class_init(void* klass, void*);
1271 static void gtk_wx_cell_editor_bin_get_property(GObject*, guint, GValue*, GParamSpec*);
1272 static void gtk_wx_cell_editor_bin_set_property(GObject*, guint, const GValue*, GParamSpec*);
1273
1274 static void gtk_wx_cell_editor_bin_cell_editable_init(void* g_iface, void*);
1275 static void gtk_wx_cell_editor_bin_cell_editable_start_editing(
1276 GtkCellEditable *cell_editable,
1277 GdkEvent *event);
1278
1279 }
1280
1281 static GType
gtk_wx_cell_editor_bin_get_type()1282 gtk_wx_cell_editor_bin_get_type()
1283 {
1284 static GType cell_editor_bin_type = 0;
1285
1286 if ( !cell_editor_bin_type )
1287 {
1288 const GTypeInfo cell_editor_bin_info =
1289 {
1290 sizeof (GtkWxCellEditorBinBaseClass),
1291 NULL, /* base_init */
1292 NULL, /* base_finalize */
1293 gtk_wx_cell_editor_bin_class_init,
1294 NULL, /* class_finalize */
1295 NULL, /* class_data */
1296 sizeof (GtkWxCellEditorBin),
1297 0, /* n_preallocs */
1298 NULL, // init
1299 NULL
1300 };
1301
1302 cell_editor_bin_type = g_type_register_static(
1303 GtkWxCellEditorBinBaseType,
1304 "GtkWxCellEditorBin", &cell_editor_bin_info, (GTypeFlags)0 );
1305
1306
1307 static const GInterfaceInfo cell_editable_iface_info =
1308 {
1309 gtk_wx_cell_editor_bin_cell_editable_init,
1310 NULL,
1311 NULL
1312 };
1313
1314 g_type_add_interface_static (cell_editor_bin_type,
1315 GTK_TYPE_CELL_EDITABLE,
1316 &cell_editable_iface_info);
1317 }
1318
1319 return cell_editor_bin_type;
1320 }
1321
gtk_wx_cell_editor_bin_class_init(void * klass,void *)1322 static void gtk_wx_cell_editor_bin_class_init(void* klass, void*)
1323 {
1324 GObjectClass* const oclass = G_OBJECT_CLASS(klass);
1325
1326 oclass->set_property = gtk_wx_cell_editor_bin_set_property;
1327 oclass->get_property = gtk_wx_cell_editor_bin_get_property;
1328
1329 if (wx_is_at_least_gtk2(20))
1330 {
1331 g_object_class_override_property(oclass,
1332 CELL_EDITOR_BIN_PROP_EDITING_CANCELED,
1333 "editing-canceled");
1334 }
1335 }
1336
1337 // We need to provide these virtual methods as we must support the
1338 // editing-canceled property, but they don't seem to be actually ever called,
1339 // so it's not clear if we must implement them and how. For now just do
1340 // nothing.
1341
1342 static void
gtk_wx_cell_editor_bin_get_property(GObject *,guint,GValue *,GParamSpec *)1343 gtk_wx_cell_editor_bin_get_property(GObject*, guint, GValue*, GParamSpec*)
1344 {
1345 }
1346
1347 static void
gtk_wx_cell_editor_bin_set_property(GObject *,guint,const GValue *,GParamSpec *)1348 gtk_wx_cell_editor_bin_set_property(GObject*, guint, const GValue*, GParamSpec*)
1349 {
1350 }
1351
1352 static GtkWidget*
gtk_wx_cell_editor_bin_new(wxWindow * editor)1353 gtk_wx_cell_editor_bin_new(wxWindow* editor)
1354 {
1355 if ( !editor )
1356 return NULL;
1357
1358 GtkWxCellEditorBin* const
1359 bin = (GtkWxCellEditorBin*)g_object_new (GTK_TYPE_WX_CELL_EDITOR_BIN, NULL);
1360
1361 bin->editor = editor;
1362 gtk_container_add(GTK_CONTAINER(bin), editor->m_widget);
1363
1364 return GTK_WIDGET(bin);
1365 }
1366
1367 // GtkCellEditable interface implementation for GtkWxCellEditorBin
1368
1369 static void
gtk_wx_cell_editor_bin_cell_editable_init(void * g_iface,void *)1370 gtk_wx_cell_editor_bin_cell_editable_init(void* g_iface, void*)
1371 {
1372 GtkCellEditableIface* iface = static_cast<GtkCellEditableIface*>(g_iface);
1373 iface->start_editing = gtk_wx_cell_editor_bin_cell_editable_start_editing;
1374 }
1375
1376 static void
gtk_wx_cell_editor_bin_cell_editable_start_editing(GtkCellEditable * cell_editable,GdkEvent * event)1377 gtk_wx_cell_editor_bin_cell_editable_start_editing(GtkCellEditable *cell_editable,
1378 GdkEvent *event)
1379 {
1380 GtkWxCellEditorBin* const bin = (GtkWxCellEditorBin *)cell_editable;
1381
1382 // If we have an editable widget inside the editor, forward to it.
1383 for ( wxWindow* win = bin->editor; win; )
1384 {
1385 GtkWidget* const widget = win->m_widget;
1386 if ( GTK_IS_CELL_EDITABLE(widget) )
1387 {
1388 gtk_cell_editable_start_editing(GTK_CELL_EDITABLE(widget), event);
1389 break;
1390 }
1391
1392 if ( win == bin->editor )
1393 win = win->GetChildren().front();
1394 else
1395 win = win->GetNextSibling();
1396 }
1397 }
1398
1399 //-----------------------------------------------------------------------------
1400 // define new GTK+ class GtkWxCellRenderer
1401 //-----------------------------------------------------------------------------
1402
1403 extern "C" {
1404
1405 #define GTK_TYPE_WX_CELL_RENDERER (gtk_wx_cell_renderer_get_type ())
1406 #define GTK_WX_CELL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_WX_CELL_RENDERER, GtkWxCellRenderer))
1407 #define GTK_IS_WX_CELL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_WX_CELL_RENDERER))
1408 #define GTK_IS_WX_CELL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_WX_CELL_RENDERER))
1409
1410 typedef struct _GtkWxCellRenderer GtkWxCellRenderer;
1411
1412 struct _GtkWxCellRenderer
1413 {
1414 GtkCellRenderer parent;
1415
1416 /*< private >*/
1417 wxDataViewCustomRenderer *cell;
1418
1419 // Non null only while editing.
1420 GtkWidget* editor_bin;
1421 };
1422
1423 static GtkCellRenderer *gtk_wx_cell_renderer_new (void);
1424 static void gtk_wx_cell_renderer_init (
1425 GTypeInstance* instance, void*);
1426 static void gtk_wx_cell_renderer_class_init(
1427 void* klass, void*);
1428 static void gtk_wx_cell_renderer_get_size (
1429 GtkCellRenderer *cell,
1430 GtkWidget *widget,
1431 wxConstGdkRect *rectangle,
1432 gint *x_offset,
1433 gint *y_offset,
1434 gint *width,
1435 gint *height );
1436 static void gtk_wx_cell_renderer_render (
1437 GtkCellRenderer *cell,
1438 #ifdef __WXGTK3__
1439 cairo_t* cr,
1440 #else
1441 GdkWindow *window,
1442 #endif
1443 GtkWidget *widget,
1444 wxConstGdkRect *background_area,
1445 wxConstGdkRect *cell_area,
1446 #ifndef __WXGTK3__
1447 GdkRectangle *expose_area,
1448 #endif
1449 GtkCellRendererState flags );
1450 static gboolean gtk_wx_cell_renderer_activate(
1451 GtkCellRenderer *cell,
1452 GdkEvent *event,
1453 GtkWidget *widget,
1454 const gchar *path,
1455 wxConstGdkRect *background_area,
1456 wxConstGdkRect *cell_area,
1457 GtkCellRendererState flags );
1458 static GtkCellEditable *gtk_wx_cell_renderer_start_editing(
1459 GtkCellRenderer *cell,
1460 GdkEvent *event,
1461 GtkWidget *widget,
1462 const gchar *path,
1463 wxConstGdkRect *background_area,
1464 wxConstGdkRect *cell_area,
1465 GtkCellRendererState flags );
1466
1467 } // extern "C"
1468
1469 static GType
gtk_wx_cell_renderer_get_type(void)1470 gtk_wx_cell_renderer_get_type (void)
1471 {
1472 static GType cell_wx_type = 0;
1473
1474 if (!cell_wx_type)
1475 {
1476 const GTypeInfo cell_wx_info =
1477 {
1478 sizeof (GtkCellRendererClass),
1479 NULL, /* base_init */
1480 NULL, /* base_finalize */
1481 gtk_wx_cell_renderer_class_init,
1482 NULL, /* class_finalize */
1483 NULL, /* class_data */
1484 sizeof (GtkWxCellRenderer),
1485 0, /* n_preallocs */
1486 gtk_wx_cell_renderer_init,
1487 NULL
1488 };
1489
1490 cell_wx_type = g_type_register_static( GTK_TYPE_CELL_RENDERER,
1491 "GtkWxCellRenderer", &cell_wx_info, (GTypeFlags)0 );
1492 }
1493
1494 return cell_wx_type;
1495 }
1496
1497 static void
gtk_wx_cell_renderer_init(GTypeInstance * instance,void *)1498 gtk_wx_cell_renderer_init(GTypeInstance* instance, void*)
1499 {
1500 GtkWxCellRenderer* cell = GTK_WX_CELL_RENDERER(instance);
1501 cell->cell = NULL;
1502 cell->editor_bin = NULL;
1503 }
1504
1505 static void
gtk_wx_cell_renderer_class_init(void * klass,void *)1506 gtk_wx_cell_renderer_class_init(void* klass, void*)
1507 {
1508 GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
1509
1510 cell_class->get_size = gtk_wx_cell_renderer_get_size;
1511 cell_class->render = gtk_wx_cell_renderer_render;
1512 cell_class->activate = gtk_wx_cell_renderer_activate;
1513 cell_class->start_editing = gtk_wx_cell_renderer_start_editing;
1514 }
1515
1516 static GtkCellRenderer*
gtk_wx_cell_renderer_new(void)1517 gtk_wx_cell_renderer_new (void)
1518 {
1519 return (GtkCellRenderer*) g_object_new (GTK_TYPE_WX_CELL_RENDERER, NULL);
1520 }
1521
gtk_wx_cell_renderer_start_editing(GtkCellRenderer * renderer,GdkEvent * WXUNUSED (event),GtkWidget * WXUNUSED (widget),const gchar * path,wxConstGdkRect * WXUNUSED (background_area),wxConstGdkRect * cell_area,GtkCellRendererState WXUNUSED (flags))1522 static GtkCellEditable *gtk_wx_cell_renderer_start_editing(
1523 GtkCellRenderer *renderer,
1524 GdkEvent *WXUNUSED(event),
1525 GtkWidget *WXUNUSED(widget),
1526 const gchar *path,
1527 wxConstGdkRect *WXUNUSED(background_area),
1528 wxConstGdkRect *cell_area,
1529 GtkCellRendererState WXUNUSED(flags) )
1530 {
1531 GtkWxCellRenderer *wxrenderer = (GtkWxCellRenderer *) renderer;
1532 wxDataViewCustomRenderer *cell = wxrenderer->cell;
1533
1534 // Renderer doesn't support in-place editing
1535 if (!cell->HasEditorCtrl())
1536 return NULL;
1537
1538 // An in-place editing control is still around
1539 if (cell->GetEditorCtrl())
1540 return NULL;
1541
1542 wxDataViewItem
1543 item(cell->GetOwner()->GetOwner()->GTKPathToItem(wxGtkTreePath(path)));
1544
1545 if (!cell->StartEditing(item, wxRectFromGDKRect(cell_area)))
1546 return NULL;
1547
1548 wxrenderer->editor_bin = gtk_wx_cell_editor_bin_new(cell->GetEditorCtrl());
1549 gtk_widget_show(wxrenderer->editor_bin);
1550
1551 return GTK_CELL_EDITABLE(wxrenderer->editor_bin);
1552 }
1553
1554 static void
gtk_wx_cell_renderer_get_size(GtkCellRenderer * renderer,GtkWidget * WXUNUSED (widget),wxConstGdkRect * cell_area,gint * x_offset,gint * y_offset,gint * width,gint * height)1555 gtk_wx_cell_renderer_get_size (GtkCellRenderer *renderer,
1556 GtkWidget *WXUNUSED(widget),
1557 wxConstGdkRect *cell_area,
1558 gint *x_offset,
1559 gint *y_offset,
1560 gint *width,
1561 gint *height)
1562 {
1563 GtkWxCellRenderer *wxrenderer = (GtkWxCellRenderer *) renderer;
1564 wxDataViewCustomRenderer *cell = wxrenderer->cell;
1565
1566 wxSize size = cell->GetSize();
1567
1568 wxDataViewCtrl * const ctrl = cell->GetOwner()->GetOwner();
1569
1570 // Uniform row height, if specified, overrides the value returned by the
1571 // renderer.
1572 if ( !ctrl->HasFlag(wxDV_VARIABLE_LINE_HEIGHT) )
1573 {
1574 const int uniformHeight = ctrl->GTKGetUniformRowHeight();
1575 if ( uniformHeight > 0 )
1576 size.y = uniformHeight;
1577 }
1578
1579 int xpad, ypad;
1580 gtk_cell_renderer_get_padding(renderer, &xpad, &ypad);
1581 int calc_width = xpad * 2 + size.x;
1582 int calc_height = ypad * 2 + size.y;
1583
1584 if (x_offset)
1585 *x_offset = 0;
1586 if (y_offset)
1587 *y_offset = 0;
1588
1589 if (cell_area && size.x > 0 && size.y > 0)
1590 {
1591 float xalign, yalign;
1592 gtk_cell_renderer_get_alignment(renderer, &xalign, &yalign);
1593 if (x_offset)
1594 {
1595 *x_offset = int(xalign * (cell_area->width - calc_width - 2 * xpad));
1596 *x_offset = MAX(*x_offset, 0) + xpad;
1597 }
1598 if (y_offset)
1599 {
1600 *y_offset = int(yalign * (cell_area->height - calc_height - 2 * ypad));
1601 *y_offset = MAX(*y_offset, 0) + ypad;
1602 }
1603 }
1604
1605 if (width)
1606 *width = calc_width;
1607
1608 if (height)
1609 *height = calc_height;
1610 }
1611
1612 struct wxDataViewCustomRenderer::GTKRenderParams
1613 {
1614 #ifdef __WXGTK3__
1615 cairo_t* cr;
1616 #else
1617 GdkWindow* window;
1618 GdkRectangle* expose_area;
1619 #endif
1620 GtkWidget* widget;
1621 wxConstGdkRect* background_area;
1622 int flags;
1623 };
1624
1625 static void
gtk_wx_cell_renderer_render(GtkCellRenderer * renderer,cairo_t * cr,GtkWidget * widget,wxConstGdkRect * background_area,wxConstGdkRect * cell_area,GdkRectangle * expose_area,GtkCellRendererState flags)1626 gtk_wx_cell_renderer_render (GtkCellRenderer *renderer,
1627 #ifdef __WXGTK3__
1628 cairo_t* cr,
1629 #else
1630 GdkWindow *window,
1631 #endif
1632 GtkWidget *widget,
1633 wxConstGdkRect *background_area,
1634 wxConstGdkRect *cell_area,
1635 #ifndef __WXGTK3__
1636 GdkRectangle *expose_area,
1637 #endif
1638 GtkCellRendererState flags)
1639
1640 {
1641 GtkWxCellRenderer *wxrenderer = (GtkWxCellRenderer *) renderer;
1642 wxDataViewCustomRenderer *cell = wxrenderer->cell;
1643
1644 wxDataViewCustomRenderer::GTKRenderParams renderParams;
1645 #ifdef __WXGTK3__
1646 renderParams.cr = cr;
1647 #else
1648 renderParams.window = window;
1649 renderParams.expose_area = expose_area;
1650 #endif
1651 renderParams.widget = widget;
1652 renderParams.background_area = background_area;
1653 renderParams.flags = flags;
1654 cell->GTKSetRenderParams(&renderParams);
1655
1656 wxRect rect(wxRectFromGDKRect(cell_area));
1657 int xpad, ypad;
1658 gtk_cell_renderer_get_padding(renderer, &xpad, &ypad);
1659 rect = rect.Deflate(xpad, ypad);
1660
1661 wxDC* dc = cell->GetDC();
1662 #ifdef __WXGTK3__
1663 wxGraphicsContext* context = dc->GetGraphicsContext();
1664 void* nativeContext = NULL;
1665 if (context)
1666 nativeContext = context->GetNativeContext();
1667 if (cr != nativeContext)
1668 dc->SetGraphicsContext(wxGraphicsContext::CreateFromNative(cr));
1669 #else
1670 wxWindowDCImpl *impl = (wxWindowDCImpl *) dc->GetImpl();
1671
1672 // Reinitialize wxWindowDC's GDK window if drawing occurs into a different
1673 // window such as a DnD drop window.
1674 if (window != impl->m_gdkwindow)
1675 {
1676 impl->Destroy();
1677 impl->m_gdkwindow = window;
1678 impl->SetUpDC();
1679 }
1680 #endif
1681
1682 int state = 0;
1683 if (flags & GTK_CELL_RENDERER_SELECTED)
1684 state |= wxDATAVIEW_CELL_SELECTED;
1685 if (flags & GTK_CELL_RENDERER_PRELIT)
1686 state |= wxDATAVIEW_CELL_PRELIT;
1687 if (flags & GTK_CELL_RENDERER_INSENSITIVE)
1688 state |= wxDATAVIEW_CELL_INSENSITIVE;
1689 if (flags & GTK_CELL_RENDERER_FOCUSED)
1690 state |= wxDATAVIEW_CELL_FOCUSED;
1691 cell->WXCallRender( rect, dc, state );
1692
1693 cell->GTKSetRenderParams(NULL);
1694 #ifdef __WXGTK3__
1695 dc->SetGraphicsContext(NULL);
1696 #endif
1697 }
1698
1699 static gboolean
gtk_wx_cell_renderer_activate(GtkCellRenderer * renderer,GdkEvent * event,GtkWidget * widget,const gchar * path,wxConstGdkRect * WXUNUSED (background_area),wxConstGdkRect * cell_area,GtkCellRendererState WXUNUSED (flags))1700 gtk_wx_cell_renderer_activate(
1701 GtkCellRenderer *renderer,
1702 GdkEvent *event,
1703 GtkWidget *widget,
1704 const gchar *path,
1705 wxConstGdkRect *WXUNUSED(background_area),
1706 wxConstGdkRect *cell_area,
1707 GtkCellRendererState WXUNUSED(flags) )
1708 {
1709 GtkWxCellRenderer *wxrenderer = (GtkWxCellRenderer *) renderer;
1710 wxDataViewCustomRenderer *cell = wxrenderer->cell;
1711
1712 GdkRectangle rect;
1713 gtk_wx_cell_renderer_get_size (renderer, widget, cell_area,
1714 &rect.x,
1715 &rect.y,
1716 &rect.width,
1717 &rect.height);
1718
1719 rect.x += cell_area->x;
1720 rect.y += cell_area->y;
1721 int xpad, ypad;
1722 gtk_cell_renderer_get_padding(renderer, &xpad, &ypad);
1723 rect.width -= xpad * 2;
1724 rect.height -= ypad * 2;
1725
1726 wxRect renderrect(wxRectFromGDKRect(&rect));
1727
1728 wxDataViewCtrl * const ctrl = cell->GetOwner()->GetOwner();
1729 wxDataViewModel *model = ctrl->GetModel();
1730
1731 wxDataViewItem item(ctrl->GTKPathToItem(wxGtkTreePath(path)));
1732
1733 unsigned int model_col = cell->GetOwner()->GetModelColumn();
1734
1735 if ( !event )
1736 {
1737 // activated by <ENTER>
1738 return cell->ActivateCell(renderrect, model, item, model_col, NULL);
1739 }
1740 else if ( event->type == GDK_BUTTON_PRESS )
1741 {
1742 GdkEventButton *button_event = (GdkEventButton*)event;
1743 if ( button_event->button == 1 )
1744 {
1745 wxMouseEvent mouse_event(wxEVT_LEFT_DOWN);
1746 InitMouseEvent(ctrl, mouse_event, button_event);
1747
1748 mouse_event.m_x -= renderrect.x;
1749 mouse_event.m_y -= renderrect.y;
1750
1751 return cell->ActivateCell(renderrect, model, item, model_col, &mouse_event);
1752 }
1753 }
1754
1755 wxLogDebug("unexpected event type in gtk_wx_cell_renderer_activate()");
1756 return false;
1757 }
1758
1759 // ---------------------------------------------------------
1760 // wxGtkDataViewModelNotifier
1761 // ---------------------------------------------------------
1762
1763 class wxGtkDataViewModelNotifier: public wxDataViewModelNotifier
1764 {
1765 public:
1766 wxGtkDataViewModelNotifier( wxDataViewModel *wx_model, wxDataViewCtrlInternal *internal );
1767 ~wxGtkDataViewModelNotifier();
1768
1769 virtual bool ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item ) wxOVERRIDE;
1770 virtual bool ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item ) wxOVERRIDE;
1771 virtual bool ItemChanged( const wxDataViewItem &item ) wxOVERRIDE;
1772 virtual bool ValueChanged( const wxDataViewItem &item, unsigned int model_column ) wxOVERRIDE;
1773 virtual bool Cleared() wxOVERRIDE;
1774 virtual void Resort() wxOVERRIDE;
1775 virtual bool BeforeReset() wxOVERRIDE;
1776 virtual bool AfterReset() wxOVERRIDE;
1777
1778 void UpdateLastCount();
1779
1780 private:
1781 wxDataViewModel *m_wx_model;
1782 wxDataViewCtrlInternal *m_internal;
1783 };
1784
1785 // ---------------------------------------------------------
1786 // wxGtkDataViewListModelNotifier
1787 // ---------------------------------------------------------
1788
wxGtkDataViewModelNotifier(wxDataViewModel * wx_model,wxDataViewCtrlInternal * internal)1789 wxGtkDataViewModelNotifier::wxGtkDataViewModelNotifier(
1790 wxDataViewModel *wx_model, wxDataViewCtrlInternal *internal )
1791 {
1792 m_wx_model = wx_model;
1793 m_internal = internal;
1794 }
1795
~wxGtkDataViewModelNotifier()1796 wxGtkDataViewModelNotifier::~wxGtkDataViewModelNotifier()
1797 {
1798 m_wx_model = NULL;
1799 m_internal = NULL;
1800 }
1801
ItemAdded(const wxDataViewItem & parent,const wxDataViewItem & item)1802 bool wxGtkDataViewModelNotifier::ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item )
1803 {
1804 m_internal->ItemAdded( parent, item );
1805 GtkWxTreeModel *wxgtk_model = m_internal->GetGtkModel();
1806
1807 GtkTreeIter iter;
1808 iter.stamp = wxgtk_model->stamp;
1809 iter.user_data = item.GetID();
1810
1811 wxGtkTreePath path(wxgtk_tree_model_get_path(
1812 GTK_TREE_MODEL(wxgtk_model), &iter ));
1813 gtk_tree_model_row_inserted(
1814 GTK_TREE_MODEL(wxgtk_model), path, &iter);
1815
1816 return true;
1817 }
1818
ItemDeleted(const wxDataViewItem & parent,const wxDataViewItem & item)1819 bool wxGtkDataViewModelNotifier::ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item )
1820 {
1821 GtkWxTreeModel *wxgtk_model = m_internal->GetGtkModel();
1822 #if 0
1823 // using _get_path for a deleted item cannot be
1824 // a good idea
1825 GtkTreeIter iter;
1826 iter.stamp = wxgtk_model->stamp;
1827 iter.user_data = (gpointer) item.GetID();
1828 wxGtkTreePath path(wxgtk_tree_model_get_path(
1829 GTK_TREE_MODEL(wxgtk_model), &iter ));
1830 #else
1831 // so get the path from the parent
1832 GtkTreeIter parentIter;
1833 parentIter.stamp = wxgtk_model->stamp;
1834 parentIter.user_data = (gpointer) parent.GetID();
1835 wxGtkTreePath parentPath(wxgtk_tree_model_get_path(
1836 GTK_TREE_MODEL(wxgtk_model), &parentIter ));
1837
1838 // and add the final index ourselves
1839 wxGtkTreePath path(gtk_tree_path_copy(parentPath));
1840 int index = m_internal->GetIndexOf( parent, item );
1841 gtk_tree_path_append_index( path, index );
1842 #endif
1843
1844 m_internal->ItemDeleted( parent, item );
1845
1846 gtk_tree_model_row_deleted(
1847 GTK_TREE_MODEL(wxgtk_model), path );
1848
1849 // Did we remove the last child, causing 'parent' to become a leaf?
1850 if ( !m_wx_model->IsContainer(parent) )
1851 {
1852 gtk_tree_model_row_has_child_toggled
1853 (
1854 GTK_TREE_MODEL(wxgtk_model),
1855 parentPath,
1856 &parentIter
1857 );
1858 }
1859
1860 return true;
1861 }
1862
Resort()1863 void wxGtkDataViewModelNotifier::Resort()
1864 {
1865 m_internal->Resort();
1866 }
1867
ItemChanged(const wxDataViewItem & item)1868 bool wxGtkDataViewModelNotifier::ItemChanged( const wxDataViewItem &item )
1869 {
1870 GtkWxTreeModel *wxgtk_model = m_internal->GetGtkModel();
1871
1872 GtkTreeIter iter;
1873 iter.stamp = wxgtk_model->stamp;
1874 iter.user_data = (gpointer) item.GetID();
1875
1876 wxGtkTreePath path(wxgtk_tree_model_get_path(
1877 GTK_TREE_MODEL(wxgtk_model), &iter ));
1878 gtk_tree_model_row_changed(
1879 GTK_TREE_MODEL(wxgtk_model), path, &iter );
1880
1881 m_internal->ItemChanged( item );
1882
1883 return true;
1884 }
1885
ValueChanged(const wxDataViewItem & item,unsigned int model_column)1886 bool wxGtkDataViewModelNotifier::ValueChanged( const wxDataViewItem &item, unsigned int model_column )
1887 {
1888 GtkWxTreeModel *wxgtk_model = m_internal->GetGtkModel();
1889 wxDataViewCtrl *ctrl = m_internal->GetOwner();
1890
1891 // This adds GTK+'s missing MVC logic for ValueChanged
1892 unsigned int index;
1893 for (index = 0; index < ctrl->GetColumnCount(); index++)
1894 {
1895 wxDataViewColumn *column = ctrl->GetColumn( index );
1896 if (column->GetModelColumn() == model_column)
1897 {
1898 GtkTreeView *widget = GTK_TREE_VIEW(ctrl->GtkGetTreeView());
1899 GtkTreeViewColumn *gcolumn = GTK_TREE_VIEW_COLUMN(column->GetGtkHandle());
1900
1901 // Don't attempt to refresh not yet realized tree, it is useless
1902 // and results in GTK errors.
1903 if ( gtk_widget_get_realized(ctrl->GtkGetTreeView()) )
1904 {
1905 // Get cell area
1906 GtkTreeIter iter;
1907 iter.stamp = wxgtk_model->stamp;
1908 iter.user_data = (gpointer) item.GetID();
1909 wxGtkTreePath path(wxgtk_tree_model_get_path(
1910 GTK_TREE_MODEL(wxgtk_model), &iter ));
1911 GdkRectangle cell_area;
1912 gtk_tree_view_get_cell_area( widget, path, gcolumn, &cell_area );
1913
1914 // Don't try to redraw the column if it's invisible, this just
1915 // results in "BUG" messages from pixman_region32_init_rect()
1916 // and would be useful even if it didn't anyhow.
1917 if ( cell_area.width > 0 && cell_area.height > 0 )
1918 {
1919 #ifdef __WXGTK3__
1920 GtkAdjustment* hadjust = gtk_scrollable_get_hadjustment(GTK_SCROLLABLE(widget));
1921 #else
1922 GtkAdjustment* hadjust = gtk_tree_view_get_hadjustment( widget );
1923 #endif
1924 double d = gtk_adjustment_get_value( hadjust );
1925 int xdiff = (int) d;
1926
1927 GtkAllocation a;
1928 gtk_widget_get_allocation(GTK_WIDGET(gtk_tree_view_column_get_button(gcolumn)), &a);
1929 int ydiff = a.height;
1930 // Redraw
1931 gtk_widget_queue_draw_area( GTK_WIDGET(widget),
1932 cell_area.x - xdiff, ydiff + cell_area.y, cell_area.width, cell_area.height );
1933 }
1934 }
1935
1936 m_internal->ValueChanged( item, model_column );
1937
1938 return true;
1939 }
1940 }
1941
1942 return false;
1943 }
1944
BeforeReset()1945 bool wxGtkDataViewModelNotifier::BeforeReset()
1946 {
1947 m_internal->UseModel(false);
1948
1949 return true;
1950 }
1951
AfterReset()1952 bool wxGtkDataViewModelNotifier::AfterReset()
1953 {
1954 m_internal->Cleared();
1955
1956 m_internal->UseModel(true);
1957
1958 return true;
1959 }
1960
Cleared()1961 bool wxGtkDataViewModelNotifier::Cleared()
1962 {
1963 return BeforeReset() && AfterReset();
1964 }
1965
1966 // ---------------------------------------------------------
1967 // wxDataViewRenderer
1968 // ---------------------------------------------------------
1969
1970 static void
wxgtk_cell_editable_editing_done(GtkCellEditable * editable,wxDataViewRenderer * wxrenderer)1971 wxgtk_cell_editable_editing_done( GtkCellEditable *editable,
1972 wxDataViewRenderer *wxrenderer )
1973 {
1974 // "editing-cancelled" property is documented as being new since 2.20 in
1975 // GtkCellEditable, but seems to have existed basically forever (since GTK+
1976 // 1.3 days) in GtkCellRendererText, so try to use it in any case.
1977 if ( g_object_class_find_property(G_OBJECT_GET_CLASS(editable),
1978 "editing-canceled") )
1979 {
1980 gboolean wasCancelled;
1981 g_object_get(editable, "editing-canceled", &wasCancelled, NULL);
1982 if ( wasCancelled )
1983 {
1984 wxrenderer->CancelEditing();
1985 return;
1986 }
1987 }
1988
1989 wxrenderer->FinishEditing();
1990 }
1991
1992 static void
wxgtk_renderer_editing_started(GtkCellRenderer * WXUNUSED (cell),GtkCellEditable * editable,gchar * path,wxDataViewRenderer * wxrenderer)1993 wxgtk_renderer_editing_started( GtkCellRenderer *WXUNUSED(cell), GtkCellEditable *editable,
1994 gchar *path, wxDataViewRenderer *wxrenderer )
1995 {
1996 if (!editable)
1997 return;
1998
1999 wxDataViewColumn *column = wxrenderer->GetOwner();
2000 wxDataViewCtrl *dv = column->GetOwner();
2001 wxDataViewItem item(dv->GTKPathToItem(wxGtkTreePath(path)));
2002 wxrenderer->NotifyEditingStarted(item);
2003
2004 if (GTK_IS_CELL_EDITABLE(editable))
2005 {
2006 g_signal_connect (editable, "editing_done",
2007 G_CALLBACK (wxgtk_cell_editable_editing_done),
2008 (gpointer) wxrenderer );
2009
2010 }
2011 }
2012
2013
2014 wxIMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase);
2015
wxDataViewRenderer(const wxString & varianttype,wxDataViewCellMode mode,int align)2016 wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype, wxDataViewCellMode mode,
2017 int align ) :
2018 wxDataViewRendererBase( varianttype, mode, align )
2019 {
2020 m_renderer = NULL;
2021 m_mode = mode;
2022
2023 // we haven't changed them yet
2024 m_usingDefaultAttrs = true;
2025
2026 // NOTE: SetMode() and SetAlignment() needs to be called in the renderer's ctor,
2027 // after the m_renderer pointer has been initialized
2028 }
2029
FinishEditing()2030 bool wxDataViewRenderer::FinishEditing()
2031 {
2032 wxWindow* editorCtrl = m_editorCtrl;
2033
2034 bool ret = wxDataViewRendererBase::FinishEditing();
2035
2036 if (editorCtrl && wxGetTopLevelParent(editorCtrl)->IsBeingDeleted())
2037 {
2038 // remove editor widget before editor control is deleted,
2039 // to prevent several GTK warnings
2040 gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE(GtkGetEditorWidget()));
2041 // delete editor control now, if it is deferred multiple erroneous
2042 // focus-out events will occur, causing debug warnings
2043 delete editorCtrl;
2044 }
2045 return ret;
2046 }
2047
GtkPackIntoColumn(GtkTreeViewColumn * column)2048 void wxDataViewRenderer::GtkPackIntoColumn(GtkTreeViewColumn *column)
2049 {
2050 gtk_tree_view_column_pack_end( column, m_renderer, TRUE /* expand */);
2051 }
2052
GtkInitHandlers()2053 void wxDataViewRenderer::GtkInitHandlers()
2054 {
2055 {
2056 g_signal_connect (m_renderer, "editing_started",
2057 G_CALLBACK (wxgtk_renderer_editing_started),
2058 this);
2059 }
2060 }
2061
GtkGetEditorWidget() const2062 GtkWidget* wxDataViewRenderer::GtkGetEditorWidget() const
2063 {
2064 return GetEditorCtrl()->m_widget;
2065 }
2066
SetMode(wxDataViewCellMode mode)2067 void wxDataViewRenderer::SetMode( wxDataViewCellMode mode )
2068 {
2069 m_mode = mode;
2070
2071 GtkSetMode(mode);
2072 }
2073
SetEnabled(bool enabled)2074 void wxDataViewRenderer::SetEnabled(bool enabled)
2075 {
2076 // a) this sets the appearance to disabled grey and should only be done for
2077 // the active cells which are disabled, not for the cells which can never
2078 // be edited at all
2079 if ( GetMode() != wxDATAVIEW_CELL_INERT )
2080 {
2081 wxGtkValue gvalue( G_TYPE_BOOLEAN );
2082 g_value_set_boolean( gvalue, enabled );
2083 g_object_set_property( G_OBJECT(m_renderer), "sensitive", gvalue );
2084 }
2085
2086 // b) this actually disables the control/renderer
2087 GtkSetMode(enabled ? GetMode() : wxDATAVIEW_CELL_INERT);
2088 }
2089
GtkSetMode(wxDataViewCellMode mode)2090 void wxDataViewRenderer::GtkSetMode( wxDataViewCellMode mode )
2091 {
2092 GtkCellRendererMode gtkMode;
2093 switch (mode)
2094 {
2095 case wxDATAVIEW_CELL_INERT:
2096 gtkMode = GTK_CELL_RENDERER_MODE_INERT;
2097 break;
2098
2099 case wxDATAVIEW_CELL_ACTIVATABLE:
2100 gtkMode = GTK_CELL_RENDERER_MODE_ACTIVATABLE;
2101 break;
2102
2103 case wxDATAVIEW_CELL_EDITABLE:
2104 gtkMode = GTK_CELL_RENDERER_MODE_EDITABLE;
2105 break;
2106
2107 default:
2108 wxFAIL_MSG( "unknown wxDataViewCellMode value" );
2109 return;
2110 }
2111
2112 wxGtkValue gvalue;
2113 g_value_init( gvalue, gtk_cell_renderer_mode_get_type() );
2114 g_value_set_enum( gvalue, gtkMode );
2115 g_object_set_property( G_OBJECT(m_renderer), "mode", gvalue );
2116 }
2117
GetMode() const2118 wxDataViewCellMode wxDataViewRenderer::GetMode() const
2119 {
2120 return m_mode;
2121 }
2122
GtkApplyAlignment(GtkCellRenderer * renderer)2123 void wxDataViewRenderer::GtkApplyAlignment(GtkCellRenderer *renderer)
2124 {
2125 int align = GetEffectiveAlignmentIfKnown();
2126 if ( align == wxDVR_DEFAULT_ALIGNMENT )
2127 return; // none set yet
2128
2129 // horizontal alignment:
2130
2131 float xalign = 0;
2132 if (align & wxALIGN_RIGHT)
2133 xalign = 1;
2134 else if (align & wxALIGN_CENTER_HORIZONTAL)
2135 xalign = 0.5f;
2136
2137 wxGtkValue gvalue( G_TYPE_FLOAT );
2138 g_value_set_float( gvalue, xalign );
2139 g_object_set_property( G_OBJECT(renderer), "xalign", gvalue );
2140
2141 // vertical alignment:
2142
2143 float yalign = 0;
2144 if (align & wxALIGN_BOTTOM)
2145 yalign = 1;
2146 else if (align & wxALIGN_CENTER_VERTICAL)
2147 yalign = 0.5f;
2148
2149 wxGtkValue gvalue2( G_TYPE_FLOAT );
2150 g_value_set_float( gvalue2, yalign );
2151 g_object_set_property( G_OBJECT(renderer), "yalign", gvalue2 );
2152 }
2153
SetAlignment(int align)2154 void wxDataViewRenderer::SetAlignment( int align )
2155 {
2156 m_alignment = align;
2157 GtkUpdateAlignment();
2158 }
2159
GetAlignment() const2160 int wxDataViewRenderer::GetAlignment() const
2161 {
2162 return m_alignment;
2163 }
2164
EnableEllipsize(wxEllipsizeMode mode)2165 void wxDataViewRenderer::EnableEllipsize(wxEllipsizeMode mode)
2166 {
2167 GtkCellRendererText * const rend = GtkGetTextRenderer();
2168 if ( !rend )
2169 return;
2170
2171 // we use the same values in wxEllipsizeMode as PangoEllipsizeMode so we
2172 // can just cast between them
2173 wxGtkValue gvalue( PANGO_TYPE_ELLIPSIZE_MODE );
2174 g_value_set_enum( gvalue, static_cast<PangoEllipsizeMode>(mode) );
2175 g_object_set_property( G_OBJECT(rend), "ellipsize", gvalue );
2176 }
2177
GetEllipsizeMode() const2178 wxEllipsizeMode wxDataViewRenderer::GetEllipsizeMode() const
2179 {
2180 GtkCellRendererText * const rend = GtkGetTextRenderer();
2181 if ( !rend )
2182 return wxELLIPSIZE_NONE;
2183
2184 wxGtkValue gvalue( PANGO_TYPE_ELLIPSIZE_MODE );
2185 g_object_get_property( G_OBJECT(rend), "ellipsize", gvalue );
2186
2187 return static_cast<wxEllipsizeMode>(g_value_get_enum( gvalue ));
2188 }
2189
IsHighlighted() const2190 bool wxDataViewRenderer::IsHighlighted() const
2191 {
2192 return m_itemBeingRendered.IsOk() &&
2193 GetOwner()->GetOwner()->IsSelected(m_itemBeingRendered);
2194 }
2195
2196 wxVariant
GtkGetValueFromString(const wxString & str) const2197 wxDataViewRenderer::GtkGetValueFromString(const wxString& str) const
2198 {
2199 return str;
2200 }
2201
2202 void
GtkOnTextEdited(const char * itempath,const wxString & str)2203 wxDataViewRenderer::GtkOnTextEdited(const char *itempath, const wxString& str)
2204 {
2205 m_item = wxDataViewItem(GetView()->GTKPathToItem(wxGtkTreePath(itempath)));
2206
2207 wxVariant value(GtkGetValueFromString(str));
2208 DoHandleEditingDone(&value);
2209 }
2210
SetAttr(const wxDataViewItemAttr & WXUNUSED (attr))2211 void wxDataViewRenderer::SetAttr(const wxDataViewItemAttr& WXUNUSED(attr))
2212 {
2213 // There is no way to apply attributes to an arbitrary renderer, so we
2214 // simply can't do anything here.
2215 }
2216
2217 // ---------------------------------------------------------
2218 // wxDataViewTextRenderer
2219 // ---------------------------------------------------------
2220
2221 extern "C"
2222 {
2223
wxGtkTextRendererEditedCallback(GtkCellRendererText * WXUNUSED (renderer),gchar * arg1,gchar * arg2,gpointer user_data)2224 static void wxGtkTextRendererEditedCallback( GtkCellRendererText *WXUNUSED(renderer),
2225 gchar *arg1, gchar *arg2, gpointer user_data )
2226 {
2227 wxDataViewRenderer *cell = (wxDataViewRenderer*) user_data;
2228
2229 cell->GtkOnTextEdited(arg1, wxGTK_CONV_BACK_FONT(
2230 arg2, cell->GetOwner()->GetOwner()->GetFont()));
2231 }
2232
2233 }
2234
2235 namespace
2236 {
2237
2238 // helper function used by wxDataViewTextRenderer and
2239 // wxDataViewCustomRenderer::RenderText(): it applies the attributes to the
2240 // given text renderer
GtkApplyAttr(GtkCellRendererText * renderer,const wxDataViewItemAttr & attr)2241 void GtkApplyAttr(GtkCellRendererText *renderer, const wxDataViewItemAttr& attr)
2242 {
2243 if (attr.HasColour())
2244 {
2245
2246 #ifdef __WXGTK3__
2247 wxGtkValue gvalue( GDK_TYPE_RGBA);
2248 g_value_set_boxed(gvalue, static_cast<const GdkRGBA*>(attr.GetColour()));
2249 g_object_set_property(G_OBJECT(renderer), "foreground-rgba", gvalue);
2250 #else
2251 const GdkColor* const gcol = attr.GetColour().GetColor();
2252 wxGtkValue gvalue( GDK_TYPE_COLOR );
2253 g_value_set_boxed( gvalue, gcol );
2254 g_object_set_property( G_OBJECT(renderer), "foreground_gdk", gvalue );
2255 #endif
2256 }
2257 else
2258 {
2259 wxGtkValue gvalue( G_TYPE_BOOLEAN );
2260 g_value_set_boolean( gvalue, FALSE );
2261 g_object_set_property( G_OBJECT(renderer), "foreground-set", gvalue );
2262 }
2263
2264 if (attr.GetItalic())
2265 {
2266 wxGtkValue gvalue( PANGO_TYPE_STYLE );
2267 g_value_set_enum( gvalue, PANGO_STYLE_ITALIC );
2268 g_object_set_property( G_OBJECT(renderer), "style", gvalue );
2269 }
2270 else
2271 {
2272 wxGtkValue gvalue( G_TYPE_BOOLEAN );
2273 g_value_set_boolean( gvalue, FALSE );
2274 g_object_set_property( G_OBJECT(renderer), "style-set", gvalue );
2275 }
2276
2277
2278 if (attr.GetBold())
2279 {
2280 wxGtkValue gvalue( PANGO_TYPE_WEIGHT );
2281 g_value_set_enum( gvalue, PANGO_WEIGHT_BOLD );
2282 g_object_set_property( G_OBJECT(renderer), "weight", gvalue );
2283 }
2284 else
2285 {
2286 wxGtkValue gvalue( G_TYPE_BOOLEAN );
2287 g_value_set_boolean( gvalue, FALSE );
2288 g_object_set_property( G_OBJECT(renderer), "weight-set", gvalue );
2289 }
2290
2291 if (attr.GetStrikethrough())
2292 {
2293 wxGtkValue gvalue( G_TYPE_BOOLEAN );
2294 g_value_set_boolean( gvalue, TRUE );
2295 g_object_set_property( G_OBJECT(renderer), "strikethrough", gvalue );
2296 }
2297 else
2298 {
2299 wxGtkValue gvalue( G_TYPE_BOOLEAN );
2300 g_value_set_boolean( gvalue, FALSE );
2301 g_object_set_property( G_OBJECT(renderer), "strikethrough-set", gvalue );
2302 }
2303
2304 if (attr.HasBackgroundColour())
2305 {
2306 wxColour colour = attr.GetBackgroundColour();
2307 #ifdef __WXGTK3__
2308 wxGtkValue gvalue( GDK_TYPE_RGBA);
2309 g_value_set_boxed(gvalue, static_cast<const GdkRGBA*>(colour));
2310 g_object_set_property(G_OBJECT(renderer), "cell-background-rgba", gvalue);
2311 #else
2312 const GdkColor * const gcol = colour.GetColor();
2313
2314 wxGtkValue gvalue( GDK_TYPE_COLOR );
2315 g_value_set_boxed( gvalue, gcol );
2316 g_object_set_property( G_OBJECT(renderer), "cell-background_gdk", gvalue );
2317 #endif
2318 }
2319 else
2320 {
2321 wxGtkValue gvalue( G_TYPE_BOOLEAN );
2322 g_value_set_boolean( gvalue, FALSE );
2323 g_object_set_property( G_OBJECT(renderer), "cell-background-set", gvalue );
2324 }
2325 }
2326
2327 } // anonymous namespace
2328
2329 wxIMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewRenderer);
2330
wxDataViewTextRenderer(const wxString & varianttype,wxDataViewCellMode mode,int align)2331 wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype, wxDataViewCellMode mode,
2332 int align ) :
2333 wxDataViewRenderer( varianttype, mode, align )
2334 {
2335 #if wxUSE_MARKUP
2336 m_useMarkup = false;
2337 #endif // wxUSE_MARKUP
2338
2339 GtkWxCellRendererText *text_renderer = gtk_wx_cell_renderer_text_new();
2340 text_renderer->wx_renderer = this;
2341 m_renderer = (GtkCellRenderer*) text_renderer;
2342
2343 if (mode & wxDATAVIEW_CELL_EDITABLE)
2344 {
2345 wxGtkValue gvalue( G_TYPE_BOOLEAN );
2346 g_value_set_boolean( gvalue, true );
2347 g_object_set_property( G_OBJECT(m_renderer), "editable", gvalue );
2348
2349 g_signal_connect_after( m_renderer, "edited", G_CALLBACK(wxGtkTextRendererEditedCallback), this );
2350
2351 GtkInitHandlers();
2352 }
2353
2354 SetMode(mode);
2355 SetAlignment(align);
2356 }
2357
2358 #if wxUSE_MARKUP
EnableMarkup(bool enable)2359 void wxDataViewTextRenderer::EnableMarkup(bool enable)
2360 {
2361 m_useMarkup = enable;
2362 }
2363 #endif // wxUSE_MARKUP
2364
GetTextPropertyName() const2365 const char* wxDataViewTextRenderer::GetTextPropertyName() const
2366 {
2367 #if wxUSE_MARKUP
2368 if ( m_useMarkup )
2369 return "markup";
2370 #endif // wxUSE_MARKUP
2371
2372 return "text";
2373 }
2374
SetTextValue(const wxString & str)2375 bool wxDataViewTextRenderer::SetTextValue(const wxString& str)
2376 {
2377 wxGtkValue gvalue( G_TYPE_STRING );
2378 g_value_set_string( gvalue, wxGTK_CONV_FONT( str, GetOwner()->GetOwner()->GetFont() ) );
2379 g_object_set_property( G_OBJECT(m_renderer), GetTextPropertyName(), gvalue );
2380
2381 return true;
2382 }
2383
GetTextValue(wxString & str) const2384 bool wxDataViewTextRenderer::GetTextValue(wxString& str) const
2385 {
2386 wxGtkValue gvalue( G_TYPE_STRING );
2387 g_object_get_property( G_OBJECT(m_renderer), GetTextPropertyName(), gvalue );
2388 str = wxGTK_CONV_BACK_FONT( g_value_get_string( gvalue ), const_cast<wxDataViewTextRenderer*>(this)->GetOwner()->GetOwner()->GetFont() );
2389
2390 return true;
2391 }
2392
GtkUpdateAlignment()2393 void wxDataViewTextRenderer::GtkUpdateAlignment()
2394 {
2395 wxDataViewRenderer::GtkUpdateAlignment();
2396
2397 if (!wx_is_at_least_gtk2(10))
2398 return;
2399
2400 int align = GetEffectiveAlignmentIfKnown();
2401 if ( align == wxDVR_DEFAULT_ALIGNMENT )
2402 return; // none set yet
2403
2404 // horizontal alignment:
2405 PangoAlignment pangoAlign = PANGO_ALIGN_LEFT;
2406 if (align & wxALIGN_RIGHT)
2407 pangoAlign = PANGO_ALIGN_RIGHT;
2408 else if (align & wxALIGN_CENTER_HORIZONTAL)
2409 pangoAlign = PANGO_ALIGN_CENTER;
2410
2411 wxGtkValue gvalue( pango_alignment_get_type() );
2412 g_value_set_enum( gvalue, pangoAlign );
2413 g_object_set_property( G_OBJECT(m_renderer), "alignment", gvalue );
2414 }
2415
SetAttr(const wxDataViewItemAttr & attr)2416 void wxDataViewTextRenderer::SetAttr(const wxDataViewItemAttr& attr)
2417 {
2418 // An optimization: don't bother resetting the attributes if we're already
2419 // using the defaults.
2420 if ( attr.IsDefault() && m_usingDefaultAttrs )
2421 return;
2422
2423 GtkApplyAttr(GtkGetTextRenderer(), attr);
2424
2425 m_usingDefaultAttrs = attr.IsDefault();
2426 }
2427
GtkGetTextRenderer() const2428 GtkCellRendererText *wxDataViewTextRenderer::GtkGetTextRenderer() const
2429 {
2430 return GTK_CELL_RENDERER_TEXT(m_renderer);
2431 }
2432
2433 // ---------------------------------------------------------
2434 // wxDataViewBitmapRenderer
2435 // ---------------------------------------------------------
2436
2437 #ifdef __WXGTK3__
2438 // Derive a type from GtkCellRendererPixbuf to allow drawing HiDPI bitmaps
2439
2440 extern "C" {
2441 static void wxCellRendererPixbufClassInit(void* g_class, void* class_data);
2442 }
2443
2444 #define WX_CELL_RENDERER_PIXBUF(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, wxCellRendererPixbuf::Type(), wxCellRendererPixbuf)
2445
2446 namespace
2447 {
2448 class wxCellRendererPixbuf: GtkCellRendererPixbuf
2449 {
2450 public:
2451 static GType Type();
2452 static GtkCellRenderer* New();
2453 void Set(const wxBitmap& bitmap);
2454
2455 wxBitmap* m_bitmap;
2456
2457 wxDECLARE_NO_COPY_CLASS(wxCellRendererPixbuf);
2458 wxCellRendererPixbuf() wxMEMBER_DELETE;
2459 ~wxCellRendererPixbuf() wxMEMBER_DELETE;
2460 };
2461
2462 GtkCellRendererClass* wxCellRendererPixbufParentClass;
2463
Type()2464 GType wxCellRendererPixbuf::Type()
2465 {
2466 static GType type;
2467 if (type == 0)
2468 {
2469 type = g_type_register_static_simple(
2470 GTK_TYPE_CELL_RENDERER_PIXBUF,
2471 "wxCellRendererPixbuf",
2472 sizeof(GtkCellRendererPixbufClass),
2473 wxCellRendererPixbufClassInit,
2474 sizeof(wxCellRendererPixbuf),
2475 NULL, GTypeFlags(0));
2476 }
2477 return type;
2478 }
2479
New()2480 GtkCellRenderer* wxCellRendererPixbuf::New()
2481 {
2482 wxCellRendererPixbuf* crp = WX_CELL_RENDERER_PIXBUF(g_object_new(Type(), NULL));
2483 crp->m_bitmap = new wxBitmap;
2484 return GTK_CELL_RENDERER(crp);
2485 }
2486
Set(const wxBitmap & bitmap)2487 void wxCellRendererPixbuf::Set(const wxBitmap& bitmap)
2488 {
2489 *m_bitmap = bitmap;
2490
2491 GdkPixbuf* pixbuf = NULL;
2492 GdkPixbuf* pixbufNew = NULL;
2493 if (bitmap.IsOk())
2494 {
2495 if (bitmap.GetScaleFactor() <= 1)
2496 {
2497 pixbuf = bitmap.GetPixbuf();
2498 m_bitmap->UnRef();
2499 }
2500 else
2501 {
2502 pixbufNew =
2503 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8,
2504 int(bitmap.GetScaledWidth()), int(bitmap.GetScaledHeight()));
2505 }
2506 }
2507 g_object_set(G_OBJECT(this), "pixbuf", pixbuf, NULL);
2508 if (pixbufNew)
2509 g_object_unref(pixbufNew);
2510 }
2511 } // anonymous namespace
2512
2513 extern "C" {
2514 static void
wxCellRendererPixbufRender(GtkCellRenderer * cell,cairo_t * cr,GtkWidget * widget,const GdkRectangle * background_area,const GdkRectangle * cell_area,GtkCellRendererState flags)2515 wxCellRendererPixbufRender(GtkCellRenderer* cell, cairo_t* cr, GtkWidget* widget,
2516 const GdkRectangle* background_area, const GdkRectangle* cell_area, GtkCellRendererState flags)
2517 {
2518 const wxBitmap& bitmap = *WX_CELL_RENDERER_PIXBUF(cell)->m_bitmap;
2519 if (!bitmap.IsOk())
2520 wxCellRendererPixbufParentClass->render(cell, cr, widget, background_area, cell_area, flags);
2521 else
2522 {
2523 const int x = (cell_area->width - int(bitmap.GetScaledWidth() )) / 2;
2524 const int y = (cell_area->height - int(bitmap.GetScaledHeight())) / 2;
2525 bitmap.Draw(cr, cell_area->x + x, cell_area->y + y);
2526 }
2527 }
2528
wxCellRendererPixbufFinalize(GObject * object)2529 static void wxCellRendererPixbufFinalize(GObject* object)
2530 {
2531 wxCellRendererPixbuf* crp = WX_CELL_RENDERER_PIXBUF(object);
2532 delete crp->m_bitmap;
2533 crp->m_bitmap = NULL;
2534 G_OBJECT_CLASS(wxCellRendererPixbufParentClass)->finalize(object);
2535 }
2536
wxCellRendererPixbufClassInit(void * g_class,void *)2537 static void wxCellRendererPixbufClassInit(void* g_class, void* /*class_data*/)
2538 {
2539 GTK_CELL_RENDERER_CLASS(g_class)->render = wxCellRendererPixbufRender;
2540 G_OBJECT_CLASS(g_class)->finalize = wxCellRendererPixbufFinalize;
2541 wxCellRendererPixbufParentClass = GTK_CELL_RENDERER_CLASS(g_type_class_peek_parent(g_class));
2542 }
2543 } // extern "C"
2544 #endif // __WXGTK3__
2545
2546 wxIMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewRenderer);
2547
wxDataViewBitmapRenderer(const wxString & varianttype,wxDataViewCellMode mode,int align)2548 wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype, wxDataViewCellMode mode,
2549 int align ) :
2550 wxDataViewRenderer( varianttype, mode, align )
2551 {
2552 #ifdef __WXGTK3__
2553 m_renderer = wxCellRendererPixbuf::New();
2554 #else
2555 m_renderer = gtk_cell_renderer_pixbuf_new();
2556 #endif
2557
2558 SetMode(mode);
2559 SetAlignment(align);
2560 }
2561
SetValue(const wxVariant & value)2562 bool wxDataViewBitmapRenderer::SetValue( const wxVariant &value )
2563 {
2564 wxBitmap bitmap;
2565 if (value.GetType() == wxS("wxBitmap") || value.GetType() == wxS("wxIcon"))
2566 bitmap << value;
2567
2568 #ifdef __WXGTK3__
2569 WX_CELL_RENDERER_PIXBUF(m_renderer)->Set(bitmap);
2570 #else
2571 g_object_set(G_OBJECT(m_renderer), "pixbuf", bitmap.IsOk() ? bitmap.GetPixbuf() : NULL, NULL);
2572 #endif
2573
2574 return true;
2575 }
2576
GetValue(wxVariant & WXUNUSED (value)) const2577 bool wxDataViewBitmapRenderer::GetValue( wxVariant &WXUNUSED(value) ) const
2578 {
2579 return false;
2580 }
2581
2582 // ---------------------------------------------------------
2583 // wxDataViewToggleRenderer
2584 // ---------------------------------------------------------
2585
2586 extern "C" {
2587 static void wxGtkToggleRendererToggledCallback( GtkCellRendererToggle *renderer,
2588 gchar *path, gpointer user_data );
2589 }
2590
wxGtkToggleRendererToggledCallback(GtkCellRendererToggle * renderer,gchar * path,gpointer user_data)2591 static void wxGtkToggleRendererToggledCallback( GtkCellRendererToggle *renderer,
2592 gchar *path, gpointer user_data )
2593 {
2594 wxDataViewToggleRenderer *cell = (wxDataViewToggleRenderer*) user_data;
2595
2596 // get old value
2597 wxGtkValue gvalue( G_TYPE_BOOLEAN );
2598 g_object_get_property( G_OBJECT(renderer), "active", gvalue );
2599 // invert it
2600 wxVariant value = !g_value_get_boolean( gvalue );
2601
2602 if (!cell->Validate( value ))
2603 return;
2604
2605 wxDataViewCtrl * const ctrl = cell->GetOwner()->GetOwner();
2606 wxDataViewModel *model = ctrl->GetModel();
2607
2608 wxDataViewItem item(ctrl->GTKPathToItem(wxGtkTreePath(path)));
2609
2610 unsigned int model_col = cell->GetOwner()->GetModelColumn();
2611
2612 model->ChangeValue( value, item, model_col );
2613 }
2614
2615 wxIMPLEMENT_CLASS(wxDataViewToggleRenderer, wxDataViewRenderer);
2616
wxDataViewToggleRenderer(const wxString & varianttype,wxDataViewCellMode mode,int align)2617 wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString &varianttype,
2618 wxDataViewCellMode mode, int align ) :
2619 wxDataViewRenderer( varianttype, mode, align )
2620 {
2621 m_renderer = (GtkCellRenderer*) gtk_cell_renderer_toggle_new();
2622
2623 if (mode & wxDATAVIEW_CELL_ACTIVATABLE)
2624 {
2625 g_signal_connect_after( m_renderer, "toggled",
2626 G_CALLBACK(wxGtkToggleRendererToggledCallback), this );
2627 }
2628 else
2629 {
2630 wxGtkValue gvalue( G_TYPE_BOOLEAN );
2631 g_value_set_boolean( gvalue, false );
2632 g_object_set_property( G_OBJECT(m_renderer), "activatable", gvalue );
2633 }
2634
2635 SetMode(mode);
2636 SetAlignment(align);
2637 }
2638
ShowAsRadio()2639 void wxDataViewToggleRenderer::ShowAsRadio()
2640 {
2641 gtk_cell_renderer_toggle_set_radio(GTK_CELL_RENDERER_TOGGLE(m_renderer), TRUE);
2642 }
2643
SetValue(const wxVariant & value)2644 bool wxDataViewToggleRenderer::SetValue( const wxVariant &value )
2645 {
2646 bool tmp = value;
2647
2648 wxGtkValue gvalue( G_TYPE_BOOLEAN );
2649 g_value_set_boolean( gvalue, tmp );
2650 g_object_set_property( G_OBJECT(m_renderer), "active", gvalue );
2651
2652 return true;
2653 }
2654
GetValue(wxVariant & value) const2655 bool wxDataViewToggleRenderer::GetValue( wxVariant &value ) const
2656 {
2657 wxGtkValue gvalue( G_TYPE_BOOLEAN );
2658 g_object_get_property( G_OBJECT(m_renderer), "active", gvalue );
2659 value = g_value_get_boolean( gvalue ) != 0;
2660
2661 return true;
2662 }
2663
2664 // ---------------------------------------------------------
2665 // wxDataViewCustomRenderer
2666 // ---------------------------------------------------------
2667
2668 #ifndef __WXGTK3__
2669 class wxDataViewCtrlDCImpl: public wxWindowDCImpl
2670 {
2671 public:
wxDataViewCtrlDCImpl(wxDC * owner,wxDataViewCtrl * window)2672 wxDataViewCtrlDCImpl( wxDC *owner, wxDataViewCtrl *window ) :
2673 wxWindowDCImpl( owner )
2674 {
2675 GtkWidget *widget = window->m_treeview;
2676 // Set later
2677 m_gdkwindow = NULL;
2678
2679 m_window = window;
2680
2681 m_context = window->GTKGetPangoDefaultContext();
2682 g_object_ref(m_context);
2683 m_layout = pango_layout_new( m_context );
2684 m_fontdesc = pango_font_description_copy(gtk_widget_get_style(widget)->font_desc);
2685
2686 m_cmap = gtk_widget_get_colormap( widget ? widget : window->m_widget );
2687
2688 // Set m_gdkwindow later
2689 // SetUpDC();
2690 }
2691 };
2692
2693 class wxDataViewCtrlDC: public wxWindowDC
2694 {
2695 public:
wxDataViewCtrlDC(wxDataViewCtrl * window)2696 wxDataViewCtrlDC( wxDataViewCtrl *window ) :
2697 wxWindowDC( new wxDataViewCtrlDCImpl( this, window ) )
2698 { }
2699 };
2700 #endif
2701
2702 // ---------------------------------------------------------
2703 // wxDataViewCustomRenderer
2704 // ---------------------------------------------------------
2705
2706 wxIMPLEMENT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer);
2707
wxDataViewCustomRenderer(const wxString & varianttype,wxDataViewCellMode mode,int align,bool no_init)2708 wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype,
2709 wxDataViewCellMode mode,
2710 int align,
2711 bool no_init )
2712 : wxDataViewCustomRendererBase( varianttype, mode, align )
2713 {
2714 m_dc = NULL;
2715 m_text_renderer = NULL;
2716 m_renderParams = NULL;
2717
2718 if (no_init)
2719 m_renderer = NULL;
2720 else
2721 Init(mode, align);
2722 }
2723
GtkUpdateAlignment()2724 void wxDataViewCustomRenderer::GtkUpdateAlignment()
2725 {
2726 wxDataViewCustomRendererBase::GtkUpdateAlignment();
2727
2728 if ( m_text_renderer )
2729 GtkApplyAlignment(GTK_CELL_RENDERER(m_text_renderer));
2730 }
2731
GtkInitTextRenderer()2732 void wxDataViewCustomRenderer::GtkInitTextRenderer()
2733 {
2734 m_text_renderer = GTK_CELL_RENDERER_TEXT(gtk_cell_renderer_text_new());
2735 g_object_ref_sink(m_text_renderer);
2736
2737 GtkApplyAlignment(GTK_CELL_RENDERER(m_text_renderer));
2738 gtk_cell_renderer_set_padding(GTK_CELL_RENDERER(m_text_renderer), 0, 0);
2739 }
2740
GtkGetTextRenderer() const2741 GtkCellRendererText *wxDataViewCustomRenderer::GtkGetTextRenderer() const
2742 {
2743 if ( !m_text_renderer )
2744 {
2745 // we create it on demand so need to do it even from a const function
2746 const_cast<wxDataViewCustomRenderer *>(this)->GtkInitTextRenderer();
2747 }
2748
2749 return m_text_renderer;
2750 }
2751
GtkGetEditorWidget() const2752 GtkWidget* wxDataViewCustomRenderer::GtkGetEditorWidget() const
2753 {
2754 return GTK_WX_CELL_RENDERER(m_renderer)->editor_bin;
2755 }
2756
RenderText(const wxString & text,int xoffset,wxRect cell,wxDC * WXUNUSED (dc),int WXUNUSED (state))2757 void wxDataViewCustomRenderer::RenderText( const wxString &text,
2758 int xoffset,
2759 wxRect cell,
2760 wxDC *WXUNUSED(dc),
2761 int WXUNUSED(state) )
2762 {
2763
2764 GtkCellRendererText * const textRenderer = GtkGetTextRenderer();
2765
2766 wxGtkValue gvalue( G_TYPE_STRING );
2767 g_value_set_string( gvalue, wxGTK_CONV_FONT( text, GetOwner()->GetOwner()->GetFont() ) );
2768 g_object_set_property( G_OBJECT(textRenderer), "text", gvalue );
2769
2770 GtkApplyAttr(textRenderer, GetAttr());
2771
2772 GdkRectangle cell_area;
2773 wxRectToGDKRect(cell, cell_area);
2774 cell_area.x += xoffset;
2775 cell_area.width -= xoffset;
2776
2777 gtk_cell_renderer_render( GTK_CELL_RENDERER(textRenderer),
2778 #ifdef __WXGTK3__
2779 m_renderParams->cr,
2780 #else
2781 m_renderParams->window,
2782 #endif
2783 m_renderParams->widget,
2784 m_renderParams->background_area,
2785 &cell_area,
2786 #ifndef __WXGTK3__
2787 m_renderParams->expose_area,
2788 #endif
2789 GtkCellRendererState(m_renderParams->flags));
2790 }
2791
Init(wxDataViewCellMode mode,int align)2792 bool wxDataViewCustomRenderer::Init(wxDataViewCellMode mode, int align)
2793 {
2794 GtkWxCellRenderer *renderer = (GtkWxCellRenderer *) gtk_wx_cell_renderer_new();
2795 renderer->cell = this;
2796
2797 m_renderer = (GtkCellRenderer*) renderer;
2798
2799 SetMode(mode);
2800 SetAlignment(align);
2801
2802 GtkInitHandlers();
2803
2804 return true;
2805 }
2806
~wxDataViewCustomRenderer()2807 wxDataViewCustomRenderer::~wxDataViewCustomRenderer()
2808 {
2809 if (m_dc)
2810 delete m_dc;
2811
2812 if (m_text_renderer)
2813 g_object_unref(m_text_renderer);
2814 }
2815
GetDC()2816 wxDC *wxDataViewCustomRenderer::GetDC()
2817 {
2818 if (m_dc == NULL)
2819 {
2820 wxDataViewCtrl* ctrl = NULL;
2821 wxDataViewColumn* column = GetOwner();
2822 if (column)
2823 ctrl = column->GetOwner();
2824 #ifdef __WXGTK3__
2825 wxASSERT(m_renderParams);
2826 cairo_t* cr = m_renderParams->cr;
2827 wxASSERT(cr && cairo_status(cr) == 0);
2828 m_dc = new wxGTKCairoDC(cr, ctrl);
2829 #else
2830 if (ctrl == NULL)
2831 return NULL;
2832 m_dc = new wxDataViewCtrlDC(ctrl);
2833 #endif
2834 }
2835
2836 return m_dc;
2837 }
2838
2839 // ---------------------------------------------------------
2840 // wxDataViewProgressRenderer
2841 // ---------------------------------------------------------
2842
2843 wxIMPLEMENT_CLASS(wxDataViewProgressRenderer, wxDataViewCustomRenderer);
2844
wxDataViewProgressRenderer(const wxString & label,const wxString & varianttype,wxDataViewCellMode mode,int align)2845 wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString &label,
2846 const wxString &varianttype, wxDataViewCellMode mode, int align ) :
2847 wxDataViewCustomRenderer( varianttype, mode, align, true )
2848 , m_label(label)
2849 {
2850 m_value = 0;
2851 m_renderer = (GtkCellRenderer*) gtk_cell_renderer_progress_new();
2852
2853 SetMode(mode);
2854 SetAlignment(align);
2855
2856 #if !wxUSE_UNICODE
2857 // We can't initialize the renderer just yet because we don't have the
2858 // pointer to the column that uses this renderer yet and so attempt to
2859 // dereference GetOwner() to get the font that is used as a source of
2860 // encoding in multibyte-to-Unicode conversion in GTKSetLabel() in
2861 // non-Unicode builds would crash. So simply remember to do it later.
2862 if ( !m_label.empty() )
2863 m_needsToSetLabel = true;
2864 else
2865 #endif // !wxUSE_UNICODE
2866 {
2867 GTKSetLabel();
2868 }
2869 }
2870
~wxDataViewProgressRenderer()2871 wxDataViewProgressRenderer::~wxDataViewProgressRenderer()
2872 {
2873 }
2874
GTKSetLabel()2875 void wxDataViewProgressRenderer::GTKSetLabel()
2876 {
2877 wxGtkValue gvalue( G_TYPE_STRING );
2878
2879 // Take care to not use GetOwner() here if the label is empty, we can be
2880 // called from ctor when GetOwner() is still NULL in this case.
2881 wxScopedCharBuffer buf;
2882 if ( m_label.empty() )
2883 buf = wxScopedCharBuffer::CreateNonOwned("");
2884 else
2885 buf = wxGTK_CONV_FONT(m_label, GetOwner()->GetOwner()->GetFont());
2886
2887 g_value_set_string( gvalue, buf);
2888 g_object_set_property( G_OBJECT(m_renderer), "text", gvalue );
2889
2890 #if !wxUSE_UNICODE
2891 m_needsToSetLabel = false;
2892 #endif // !wxUSE_UNICODE
2893 }
2894
SetValue(const wxVariant & value)2895 bool wxDataViewProgressRenderer::SetValue( const wxVariant &value )
2896 {
2897 #if !wxUSE_UNICODE
2898 if ( m_needsToSetLabel )
2899 GTKSetLabel();
2900 #endif // !wxUSE_UNICODE
2901
2902 gint tmp = (long) value;
2903 wxGtkValue gvalue( G_TYPE_INT );
2904 g_value_set_int( gvalue, tmp );
2905 g_object_set_property( G_OBJECT(m_renderer), "value", gvalue );
2906
2907 return true;
2908 }
2909
GetValue(wxVariant & WXUNUSED (value)) const2910 bool wxDataViewProgressRenderer::GetValue( wxVariant &WXUNUSED(value) ) const
2911 {
2912 return false;
2913 }
2914
Render(wxRect cell,wxDC * dc,int WXUNUSED (state))2915 bool wxDataViewProgressRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
2916 {
2917 double pct = (double)m_value / 100.0;
2918 wxRect bar = cell;
2919 bar.width = (int)(cell.width * pct);
2920 dc->SetPen( *wxTRANSPARENT_PEN );
2921 dc->SetBrush( *wxBLUE_BRUSH );
2922 dc->DrawRectangle( bar );
2923
2924 dc->SetBrush( *wxTRANSPARENT_BRUSH );
2925 dc->SetPen( *wxBLACK_PEN );
2926 dc->DrawRectangle( cell );
2927
2928 return true;
2929 }
2930
GetSize() const2931 wxSize wxDataViewProgressRenderer::GetSize() const
2932 {
2933 return wxSize(40,12);
2934 }
2935
2936 // -------------------------------------
2937 // wxDataViewChoiceRenderer
2938 // -------------------------------------
2939
wxDataViewChoiceRenderer(const wxArrayString & choices,wxDataViewCellMode mode,int alignment)2940 wxDataViewChoiceRenderer::wxDataViewChoiceRenderer( const wxArrayString &choices,
2941 wxDataViewCellMode mode, int alignment ) :
2942 wxDataViewCustomRenderer( "string", mode, alignment, true )
2943 , m_choices(choices)
2944 {
2945 m_renderer = (GtkCellRenderer*) gtk_cell_renderer_combo_new();
2946 GtkListStore *store = gtk_list_store_new( 1, G_TYPE_STRING );
2947 for (size_t n = 0; n < m_choices.GetCount(); n++)
2948 {
2949 gtk_list_store_insert_with_values(
2950 store, NULL, n, 0,
2951 static_cast<const char *>(m_choices[n].utf8_str()), -1 );
2952 }
2953
2954 g_object_set (m_renderer,
2955 "model", store,
2956 "text-column", 0,
2957 "has-entry", FALSE,
2958 NULL);
2959
2960 bool editable = (mode & wxDATAVIEW_CELL_EDITABLE) != 0;
2961 g_object_set (m_renderer, "editable", editable, NULL);
2962
2963 SetAlignment(alignment);
2964
2965 g_signal_connect_after( m_renderer, "edited", G_CALLBACK(wxGtkTextRendererEditedCallback), this );
2966
2967 GtkInitHandlers();
2968 }
2969
Render(wxRect rect,wxDC * dc,int state)2970 bool wxDataViewChoiceRenderer::Render( wxRect rect, wxDC *dc, int state )
2971 {
2972 RenderText( m_data, 0, rect, dc, state );
2973 return true;
2974 }
2975
GetSize() const2976 wxSize wxDataViewChoiceRenderer::GetSize() const
2977 {
2978 return wxSize(70,20);
2979 }
2980
SetValue(const wxVariant & value)2981 bool wxDataViewChoiceRenderer::SetValue( const wxVariant &value )
2982 {
2983 wxGtkValue gvalue( G_TYPE_STRING );
2984 g_value_set_string(gvalue,
2985 wxGTK_CONV_FONT(value.GetString(),
2986 GetOwner()->GetOwner()->GetFont()));
2987 g_object_set_property( G_OBJECT(m_renderer), "text", gvalue );
2988
2989 return true;
2990 }
2991
GetValue(wxVariant & value) const2992 bool wxDataViewChoiceRenderer::GetValue( wxVariant &value ) const
2993 {
2994 wxGtkValue gvalue( G_TYPE_STRING );
2995 g_object_get_property( G_OBJECT(m_renderer), "text", gvalue );
2996 value = wxGTK_CONV_BACK_FONT(g_value_get_string(gvalue),
2997 GetOwner()->GetOwner()->GetFont());
2998
2999 return true;
3000 }
3001
GtkUpdateAlignment()3002 void wxDataViewChoiceRenderer::GtkUpdateAlignment()
3003 {
3004 wxDataViewCustomRenderer::GtkUpdateAlignment();
3005
3006 if (!wx_is_at_least_gtk2(10))
3007 return;
3008
3009 int align = GetEffectiveAlignmentIfKnown();
3010 if ( align == wxDVR_DEFAULT_ALIGNMENT )
3011 return; // none set yet
3012
3013 // horizontal alignment:
3014 PangoAlignment pangoAlign = PANGO_ALIGN_LEFT;
3015 if (align & wxALIGN_RIGHT)
3016 pangoAlign = PANGO_ALIGN_RIGHT;
3017 else if (align & wxALIGN_CENTER_HORIZONTAL)
3018 pangoAlign = PANGO_ALIGN_CENTER;
3019
3020 wxGtkValue gvalue( pango_alignment_get_type() );
3021 g_value_set_enum( gvalue, pangoAlign );
3022 g_object_set_property( G_OBJECT(m_renderer), "alignment", gvalue );
3023 }
3024
3025 // ----------------------------------------------------------------------------
3026 // wxDataViewChoiceByIndexRenderer
3027 // ----------------------------------------------------------------------------
3028
wxDataViewChoiceByIndexRenderer(const wxArrayString & choices,wxDataViewCellMode mode,int alignment)3029 wxDataViewChoiceByIndexRenderer::wxDataViewChoiceByIndexRenderer( const wxArrayString &choices,
3030 wxDataViewCellMode mode, int alignment ) :
3031 wxDataViewChoiceRenderer( choices, mode, alignment )
3032 {
3033 m_variantType = wxS("long");
3034 }
3035
3036 wxVariant
GtkGetValueFromString(const wxString & str) const3037 wxDataViewChoiceByIndexRenderer::GtkGetValueFromString(const wxString& str) const
3038 {
3039 return static_cast<long>(GetChoices().Index(str));
3040 }
3041
SetValue(const wxVariant & value)3042 bool wxDataViewChoiceByIndexRenderer::SetValue( const wxVariant &value )
3043 {
3044 wxVariant string_value = GetChoice( value.GetLong() );
3045 return wxDataViewChoiceRenderer::SetValue( string_value );
3046 }
3047
GetValue(wxVariant & value) const3048 bool wxDataViewChoiceByIndexRenderer::GetValue( wxVariant &value ) const
3049 {
3050 wxVariant string_value;
3051 if (!wxDataViewChoiceRenderer::GetValue( string_value ))
3052 return false;
3053
3054 value = (long) GetChoices().Index( string_value.GetString() );
3055 return true;
3056 }
3057
3058 // ---------------------------------------------------------
3059 // wxDataViewIconTextRenderer
3060 // ---------------------------------------------------------
3061
3062 wxIMPLEMENT_CLASS(wxDataViewIconTextRenderer, wxDataViewCustomRenderer);
3063
wxDataViewIconTextRenderer(const wxString & varianttype,wxDataViewCellMode mode,int align)3064 wxDataViewIconTextRenderer::wxDataViewIconTextRenderer
3065 (
3066 const wxString &varianttype,
3067 wxDataViewCellMode mode,
3068 int align
3069 )
3070 : wxDataViewTextRenderer(varianttype, mode, align)
3071 {
3072 #ifdef __WXGTK3__
3073 m_rendererIcon = wxCellRendererPixbuf::New();
3074 #else
3075 m_rendererIcon = gtk_cell_renderer_pixbuf_new();
3076 #endif
3077 }
3078
~wxDataViewIconTextRenderer()3079 wxDataViewIconTextRenderer::~wxDataViewIconTextRenderer()
3080 {
3081 }
3082
GtkPackIntoColumn(GtkTreeViewColumn * column)3083 void wxDataViewIconTextRenderer::GtkPackIntoColumn(GtkTreeViewColumn *column)
3084 {
3085 // add the icon renderer first
3086 gtk_tree_view_column_pack_start(column, m_rendererIcon, FALSE /* !expand */);
3087
3088 // add the text renderer too
3089 wxDataViewRenderer::GtkPackIntoColumn(column);
3090 }
3091
SetValue(const wxVariant & value)3092 bool wxDataViewIconTextRenderer::SetValue( const wxVariant &value )
3093 {
3094 m_value << value;
3095
3096 SetTextValue(m_value.GetText());
3097
3098 const wxIcon& icon = m_value.GetIcon();
3099 #ifdef __WXGTK3__
3100 WX_CELL_RENDERER_PIXBUF(m_rendererIcon)->Set(icon);
3101 #else
3102 g_object_set(G_OBJECT(m_rendererIcon), "pixbuf", icon.IsOk() ? icon.GetPixbuf() : NULL, NULL);
3103 #endif
3104
3105 return true;
3106 }
3107
GetValue(wxVariant & value) const3108 bool wxDataViewIconTextRenderer::GetValue(wxVariant& value) const
3109 {
3110 wxString str;
3111 if ( !GetTextValue(str) )
3112 return false;
3113
3114 // user doesn't have any way to edit the icon so leave it unchanged
3115 value << wxDataViewIconText(str, m_value.GetIcon());
3116
3117 return true;
3118 }
3119
3120 wxVariant
GtkGetValueFromString(const wxString & str) const3121 wxDataViewIconTextRenderer::GtkGetValueFromString(const wxString& str) const
3122 {
3123 // we receive just the text part of our value as it's the only one which
3124 // can be edited, but we need the full wxDataViewIconText value for the
3125 // model
3126 wxVariant valueIconText;
3127 valueIconText << wxDataViewIconText(str, m_value.GetIcon());
3128 return valueIconText;
3129 }
3130
3131 // ---------------------------------------------------------
3132 // wxDataViewColumn
3133 // ---------------------------------------------------------
3134
3135
3136 static gboolean
gtk_dataview_header_button_press_callback(GtkWidget * WXUNUSED (widget),GdkEventButton * gdk_event,wxDataViewColumn * column)3137 gtk_dataview_header_button_press_callback( GtkWidget *WXUNUSED(widget),
3138 GdkEventButton *gdk_event,
3139 wxDataViewColumn *column )
3140 {
3141 if (gdk_event->type != GDK_BUTTON_PRESS)
3142 return FALSE;
3143
3144 if (gdk_event->button == 1)
3145 {
3146 gs_lastLeftClickHeader = column;
3147
3148 wxDataViewCtrl *dv = column->GetOwner();
3149 wxDataViewEvent event(wxEVT_DATAVIEW_COLUMN_HEADER_CLICK, dv, column);
3150 if (dv->HandleWindowEvent( event ))
3151 return FALSE;
3152 }
3153
3154 if (gdk_event->button == 3)
3155 {
3156 wxDataViewCtrl *dv = column->GetOwner();
3157 wxDataViewEvent
3158 event(wxEVT_DATAVIEW_COLUMN_HEADER_RIGHT_CLICK, dv, column);
3159 if (dv->HandleWindowEvent( event ))
3160 return FALSE;
3161 }
3162
3163 return FALSE;
3164 }
3165
3166 extern "C"
3167 {
3168
wxGtkTreeCellDataFunc(GtkTreeViewColumn * WXUNUSED (column),GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer data)3169 static void wxGtkTreeCellDataFunc( GtkTreeViewColumn *WXUNUSED(column),
3170 GtkCellRenderer *renderer,
3171 GtkTreeModel *model,
3172 GtkTreeIter *iter,
3173 gpointer data )
3174 {
3175 g_return_if_fail (GTK_IS_WX_TREE_MODEL (model));
3176 GtkWxTreeModel *tree_model = (GtkWxTreeModel *) model;
3177
3178 if ( !tree_model->stamp )
3179 {
3180 // The model is temporarily invalid and can't be used, see the code in
3181 // wxDataViewCtrlInternal::UseModel().
3182 return;
3183 }
3184
3185 wxDataViewRenderer *cell = (wxDataViewRenderer*) data;
3186
3187 wxDataViewItem item( (void*) iter->user_data );
3188 const unsigned column = cell->GetOwner()->GetModelColumn();
3189
3190 wxDataViewModel *wx_model = tree_model->internal->GetDataViewModel();
3191
3192 if (!wx_model->IsVirtualListModel())
3193 {
3194 gboolean visible = wx_model->HasValue(item, column);
3195 wxGtkValue gvalue( G_TYPE_BOOLEAN );
3196 g_value_set_boolean( gvalue, visible );
3197 g_object_set_property( G_OBJECT(renderer), "visible", gvalue );
3198
3199 if ( !visible )
3200 return;
3201 }
3202
3203 cell->GtkSetCurrentItem(item);
3204 cell->PrepareForItem(wx_model, item, column);
3205 }
3206
3207 } // extern "C"
3208
3209 #include <wx/listimpl.cpp>
WX_DEFINE_LIST(wxDataViewColumnList)3210 WX_DEFINE_LIST(wxDataViewColumnList)
3211
3212 wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewRenderer *cell,
3213 unsigned int model_column, int width,
3214 wxAlignment align, int flags )
3215 : wxDataViewColumnBase( cell, model_column )
3216 {
3217 Init( align, flags, width );
3218
3219 SetTitle( title );
3220 }
3221
wxDataViewColumn(const wxBitmap & bitmap,wxDataViewRenderer * cell,unsigned int model_column,int width,wxAlignment align,int flags)3222 wxDataViewColumn::wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *cell,
3223 unsigned int model_column, int width,
3224 wxAlignment align, int flags )
3225 : wxDataViewColumnBase( bitmap, cell, model_column )
3226 {
3227 Init( align, flags, width );
3228
3229 SetBitmap( bitmap );
3230 }
3231
Init(wxAlignment align,int flags,int width)3232 void wxDataViewColumn::Init(wxAlignment align, int flags, int width)
3233 {
3234 m_isConnected = false;
3235
3236 GtkTreeViewColumn *column = gtk_tree_view_column_new();
3237 m_column = (GtkWidget*) column;
3238
3239 SetFlags( flags );
3240 SetAlignment( align );
3241
3242 SetWidth( width );
3243
3244 // Create container for icon and label
3245 GtkWidget* box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);
3246 gtk_widget_show( box );
3247 // gtk_container_set_border_width((GtkContainer*)box, 2);
3248 m_image = wxGtkImage::New();
3249 gtk_box_pack_start(GTK_BOX(box), m_image, FALSE, FALSE, 1);
3250 m_label = gtk_label_new("");
3251 gtk_box_pack_end( GTK_BOX(box), GTK_WIDGET(m_label), FALSE, FALSE, 1 );
3252 gtk_tree_view_column_set_widget( column, box );
3253
3254 wxDataViewRenderer * const colRenderer = GetRenderer();
3255 GtkCellRenderer * const cellRenderer = colRenderer->GetGtkHandle();
3256
3257 colRenderer->GtkPackIntoColumn(column);
3258
3259 gtk_tree_view_column_set_cell_data_func( column, cellRenderer,
3260 wxGtkTreeCellDataFunc, (gpointer) colRenderer, NULL );
3261 }
3262
OnInternalIdle()3263 void wxDataViewColumn::OnInternalIdle()
3264 {
3265 if (m_isConnected)
3266 return;
3267
3268 if (gtk_widget_get_realized(GetOwner()->m_treeview))
3269 {
3270 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
3271 GtkWidget* button = gtk_tree_view_column_get_button(column);
3272 if (button)
3273 {
3274 g_signal_connect(button, "button_press_event",
3275 G_CALLBACK (gtk_dataview_header_button_press_callback), this);
3276
3277 // otherwise the event will be blocked by GTK+
3278 gtk_tree_view_column_set_clickable( column, TRUE );
3279
3280 m_isConnected = true;
3281 }
3282 }
3283 }
3284
SetOwner(wxDataViewCtrl * owner)3285 void wxDataViewColumn::SetOwner( wxDataViewCtrl *owner )
3286 {
3287 wxDataViewColumnBase::SetOwner( owner );
3288
3289 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
3290
3291 gtk_tree_view_column_set_title( column, wxGTK_CONV_FONT(GetTitle(), GetOwner()->GetFont() ) );
3292 }
3293
SetTitle(const wxString & title)3294 void wxDataViewColumn::SetTitle( const wxString &title )
3295 {
3296 wxDataViewCtrl *ctrl = GetOwner();
3297 gtk_label_set_text( GTK_LABEL(m_label), ctrl ? wxGTK_CONV_FONT(title, ctrl->GetFont())
3298 : wxGTK_CONV_SYS(title) );
3299 if (title.empty())
3300 gtk_widget_hide( m_label );
3301 else
3302 gtk_widget_show( m_label );
3303 }
3304
GetTitle() const3305 wxString wxDataViewColumn::GetTitle() const
3306 {
3307 return wxGTK_CONV_BACK_FONT(
3308 gtk_label_get_text( GTK_LABEL(m_label) ),
3309 GetOwner()->GetFont()
3310 );
3311 }
3312
SetBitmap(const wxBitmap & bitmap)3313 void wxDataViewColumn::SetBitmap( const wxBitmap &bitmap )
3314 {
3315 wxDataViewColumnBase::SetBitmap( bitmap );
3316
3317 if (bitmap.IsOk())
3318 {
3319 WX_GTK_IMAGE(m_image)->Set(bitmap);
3320 gtk_widget_show( m_image );
3321 }
3322 else
3323 {
3324 gtk_widget_hide( m_image );
3325 }
3326 }
3327
SetHidden(bool hidden)3328 void wxDataViewColumn::SetHidden( bool hidden )
3329 {
3330 gtk_tree_view_column_set_visible( GTK_TREE_VIEW_COLUMN(m_column), !hidden );
3331 }
3332
SetResizeable(bool resizable)3333 void wxDataViewColumn::SetResizeable( bool resizable )
3334 {
3335 gtk_tree_view_column_set_resizable( GTK_TREE_VIEW_COLUMN(m_column), resizable );
3336 }
3337
SetAlignment(wxAlignment align)3338 void wxDataViewColumn::SetAlignment( wxAlignment align )
3339 {
3340 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
3341
3342 float xalign = 0;
3343 if (align == wxALIGN_RIGHT)
3344 xalign = 1;
3345 if (align == wxALIGN_CENTER_HORIZONTAL ||
3346 align == wxALIGN_CENTER)
3347 xalign = 0.5f;
3348
3349 gtk_tree_view_column_set_alignment( column, xalign );
3350
3351 if (m_renderer && m_renderer->GetAlignment() == -1)
3352 m_renderer->GtkUpdateAlignment();
3353 }
3354
GetAlignment() const3355 wxAlignment wxDataViewColumn::GetAlignment() const
3356 {
3357 gfloat xalign = gtk_tree_view_column_get_alignment( GTK_TREE_VIEW_COLUMN(m_column) );
3358
3359 if (xalign == 1)
3360 return wxALIGN_RIGHT;
3361 if (xalign == 0.5f)
3362 return wxALIGN_CENTER_HORIZONTAL;
3363
3364 return wxALIGN_LEFT;
3365 }
3366
SetSortable(bool sortable)3367 void wxDataViewColumn::SetSortable( bool sortable )
3368 {
3369 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
3370
3371 if ( sortable )
3372 {
3373 gtk_tree_view_column_set_sort_column_id( column, GetModelColumn() );
3374 }
3375 else
3376 {
3377 gtk_tree_view_column_set_sort_column_id( column, -1 );
3378 gtk_tree_view_column_set_sort_indicator( column, FALSE );
3379 gtk_tree_view_column_set_clickable( column, FALSE );
3380 }
3381 }
3382
IsSortable() const3383 bool wxDataViewColumn::IsSortable() const
3384 {
3385 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
3386 return gtk_tree_view_column_get_clickable( column ) != 0;
3387 }
3388
IsSortKey() const3389 bool wxDataViewColumn::IsSortKey() const
3390 {
3391 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
3392 return gtk_tree_view_column_get_sort_indicator( column ) != 0;
3393 }
3394
IsResizeable() const3395 bool wxDataViewColumn::IsResizeable() const
3396 {
3397 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
3398 return gtk_tree_view_column_get_resizable( column ) != 0;
3399 }
3400
IsHidden() const3401 bool wxDataViewColumn::IsHidden() const
3402 {
3403 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
3404 return !gtk_tree_view_column_get_visible( column );
3405 }
3406
SetSortOrder(bool ascending)3407 void wxDataViewColumn::SetSortOrder( bool ascending )
3408 {
3409 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
3410
3411 GtkSortType order = GTK_SORT_DESCENDING;
3412 if (ascending)
3413 order = GTK_SORT_ASCENDING;
3414
3415 gtk_tree_view_column_set_sort_order(column, order);
3416 gtk_tree_view_column_set_sort_indicator( column, TRUE );
3417
3418 wxDataViewCtrlInternal* internal = m_owner->GtkGetInternal();
3419 internal->SetSortOrder(order);
3420 internal->SetSortColumn(m_model_column);
3421 internal->SetDataViewSortColumn(this);
3422 }
3423
UnsetAsSortKey()3424 void wxDataViewColumn::UnsetAsSortKey()
3425 {
3426 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
3427
3428 gtk_tree_view_column_set_sort_indicator( column, FALSE );
3429
3430 wxDataViewCtrlInternal* internal = m_owner->GtkGetInternal();
3431 internal->SetSortColumn(-1);
3432 internal->SetDataViewSortColumn(NULL);
3433 }
3434
IsSortOrderAscending() const3435 bool wxDataViewColumn::IsSortOrderAscending() const
3436 {
3437 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
3438
3439 return (gtk_tree_view_column_get_sort_order( column ) != GTK_SORT_DESCENDING);
3440 }
3441
SetMinWidth(int width)3442 void wxDataViewColumn::SetMinWidth( int width )
3443 {
3444 gtk_tree_view_column_set_min_width( GTK_TREE_VIEW_COLUMN(m_column), width );
3445 }
3446
GetMinWidth() const3447 int wxDataViewColumn::GetMinWidth() const
3448 {
3449 return gtk_tree_view_column_get_min_width( GTK_TREE_VIEW_COLUMN(m_column) );
3450 }
3451
GetWidth() const3452 int wxDataViewColumn::GetWidth() const
3453 {
3454 return gtk_tree_view_column_get_width( GTK_TREE_VIEW_COLUMN(m_column) );
3455 }
3456
SetWidth(int width)3457 void wxDataViewColumn::SetWidth( int width )
3458 {
3459 // Notice that we don't have anything to do for wxCOL_WIDTH_DEFAULT and
3460 // wxCOL_WIDTH_AUTOSIZE as the native control tries to use the appropriate
3461 // width by default anyhow, don't use GTK_TREE_VIEW_COLUMN_AUTOSIZE to
3462 // force it because, as mentioned in GTK+ documentation, it's completely
3463 // inappropriate for controls with a lot of items (because it's O(N)) and
3464 // it would also prevent the user from resizing the column manually which
3465 // we want to allow for resizeable columns.
3466 if ( width >= 0 )
3467 {
3468 gtk_tree_view_column_set_sizing( GTK_TREE_VIEW_COLUMN(m_column), GTK_TREE_VIEW_COLUMN_FIXED );
3469 gtk_tree_view_column_set_fixed_width( GTK_TREE_VIEW_COLUMN(m_column), width );
3470 }
3471 }
3472
SetReorderable(bool reorderable)3473 void wxDataViewColumn::SetReorderable( bool reorderable )
3474 {
3475 gtk_tree_view_column_set_reorderable( GTK_TREE_VIEW_COLUMN(m_column), reorderable );
3476 }
3477
IsReorderable() const3478 bool wxDataViewColumn::IsReorderable() const
3479 {
3480 return gtk_tree_view_column_get_reorderable( GTK_TREE_VIEW_COLUMN(m_column) ) != 0;
3481 }
3482
3483 //-----------------------------------------------------------------------------
3484 // wxGtkTreeModelNode
3485 //-----------------------------------------------------------------------------
3486
3487 #if 0
3488 class wxGtkTreeModelChildWithPos
3489 {
3490 public:
3491 unsigned int pos;
3492 void *id;
3493 };
3494
3495 static
3496 int wxGtkTreeModelChildWithPosCmp( const void* data1, const void* data2, const void* user_data )
3497 {
3498 const wxGtkTreeModelChildWithPos* child1 = (const wxGtkTreeModelChildWithPos*) data1;
3499 const wxGtkTreeModelChildWithPos* child2 = (const wxGtkTreeModelChildWithPos*) data2;
3500 const wxDataViewCtrlInternal *internal = (const wxDataViewCtrlInternal *) user_data;
3501 int ret = internal->GetDataViewModel()->Compare( child1->id, child2->id,
3502 internal->GetSortColumn(), (internal->GetSortOrder() == GTK_SORT_DESCENDING) );
3503
3504 return ret;
3505 }
3506 #else
3507 static
wxGtkTreeModelChildPtrCmp(void *** data1,void *** data2)3508 int LINKAGEMODE wxGtkTreeModelChildPtrCmp( void*** data1, void*** data2 )
3509 {
3510 return gs_internal->GetDataViewModel()->Compare( wxDataViewItem(**data1), wxDataViewItem(**data2),
3511 gs_internal->GetSortColumn(), (gs_internal->GetSortOrder() == GTK_SORT_ASCENDING) );
3512 }
3513
3514 WX_DEFINE_ARRAY_PTR( void**, wxGtkTreeModelChildrenPtr );
3515 #endif
3516
Resort()3517 void wxGtkTreeModelNode::Resort()
3518 {
3519 size_t child_count = GetChildCount();
3520 if (child_count == 0)
3521 return;
3522
3523 size_t node_count = GetNodesCount();
3524
3525 if (child_count == 1)
3526 {
3527 if (node_count == 1)
3528 {
3529 wxGtkTreeModelNode *node = m_nodes.Item( 0 );
3530 node->Resort();
3531 }
3532 return;
3533 }
3534
3535 gint *new_order = new gint[child_count];
3536
3537 #if 1
3538 // m_children has the original *void
3539 // ptrs points to these
3540 wxGtkTreeModelChildrenPtr ptrs;
3541 size_t i;
3542 for (i = 0; i < child_count; i++)
3543 ptrs.Add( &(m_children[i]) );
3544 // Sort the ptrs
3545 gs_internal = m_internal;
3546 ptrs.Sort( &wxGtkTreeModelChildPtrCmp );
3547
3548 wxGtkTreeModelChildren temp;
3549 void** base_ptr = &(m_children[0]);
3550 // Transfer positions to new_order array and
3551 // IDs to temp
3552 for (i = 0; i < child_count; i++)
3553 {
3554 new_order[i] = ptrs[i] - base_ptr;
3555 temp.Add( *ptrs[i] );
3556 }
3557
3558 // Transfer IDs back to m_children
3559 m_children.Clear();
3560 WX_APPEND_ARRAY( temp, m_children );
3561 #endif
3562 #if 0
3563 // Too slow
3564
3565 // Build up array with IDs and original positions
3566 wxGtkTreeModelChildWithPos* temp = new wxGtkTreeModelChildWithPos[child_count];
3567 size_t i;
3568 for (i = 0; i < child_count; i++)
3569 {
3570 temp[i].pos = i;
3571 temp[i].id = m_children[i];
3572 }
3573 // Sort array keeping original positions
3574 wxQsort( temp, child_count, sizeof(wxGtkTreeModelChildWithPos),
3575 &wxGtkTreeModelChildWithPosCmp, m_internal );
3576 // Transfer positions to new_order array and
3577 // IDs to m_children
3578 m_children.Clear();
3579 for (i = 0; i < child_count; i++)
3580 {
3581 new_order[i] = temp[i].pos;
3582 m_children.Add( temp[i].id );
3583 }
3584 // Delete array
3585 delete [] temp;
3586 #endif
3587
3588 #if 0
3589 // Too slow
3590
3591 wxGtkTreeModelChildren temp;
3592 WX_APPEND_ARRAY( temp, m_children );
3593
3594 gs_internal = m_internal;
3595 m_children.Sort( &wxGtkTreeModelChildCmp );
3596
3597 unsigned int pos;
3598 for (pos = 0; pos < child_count; pos++)
3599 {
3600 void *id = m_children.Item( pos );
3601 int old_pos = temp.Index( id );
3602 new_order[pos] = old_pos;
3603 }
3604 #endif
3605
3606 GtkTreeModel *gtk_tree_model = GTK_TREE_MODEL( m_internal->GetGtkModel() );
3607
3608 GtkTreeIter iter;
3609 iter.user_data = GetItem().GetID();
3610 iter.stamp = m_internal->GetGtkModel()->stamp;
3611
3612 gtk_tree_model_rows_reordered( gtk_tree_model,
3613 wxGtkTreePath(m_internal->get_path(&iter)), &iter, new_order );
3614
3615 delete [] new_order;
3616
3617 unsigned int pos;
3618 for (pos = 0; pos < node_count; pos++)
3619 {
3620 wxGtkTreeModelNode *node = m_nodes.Item( pos );
3621 node->Resort();
3622 }
3623 }
3624
3625 //-----------------------------------------------------------------------------
3626 // wxDataViewCtrlInternal
3627 //-----------------------------------------------------------------------------
3628
wxDataViewCtrlInternal(wxDataViewCtrl * owner,wxDataViewModel * wx_model)3629 wxDataViewCtrlInternal::wxDataViewCtrlInternal( wxDataViewCtrl *owner, wxDataViewModel *wx_model )
3630 {
3631 m_owner = owner;
3632 m_wx_model = wx_model;
3633
3634 m_root = NULL;
3635 m_sort_order = GTK_SORT_ASCENDING;
3636 m_sort_column = -1;
3637 m_sort_frozen = false;
3638 m_dataview_sort_column = NULL;
3639
3640 m_dragDataObject = NULL;
3641 m_dropDataObject = NULL;
3642
3643 m_dirty = false;
3644 m_selectionFuncSet = false;
3645
3646 m_gtk_model = wxgtk_tree_model_new();
3647 m_gtk_model->internal = this;
3648
3649 m_notifier = new wxGtkDataViewModelNotifier( wx_model, this );
3650
3651 wx_model->AddNotifier( m_notifier );
3652
3653 // g_object_unref( gtk_model ); ???
3654
3655 if (!m_wx_model->IsVirtualListModel())
3656 InitTree();
3657
3658 UseModel(true);
3659 }
3660
~wxDataViewCtrlInternal()3661 wxDataViewCtrlInternal::~wxDataViewCtrlInternal()
3662 {
3663 m_wx_model->RemoveNotifier( m_notifier );
3664
3665 // remove the model from the GtkTreeView before it gets destroyed
3666 UseModel(false);
3667
3668 g_object_unref( m_gtk_model );
3669
3670 delete m_root;
3671 delete m_dragDataObject;
3672 delete m_dropDataObject;
3673 }
3674
UseModel(bool use)3675 void wxDataViewCtrlInternal::UseModel(bool use)
3676 {
3677 // Avoid any selection changed events from gtk_tree_view_set_model() call
3678 // below as they don't happen under the other platforms and can be
3679 // unexpected with the possibly fatal consequences for the user-defined
3680 // event handler.
3681 wxDataViewCtrl::SelectionEventsSuppressor noSelection(m_owner);
3682
3683 if ( use )
3684 {
3685 gtk_tree_view_set_model(GTK_TREE_VIEW(m_owner->GtkGetTreeView()),
3686 GTK_TREE_MODEL(m_gtk_model));
3687 }
3688 else // Disassociate the model from the control.
3689 {
3690 // We need to prevent wxGtkTreeCellDataFunc and other callbacks that
3691 // may be called from inside gtk_tree_view_set_model() from using the
3692 // model items not existing any longer, so change the model stamp to
3693 // indicate that it temporarily can't be used.
3694 const gint stampOrig = m_gtk_model->stamp;
3695 m_gtk_model->stamp = 0;
3696
3697 gtk_tree_view_set_model(GTK_TREE_VIEW(m_owner->GtkGetTreeView()), NULL);
3698
3699 m_gtk_model->stamp = stampOrig;
3700 }
3701 }
3702
ScheduleRefresh()3703 void wxDataViewCtrlInternal::ScheduleRefresh()
3704 {
3705 m_dirty = true;
3706 }
3707
OnInternalIdle()3708 void wxDataViewCtrlInternal::OnInternalIdle()
3709 {
3710 if (m_dirty)
3711 {
3712 GtkWidget *widget = m_owner->GtkGetTreeView();
3713 gtk_widget_queue_draw( widget );
3714 m_dirty = false;
3715 }
3716 }
3717
InitTree()3718 void wxDataViewCtrlInternal::InitTree()
3719 {
3720 wxDataViewItem item;
3721 m_root = new wxGtkTreeModelNode( NULL, item, this );
3722
3723 BuildBranch( m_root );
3724 }
3725
BuildBranch(wxGtkTreeModelNode * node)3726 void wxDataViewCtrlInternal::BuildBranch( wxGtkTreeModelNode *node )
3727 {
3728 if (node->GetChildCount() == 0)
3729 {
3730 wxDataViewItemArray children;
3731 unsigned int count = m_wx_model->GetChildren( node->GetItem(), children );
3732
3733 // Avoid sorting children multiple times when inserting many nodes,
3734 // this results in O(N^2*log(N)) complexity.
3735 if ( count > 1 )
3736 node->FreezeSort(true);
3737
3738 unsigned int pos;
3739 for (pos = 0; pos < count; pos++)
3740 {
3741 wxDataViewItem child = children[pos];
3742
3743 // Rr-enable sorting before inserting the last child if we had
3744 // disabled it above.
3745 if ( pos == count - 1 && pos != 0 )
3746 node->FreezeSort(false);
3747
3748 if (m_wx_model->IsContainer( child ))
3749 node->AddNode( new wxGtkTreeModelNode( node, child, this ) );
3750 else
3751 node->AddLeaf( child.GetID() );
3752
3753 // Don't send any events here
3754 }
3755 }
3756 }
3757
3758 // GTK+ dnd iface
3759
EnableDragSource(const wxDataFormat & format)3760 bool wxDataViewCtrlInternal::EnableDragSource( const wxDataFormat &format )
3761 {
3762 wxGtkString atom_str( gdk_atom_name( format ) );
3763 m_dragSourceTargetEntryTarget = wxCharBuffer( atom_str );
3764
3765 m_dragSourceTargetEntry.target = m_dragSourceTargetEntryTarget.data();
3766 m_dragSourceTargetEntry.flags = 0;
3767 m_dragSourceTargetEntry.info = static_cast<guint>(-1);
3768
3769 gtk_tree_view_enable_model_drag_source( GTK_TREE_VIEW(m_owner->GtkGetTreeView() ),
3770 GDK_BUTTON1_MASK, &m_dragSourceTargetEntry, 1, (GdkDragAction) GDK_ACTION_COPY );
3771
3772 return true;
3773 }
3774
EnableDropTarget(const wxDataFormat & format)3775 bool wxDataViewCtrlInternal::EnableDropTarget( const wxDataFormat &format )
3776 {
3777 wxGtkString atom_str( gdk_atom_name( format ) );
3778 m_dropTargetTargetEntryTarget = wxCharBuffer( atom_str );
3779
3780 m_dropTargetTargetEntry.target = m_dropTargetTargetEntryTarget.data();
3781 m_dropTargetTargetEntry.flags = 0;
3782 m_dropTargetTargetEntry.info = static_cast<guint>(-1);
3783
3784 gtk_tree_view_enable_model_drag_dest( GTK_TREE_VIEW(m_owner->GtkGetTreeView() ),
3785 &m_dropTargetTargetEntry, 1, (GdkDragAction) GDK_ACTION_COPY );
3786
3787 return true;
3788 }
3789
row_draggable(GtkTreeDragSource * WXUNUSED (drag_source),GtkTreePath * path)3790 gboolean wxDataViewCtrlInternal::row_draggable( GtkTreeDragSource *WXUNUSED(drag_source),
3791 GtkTreePath *path )
3792 {
3793 delete m_dragDataObject;
3794 m_dragDataObject = NULL;
3795
3796 #ifdef __WXGTK4__
3797 return false;
3798 #else
3799 wxDataViewCtrl* const dvc = GetOwner();
3800 wxDataViewItem item(dvc->GTKPathToItem(path));
3801 if ( !item )
3802 return FALSE;
3803
3804 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_BEGIN_DRAG, dvc, item);
3805 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
3806 gint x, y;
3807 gtk_widget_get_pointer(m_owner->GtkGetTreeView(), &x, &y);
3808 wxGCC_WARNING_RESTORE()
3809 event.SetPosition(x, y);
3810 if (!m_owner->HandleWindowEvent( event ))
3811 return FALSE;
3812
3813 if (!event.IsAllowed())
3814 return FALSE;
3815
3816 wxDataObject *obj = event.GetDataObject();
3817 if (!obj)
3818 return FALSE;
3819
3820 m_dragDataObject = obj;
3821
3822 return TRUE;
3823 #endif
3824 }
3825
3826 gboolean
drag_data_delete(GtkTreeDragSource * WXUNUSED (drag_source),GtkTreePath * WXUNUSED (path))3827 wxDataViewCtrlInternal::drag_data_delete(GtkTreeDragSource *WXUNUSED(drag_source),
3828 GtkTreePath *WXUNUSED(path))
3829 {
3830 return FALSE;
3831 }
3832
drag_data_get(GtkTreeDragSource * WXUNUSED (drag_source),GtkTreePath * path,GtkSelectionData * selection_data)3833 gboolean wxDataViewCtrlInternal::drag_data_get( GtkTreeDragSource *WXUNUSED(drag_source),
3834 GtkTreePath *path, GtkSelectionData *selection_data )
3835 {
3836 wxDataViewItem item(GetOwner()->GTKPathToItem(path));
3837 if ( !item )
3838 return FALSE;
3839
3840 GdkAtom target = gtk_selection_data_get_target(selection_data);
3841 if (!m_dragDataObject->IsSupported(target))
3842 return FALSE;
3843
3844 size_t size = m_dragDataObject->GetDataSize(target);
3845 if (size == 0)
3846 return FALSE;
3847
3848 void *buf = malloc( size );
3849
3850 gboolean res = FALSE;
3851 if (m_dragDataObject->GetDataHere(target, buf))
3852 {
3853 res = TRUE;
3854
3855 gtk_selection_data_set(selection_data, target,
3856 8, (const guchar*) buf, size );
3857 }
3858
3859 free( buf );
3860
3861 return res;
3862 }
3863
3864 gboolean
drag_data_received(GtkTreeDragDest * WXUNUSED (drag_dest),GtkTreePath * path,GtkSelectionData * selection_data)3865 wxDataViewCtrlInternal::drag_data_received(GtkTreeDragDest *WXUNUSED(drag_dest),
3866 GtkTreePath *path,
3867 GtkSelectionData *selection_data)
3868 {
3869 wxDataViewItem item(GetOwner()->GTKPathToItem(path));
3870
3871 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_DROP, m_owner, item);
3872 event.SetDataFormat(gtk_selection_data_get_target(selection_data));
3873 event.SetDataSize(gtk_selection_data_get_length(selection_data));
3874 event.SetDataBuffer(const_cast<guchar*>(gtk_selection_data_get_data(selection_data)));
3875 if (!m_owner->HandleWindowEvent( event ))
3876 return FALSE;
3877
3878 if (!event.IsAllowed())
3879 return FALSE;
3880
3881 return TRUE;
3882 }
3883
3884 gboolean
row_drop_possible(GtkTreeDragDest * WXUNUSED (drag_dest),GtkTreePath * path,GtkSelectionData * selection_data)3885 wxDataViewCtrlInternal::row_drop_possible(GtkTreeDragDest *WXUNUSED(drag_dest),
3886 GtkTreePath *path,
3887 GtkSelectionData *selection_data)
3888 {
3889 wxDataViewItem item(GetOwner()->GTKPathToItem(path));
3890
3891 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, m_owner, item);
3892 event.SetDataFormat(gtk_selection_data_get_target(selection_data));
3893 event.SetDataSize(gtk_selection_data_get_length(selection_data));
3894 if (!m_owner->HandleWindowEvent( event ))
3895 return FALSE;
3896
3897 if (!event.IsAllowed())
3898 return FALSE;
3899
3900 return TRUE;
3901 }
3902
3903 // notifications from wxDataViewModel
3904
Cleared()3905 bool wxDataViewCtrlInternal::Cleared()
3906 {
3907 if (m_root)
3908 {
3909 delete m_root;
3910 m_root = NULL;
3911 }
3912
3913 InitTree();
3914
3915 ScheduleRefresh();
3916
3917 return true;
3918 }
3919
Resort()3920 void wxDataViewCtrlInternal::Resort()
3921 {
3922 if (!m_wx_model->IsVirtualListModel())
3923 m_root->Resort();
3924
3925 ScheduleRefresh();
3926 }
3927
ItemAdded(const wxDataViewItem & parent,const wxDataViewItem & item)3928 bool wxDataViewCtrlInternal::ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item )
3929 {
3930 if (!m_wx_model->IsVirtualListModel())
3931 {
3932 wxGtkTreeModelNode *parent_node = FindNode( parent );
3933 wxCHECK_MSG(parent_node, false,
3934 "Did you forget a call to ItemAdded()? The parent node is unknown to the wxGtkTreeModel");
3935
3936 wxDataViewItemArray modelSiblings;
3937 m_wx_model->GetChildren(parent, modelSiblings);
3938 const int modelSiblingsSize = modelSiblings.size();
3939
3940 int posInModel = modelSiblings.Index(item, /*fromEnd=*/true);
3941 wxCHECK_MSG( posInModel != wxNOT_FOUND, false, "adding non-existent item?" );
3942
3943 const wxGtkTreeModelChildren& nodeSiblings = parent_node->GetChildren();
3944 const int nodeSiblingsSize = nodeSiblings.size();
3945
3946 int nodePos = 0;
3947
3948 if ( posInModel == modelSiblingsSize - 1 )
3949 {
3950 nodePos = nodeSiblingsSize;
3951 }
3952 else if ( modelSiblingsSize == nodeSiblingsSize + 1 )
3953 {
3954 // This is the simple case when our node tree already matches the
3955 // model and only this one item is missing.
3956 nodePos = posInModel;
3957 }
3958 else
3959 {
3960 // It's possible that a larger discrepancy between the model and
3961 // our realization exists. This can happen e.g. when adding a bunch
3962 // of items to the model and then calling ItemsAdded() just once
3963 // afterwards. In this case, we must find the right position by
3964 // looking at sibling items.
3965
3966 // append to the end if we won't find a better position:
3967 nodePos = nodeSiblingsSize;
3968
3969 for ( int nextItemPos = posInModel + 1;
3970 nextItemPos < modelSiblingsSize;
3971 nextItemPos++ )
3972 {
3973 int nextNodePos = parent_node->FindChildByItem(modelSiblings[nextItemPos]);
3974 if ( nextNodePos != wxNOT_FOUND )
3975 {
3976 nodePos = nextNodePos;
3977 break;
3978 }
3979 }
3980 }
3981
3982 if (m_wx_model->IsContainer( item ))
3983 parent_node->InsertNode( new wxGtkTreeModelNode( parent_node, item, this ), nodePos );
3984 else
3985 parent_node->InsertLeaf( item.GetID(), nodePos );
3986 }
3987
3988 ScheduleRefresh();
3989
3990 return true;
3991 }
3992
ItemDeleted(const wxDataViewItem & parent,const wxDataViewItem & item)3993 bool wxDataViewCtrlInternal::ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item )
3994 {
3995 if (!m_wx_model->IsVirtualListModel())
3996 {
3997 wxGtkTreeModelNode *parent_node = FindNode( parent );
3998 wxASSERT_MSG(parent_node,
3999 "Did you forget a call to ItemAdded()? The parent node is unknown to the wxGtkTreeModel");
4000
4001 parent_node->DeleteChild( item.GetID() );
4002 }
4003
4004 ScheduleRefresh();
4005
4006 return true;
4007 }
4008
ItemChanged(const wxDataViewItem & item)4009 bool wxDataViewCtrlInternal::ItemChanged( const wxDataViewItem &item )
4010 {
4011 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, m_owner, item);
4012 m_owner->HandleWindowEvent( event );
4013
4014 return true;
4015 }
4016
ValueChanged(const wxDataViewItem & item,unsigned int view_column)4017 bool wxDataViewCtrlInternal::ValueChanged( const wxDataViewItem &item, unsigned int view_column )
4018 {
4019 wxDataViewColumn* const column = m_owner->GetColumn(view_column);
4020 wxDataViewEvent
4021 event(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, m_owner, column, item);
4022 m_owner->HandleWindowEvent( event );
4023
4024 return true;
4025 }
4026
4027 // GTK+ model iface
4028
get_flags()4029 GtkTreeModelFlags wxDataViewCtrlInternal::get_flags()
4030 {
4031 int flags = 0;
4032
4033 if ( m_wx_model->IsListModel() )
4034 flags |= GTK_TREE_MODEL_LIST_ONLY;
4035
4036 if ( !m_wx_model->IsVirtualListModel() )
4037 flags |= GTK_TREE_MODEL_ITERS_PERSIST;
4038
4039 return GtkTreeModelFlags(flags);
4040 }
4041
get_iter(GtkTreeIter * iter,GtkTreePath * path)4042 gboolean wxDataViewCtrlInternal::get_iter( GtkTreeIter *iter, GtkTreePath *path )
4043 {
4044
4045 if (m_wx_model->IsVirtualListModel())
4046 {
4047 wxDataViewVirtualListModel *wx_model = (wxDataViewVirtualListModel*) m_wx_model;
4048
4049 unsigned int i = (unsigned int)gtk_tree_path_get_indices (path)[0];
4050
4051 if (i >= wx_model->GetCount())
4052 return FALSE;
4053
4054 iter->stamp = m_gtk_model->stamp;
4055 // user_data is just the index +1
4056 iter->user_data = wxUIntToPtr(i+1);
4057
4058 return TRUE;
4059 }
4060 else
4061 {
4062 int depth = gtk_tree_path_get_depth( path );
4063
4064 wxGtkTreeModelNode *node = m_root;
4065
4066 int i;
4067 for (i = 0; i < depth; i++)
4068 {
4069 BuildBranch( node );
4070
4071 gint pos = gtk_tree_path_get_indices (path)[i];
4072 if (pos < 0) return FALSE;
4073 if ((size_t)pos >= node->GetChildCount()) return FALSE;
4074
4075 void* id = node->GetChildren().Item( (size_t) pos );
4076
4077 if (i == depth-1)
4078 {
4079 iter->stamp = m_gtk_model->stamp;
4080 iter->user_data = id;
4081 return TRUE;
4082 }
4083
4084 size_t count = node->GetNodes().GetCount();
4085 size_t pos2;
4086 for (pos2 = 0; pos2 < count; pos2++)
4087 {
4088 wxGtkTreeModelNode *child_node = node->GetNodes().Item( pos2 );
4089 if (child_node->GetItem().GetID() == id)
4090 {
4091 node = child_node;
4092 break;
4093 }
4094 }
4095 }
4096 }
4097
4098 return FALSE;
4099 }
4100
get_path(GtkTreeIter * iter)4101 GtkTreePath *wxDataViewCtrlInternal::get_path( GtkTreeIter *iter )
4102 {
4103 // When this is called from ItemDeleted(), the item is already
4104 // deleted in the model.
4105
4106 GtkTreePath *retval = gtk_tree_path_new ();
4107
4108 if (m_wx_model->IsVirtualListModel())
4109 {
4110 // iter is root, add nothing
4111 if (!iter->user_data)
4112 return retval;
4113
4114 // user_data is just the index +1
4115 int i = ( (wxUIntPtr) iter->user_data ) -1;
4116 gtk_tree_path_append_index (retval, i);
4117 }
4118 else
4119 {
4120 void *id = iter->user_data;
4121
4122 wxGtkTreeModelNode *node = FindParentNode( iter );
4123 while (node)
4124 {
4125 int pos = node->GetChildren().Index( id );
4126
4127 gtk_tree_path_prepend_index( retval, pos );
4128
4129 id = node->GetItem().GetID();
4130 node = node->GetParent();
4131 }
4132 }
4133
4134 return retval;
4135 }
4136
iter_next(GtkTreeIter * iter)4137 gboolean wxDataViewCtrlInternal::iter_next( GtkTreeIter *iter )
4138 {
4139 if (m_wx_model->IsVirtualListModel())
4140 {
4141 wxDataViewVirtualListModel *wx_model = (wxDataViewVirtualListModel*) m_wx_model;
4142
4143 // user_data is just the index +1
4144 int n = ( (wxUIntPtr) iter->user_data ) -1;
4145
4146 if (n == -1)
4147 {
4148 iter->user_data = NULL;
4149 return FALSE;
4150 }
4151
4152 if (n >= (int) wx_model->GetCount()-1)
4153 {
4154 iter->user_data = NULL;
4155 return FALSE;
4156 }
4157
4158 // user_data is just the index +1 (+2 because we need the next)
4159 iter->user_data = wxUIntToPtr(n+2);
4160 }
4161 else
4162 {
4163 wxGtkTreeModelNode *parent = FindParentNode( iter );
4164 if( parent == NULL )
4165 {
4166 iter->user_data = NULL;
4167 return FALSE;
4168 }
4169
4170 int pos = parent->GetChildren().Index( iter->user_data );
4171
4172 if (pos == (int) parent->GetChildCount()-1)
4173 {
4174 iter->user_data = NULL;
4175 return FALSE;
4176 }
4177
4178 iter->user_data = parent->GetChildren().Item( pos+1 );
4179 }
4180
4181 return TRUE;
4182 }
4183
iter_children(GtkTreeIter * iter,GtkTreeIter * parent)4184 gboolean wxDataViewCtrlInternal::iter_children( GtkTreeIter *iter, GtkTreeIter *parent )
4185 {
4186 if (m_wx_model->IsVirtualListModel())
4187 {
4188 // this is a list, nodes have no children
4189 if (parent)
4190 return FALSE;
4191
4192 iter->stamp = m_gtk_model->stamp;
4193 iter->user_data = (gpointer) 1;
4194
4195 return TRUE;
4196 }
4197 else
4198 {
4199 wxDataViewItem item;
4200 if (parent)
4201 item = wxDataViewItem( (void*) parent->user_data );
4202
4203 if (!m_wx_model->IsContainer( item ))
4204 return FALSE;
4205
4206 wxGtkTreeModelNode *parent_node = FindNode( parent );
4207 wxCHECK_MSG(parent_node, FALSE,
4208 "Did you forget a call to ItemAdded()? The parent node is unknown to the wxGtkTreeModel");
4209
4210 BuildBranch( parent_node );
4211
4212 if (parent_node->GetChildCount() == 0)
4213 return FALSE;
4214
4215 iter->stamp = m_gtk_model->stamp;
4216 iter->user_data = (gpointer) parent_node->GetChildren().Item( 0 );
4217 }
4218
4219 return TRUE;
4220 }
4221
iter_has_child(GtkTreeIter * iter)4222 gboolean wxDataViewCtrlInternal::iter_has_child( GtkTreeIter *iter )
4223 {
4224 if (m_wx_model->IsVirtualListModel())
4225 {
4226 wxDataViewVirtualListModel *wx_model = (wxDataViewVirtualListModel*) m_wx_model;
4227
4228 if (iter == NULL)
4229 return (wx_model->GetCount() > 0);
4230
4231 // this is a list, nodes have no children
4232 return FALSE;
4233 }
4234 else
4235 {
4236 if (iter == NULL)
4237 return (m_root->GetChildCount() > 0);
4238
4239 wxDataViewItem item( (void*) iter->user_data );
4240
4241 bool is_container = m_wx_model->IsContainer( item );
4242
4243 if (!is_container)
4244 return FALSE;
4245
4246 wxGtkTreeModelNode *node = FindNode( iter );
4247 wxCHECK_MSG(node, FALSE,
4248 "Did you forget a call to ItemAdded()? The iterator is unknown to the wxGtkTreeModel");
4249
4250 BuildBranch( node );
4251
4252 return (node->GetChildCount() > 0);
4253 }
4254 }
4255
iter_n_children(GtkTreeIter * iter)4256 gint wxDataViewCtrlInternal::iter_n_children( GtkTreeIter *iter )
4257 {
4258 if (m_wx_model->IsVirtualListModel())
4259 {
4260 wxDataViewVirtualListModel *wx_model = (wxDataViewVirtualListModel*) m_wx_model;
4261
4262 if (iter == NULL)
4263 return (gint) wx_model->GetCount();
4264
4265 return 0;
4266 }
4267 else
4268 {
4269 if (iter == NULL)
4270 return m_root->GetChildCount();
4271
4272 wxDataViewItem item( (void*) iter->user_data );
4273
4274 if (!m_wx_model->IsContainer( item ))
4275 return 0;
4276
4277 wxGtkTreeModelNode *parent_node = FindNode( iter );
4278 wxCHECK_MSG(parent_node, FALSE,
4279 "Did you forget a call to ItemAdded()? The parent node is unknown to the wxGtkTreeModel");
4280
4281 BuildBranch( parent_node );
4282
4283 return parent_node->GetChildCount();
4284 }
4285 }
4286
iter_nth_child(GtkTreeIter * iter,GtkTreeIter * parent,gint n)4287 gboolean wxDataViewCtrlInternal::iter_nth_child( GtkTreeIter *iter, GtkTreeIter *parent, gint n )
4288 {
4289 if (m_wx_model->IsVirtualListModel())
4290 {
4291 wxDataViewVirtualListModel *wx_model = (wxDataViewVirtualListModel*) m_wx_model;
4292
4293 if (parent)
4294 return FALSE;
4295
4296 if (n < 0)
4297 return FALSE;
4298
4299 if (n >= (gint) wx_model->GetCount())
4300 return FALSE;
4301
4302 iter->stamp = m_gtk_model->stamp;
4303 // user_data is just the index +1
4304 iter->user_data = wxUIntToPtr(n+1);
4305
4306 return TRUE;
4307 }
4308 else
4309 {
4310 void* id = NULL;
4311 if (parent) id = (void*) parent->user_data;
4312 wxDataViewItem item( id );
4313
4314 if (!m_wx_model->IsContainer( item ))
4315 return FALSE;
4316
4317 wxGtkTreeModelNode *parent_node = FindNode( parent );
4318 wxCHECK_MSG(parent_node, FALSE,
4319 "Did you forget a call to ItemAdded()? The parent node is unknown to the wxGtkTreeModel");
4320
4321 BuildBranch( parent_node );
4322
4323 iter->stamp = m_gtk_model->stamp;
4324 iter->user_data = parent_node->GetChildren().Item( n );
4325
4326 return TRUE;
4327 }
4328 }
4329
iter_parent(GtkTreeIter * iter,GtkTreeIter * child)4330 gboolean wxDataViewCtrlInternal::iter_parent( GtkTreeIter *iter, GtkTreeIter *child )
4331 {
4332 if (m_wx_model->IsVirtualListModel())
4333 {
4334 return FALSE;
4335 }
4336 else
4337 {
4338 wxGtkTreeModelNode *node = FindParentNode( child );
4339 if (!node)
4340 return FALSE;
4341
4342 iter->stamp = m_gtk_model->stamp;
4343 iter->user_data = (gpointer) node->GetItem().GetID();
4344
4345 return TRUE;
4346 }
4347 }
4348
4349 // item can be deleted already in the model
GetIndexOf(const wxDataViewItem & parent,const wxDataViewItem & item)4350 int wxDataViewCtrlInternal::GetIndexOf( const wxDataViewItem &parent, const wxDataViewItem &item )
4351 {
4352 if (m_wx_model->IsVirtualListModel())
4353 {
4354 return wxPtrToUInt(item.GetID()) - 1;
4355 }
4356 else
4357 {
4358 wxGtkTreeModelNode *parent_node = FindNode( parent );
4359 wxGtkTreeModelChildren &children = parent_node->GetChildren();
4360 size_t j;
4361 for (j = 0; j < children.GetCount(); j++)
4362 {
4363 if (children[j] == item.GetID())
4364 return j;
4365 }
4366 }
4367 return -1;
4368 }
4369
4370
4371 static wxGtkTreeModelNode*
wxDataViewCtrlInternal_FindNode(wxDataViewModel * model,wxGtkTreeModelNode * treeNode,const wxDataViewItem & item)4372 wxDataViewCtrlInternal_FindNode( wxDataViewModel * model, wxGtkTreeModelNode *treeNode, const wxDataViewItem &item )
4373 {
4374 if( model == NULL )
4375 return NULL;
4376
4377 ItemList list;
4378 list.DeleteContents( true );
4379 wxDataViewItem it( item );
4380
4381 while( it.IsOk() )
4382 {
4383 wxDataViewItem * pItem = new wxDataViewItem( it );
4384 list.Insert( pItem );
4385 it = model->GetParent( it );
4386 }
4387
4388 wxGtkTreeModelNode * node = treeNode;
4389 for( ItemList::compatibility_iterator n = list.GetFirst(); n; n = n->GetNext() )
4390 {
4391 if( node && node->GetNodes().GetCount() != 0 )
4392 {
4393 int len = node->GetNodes().GetCount();
4394 wxGtkTreeModelNodes &nodes = node->GetNodes();
4395 int j = 0;
4396 for( ; j < len; j ++)
4397 {
4398 if( nodes[j]->GetItem() == *(n->GetData()))
4399 {
4400 node = nodes[j];
4401 break;
4402 }
4403 }
4404
4405 if( j == len )
4406 {
4407 return NULL;
4408 }
4409 }
4410 else
4411 return NULL;
4412 }
4413 return node;
4414
4415 }
4416
FindNode(GtkTreeIter * iter)4417 wxGtkTreeModelNode *wxDataViewCtrlInternal::FindNode( GtkTreeIter *iter )
4418 {
4419 if (!iter)
4420 return m_root;
4421
4422 wxDataViewItem item( (void*) iter->user_data );
4423 if (!item.IsOk())
4424 return m_root;
4425
4426 wxGtkTreeModelNode *result = wxDataViewCtrlInternal_FindNode( m_wx_model, m_root, item );
4427
4428 /*
4429 if (!result)
4430 {
4431 wxLogDebug( "Not found %p", iter->user_data );
4432 char *crash = NULL;
4433 *crash = 0;
4434 }
4435 // TODO: remove this code
4436 */
4437
4438 return result;
4439 }
4440
FindNode(const wxDataViewItem & item)4441 wxGtkTreeModelNode *wxDataViewCtrlInternal::FindNode( const wxDataViewItem &item )
4442 {
4443 if (!item.IsOk())
4444 return m_root;
4445
4446 wxGtkTreeModelNode *result = wxDataViewCtrlInternal_FindNode( m_wx_model, m_root, item );
4447
4448 /*
4449 if (!result)
4450 {
4451 wxLogDebug( "Not found %p", item.GetID() );
4452 char *crash = NULL;
4453 *crash = 0;
4454 }
4455 // TODO: remove this code
4456 */
4457
4458 return result;
4459 }
4460
4461 static wxGtkTreeModelNode*
wxDataViewCtrlInternal_FindParentNode(wxDataViewModel * model,wxGtkTreeModelNode * treeNode,const wxDataViewItem & item)4462 wxDataViewCtrlInternal_FindParentNode( wxDataViewModel * model, wxGtkTreeModelNode *treeNode, const wxDataViewItem &item )
4463 {
4464 if( model == NULL )
4465 return NULL;
4466
4467 ItemList list;
4468 list.DeleteContents( true );
4469 if( !item.IsOk() )
4470 return NULL;
4471
4472 wxDataViewItem it( model->GetParent( item ) );
4473 while( it.IsOk() )
4474 {
4475 wxDataViewItem * pItem = new wxDataViewItem( it );
4476 list.Insert( pItem );
4477 it = model->GetParent( it );
4478 }
4479
4480 wxGtkTreeModelNode * node = treeNode;
4481 for( ItemList::compatibility_iterator n = list.GetFirst(); n; n = n->GetNext() )
4482 {
4483 if( node && node->GetNodes().GetCount() != 0 )
4484 {
4485 int len = node->GetNodes().GetCount();
4486 wxGtkTreeModelNodes nodes = node->GetNodes();
4487 int j = 0;
4488 for( ; j < len; j ++)
4489 {
4490 if( nodes[j]->GetItem() == *(n->GetData()))
4491 {
4492 node = nodes[j];
4493 break;
4494 }
4495 }
4496
4497 if( j == len )
4498 {
4499 return NULL;
4500 }
4501 }
4502 else
4503 return NULL;
4504 }
4505 //Examine whether the node is item's parent node
4506 int len = node->GetChildCount();
4507 for( int i = 0; i < len ; i ++ )
4508 {
4509 if( node->GetChildren().Item( i ) == item.GetID() )
4510 return node;
4511 }
4512 return NULL;
4513 }
4514
FindParentNode(GtkTreeIter * iter)4515 wxGtkTreeModelNode *wxDataViewCtrlInternal::FindParentNode( GtkTreeIter *iter )
4516 {
4517 if (!iter)
4518 return NULL;
4519
4520 wxDataViewItem item( (void*) iter->user_data );
4521 if (!item.IsOk())
4522 return NULL;
4523
4524 return wxDataViewCtrlInternal_FindParentNode( m_wx_model, m_root, item );
4525 }
4526
FindParentNode(const wxDataViewItem & item)4527 wxGtkTreeModelNode *wxDataViewCtrlInternal::FindParentNode( const wxDataViewItem &item )
4528 {
4529 if (!item.IsOk())
4530 return NULL;
4531
4532 return wxDataViewCtrlInternal_FindParentNode( m_wx_model, m_root, item );
4533 }
4534
4535 //-----------------------------------------------------------------------------
4536 // wxDataViewCtrl signal callbacks
4537 //-----------------------------------------------------------------------------
4538
4539 static void
wxdataview_selection_changed_callback(GtkTreeSelection * WXUNUSED (selection),wxDataViewCtrl * dv)4540 wxdataview_selection_changed_callback( GtkTreeSelection* WXUNUSED(selection), wxDataViewCtrl *dv )
4541 {
4542 if (!gtk_widget_get_realized(dv->m_widget))
4543 return;
4544
4545 wxDataViewEvent
4546 event(wxEVT_DATAVIEW_SELECTION_CHANGED, dv, dv->GetSelection());
4547 dv->HandleWindowEvent( event );
4548 }
4549
4550 static void
wxdataview_row_activated_callback(GtkTreeView * WXUNUSED (treeview),GtkTreePath * path,GtkTreeViewColumn * column,wxDataViewCtrl * dv)4551 wxdataview_row_activated_callback( GtkTreeView* WXUNUSED(treeview), GtkTreePath *path,
4552 GtkTreeViewColumn *column, wxDataViewCtrl *dv )
4553 {
4554 wxDataViewItem item(dv->GTKPathToItem(path));
4555 wxDataViewEvent
4556 event(wxEVT_DATAVIEW_ITEM_ACTIVATED, dv, dv->GTKColumnToWX(column), item);
4557 dv->HandleWindowEvent( event );
4558 }
4559
4560 static gboolean
wxdataview_test_expand_row_callback(GtkTreeView * WXUNUSED (treeview),GtkTreeIter * iter,GtkTreePath * WXUNUSED (path),wxDataViewCtrl * dv)4561 wxdataview_test_expand_row_callback( GtkTreeView* WXUNUSED(treeview), GtkTreeIter* iter,
4562 GtkTreePath *WXUNUSED(path), wxDataViewCtrl *dv )
4563 {
4564 wxDataViewItem item( (void*) iter->user_data );
4565 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_EXPANDING, dv, item);
4566 dv->HandleWindowEvent( event );
4567
4568 return !event.IsAllowed();
4569 }
4570
4571 static void
wxdataview_row_expanded_callback(GtkTreeView * WXUNUSED (treeview),GtkTreeIter * iter,GtkTreePath * WXUNUSED (path),wxDataViewCtrl * dv)4572 wxdataview_row_expanded_callback( GtkTreeView* WXUNUSED(treeview), GtkTreeIter* iter,
4573 GtkTreePath *WXUNUSED(path), wxDataViewCtrl *dv )
4574 {
4575 wxDataViewItem item( (void*) iter->user_data );
4576 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_EXPANDED, dv, item);
4577 dv->HandleWindowEvent( event );
4578 }
4579
4580 static gboolean
wxdataview_test_collapse_row_callback(GtkTreeView * WXUNUSED (treeview),GtkTreeIter * iter,GtkTreePath * WXUNUSED (path),wxDataViewCtrl * dv)4581 wxdataview_test_collapse_row_callback( GtkTreeView* WXUNUSED(treeview), GtkTreeIter* iter,
4582 GtkTreePath *WXUNUSED(path), wxDataViewCtrl *dv )
4583 {
4584 wxDataViewItem item( (void*) iter->user_data );
4585 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_COLLAPSING, dv, item);
4586 dv->HandleWindowEvent( event );
4587
4588 return !event.IsAllowed();
4589 }
4590
4591 static void
wxdataview_row_collapsed_callback(GtkTreeView * WXUNUSED (treeview),GtkTreeIter * iter,GtkTreePath * WXUNUSED (path),wxDataViewCtrl * dv)4592 wxdataview_row_collapsed_callback( GtkTreeView* WXUNUSED(treeview), GtkTreeIter* iter,
4593 GtkTreePath *WXUNUSED(path), wxDataViewCtrl *dv )
4594 {
4595 wxDataViewItem item( (void*) iter->user_data );
4596 wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_COLLAPSED, dv, item);
4597 dv->HandleWindowEvent( event );
4598 }
4599
4600 //-----------------------------------------------------------------------------
4601 // wxDataViewCtrl
4602 //-----------------------------------------------------------------------------
4603
AddChildGTK(wxWindowGTK *)4604 void wxDataViewCtrl::AddChildGTK(wxWindowGTK*)
4605 {
4606 // this is for cell editing controls, which will be
4607 // made children of the GtkTreeView automatically
4608 }
4609
4610
4611 //-----------------------------------------------------------------------------
4612 // "motion_notify_event"
4613 //-----------------------------------------------------------------------------
4614
4615 static gboolean
gtk_dataview_motion_notify_callback(GtkWidget * WXUNUSED (widget),GdkEventMotion * gdk_event,wxDataViewCtrl * dv)4616 gtk_dataview_motion_notify_callback( GtkWidget *WXUNUSED(widget),
4617 GdkEventMotion *gdk_event,
4618 wxDataViewCtrl *dv )
4619 {
4620 int x = gdk_event->x;
4621 int y = gdk_event->y;
4622 if (gdk_event->is_hint)
4623 {
4624 #ifdef __WXGTK3__
4625 gdk_window_get_device_position(gdk_event->window, gdk_event->device, &x, &y, NULL);
4626 #else
4627 gdk_window_get_pointer(gdk_event->window, &x, &y, NULL);
4628 #endif
4629 }
4630
4631 wxGtkTreePath path;
4632 GtkTreeViewColumn *column = NULL;
4633 gint cell_x = 0;
4634 gint cell_y = 0;
4635 if (gtk_tree_view_get_path_at_pos(
4636 GTK_TREE_VIEW(dv->GtkGetTreeView()),
4637 x, y,
4638 path.ByRef(),
4639 &column,
4640 &cell_x,
4641 &cell_y))
4642 {
4643 if (path)
4644 {
4645 GtkTreeIter iter;
4646 dv->GtkGetInternal()->get_iter( &iter, path );
4647 }
4648 }
4649
4650
4651 return FALSE;
4652 }
4653
4654 //-----------------------------------------------------------------------------
4655 // "button_press_event"
4656 //-----------------------------------------------------------------------------
4657
4658 static gboolean
gtk_dataview_button_press_callback(GtkWidget * WXUNUSED (widget),GdkEventButton * gdk_event,wxDataViewCtrl * dv)4659 gtk_dataview_button_press_callback( GtkWidget *WXUNUSED(widget),
4660 GdkEventButton *gdk_event,
4661 wxDataViewCtrl *dv )
4662 {
4663 if ((gdk_event->button == 3) && (gdk_event->type == GDK_BUTTON_PRESS))
4664 {
4665 wxGtkTreePath path;
4666 GtkTreeViewColumn *column = NULL;
4667 gint cell_x = 0;
4668 gint cell_y = 0;
4669 gtk_tree_view_get_path_at_pos
4670 (
4671 GTK_TREE_VIEW(dv->GtkGetTreeView()),
4672 (int) gdk_event->x, (int) gdk_event->y,
4673 path.ByRef(),
4674 &column,
4675 &cell_x,
4676 &cell_y
4677 );
4678
4679 // If the right click is on an item that isn't selected, select it, as is
4680 // commonly done. Do not do it if the item under mouse is already selected,
4681 // because it could be a part of multi-item selection.
4682 if ( path )
4683 {
4684 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dv->GtkGetTreeView()));
4685 if ( !gtk_tree_selection_path_is_selected(selection, path) )
4686 {
4687 gtk_tree_selection_unselect_all(selection);
4688 gtk_tree_selection_select_path(selection, path);
4689 }
4690 }
4691
4692 wxDataViewEvent
4693 event(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, dv, dv->GTKPathToItem(path));
4694 return dv->HandleWindowEvent( event );
4695 }
4696
4697 return FALSE;
4698 }
4699
4700 wxIMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase);
4701
~wxDataViewCtrl()4702 wxDataViewCtrl::~wxDataViewCtrl()
4703 {
4704 // Stop editing before destroying the control to remove any event handlers
4705 // which are added when editing started: if we didn't do this, the base
4706 // class dtor would assert as it checks for any leftover handlers.
4707 if ( m_treeview )
4708 {
4709 GtkTreeViewColumn *col;
4710 gtk_tree_view_get_cursor(GTK_TREE_VIEW(m_treeview), NULL, &col);
4711
4712 wxDataViewColumn * const wxcol = GTKColumnToWX(col);
4713 if ( wxcol )
4714 {
4715 // This won't do anything if we're not editing it
4716 wxcol->GetRenderer()->CancelEditing();
4717 }
4718
4719 GTKDisconnect(m_treeview);
4720 GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(m_treeview));
4721 if (selection)
4722 GTKDisconnect(selection);
4723 }
4724
4725 m_cols.Clear();
4726
4727 delete m_internal;
4728 }
4729
Init()4730 void wxDataViewCtrl::Init()
4731 {
4732 m_treeview = NULL;
4733 m_internal = NULL;
4734
4735 m_cols.DeleteContents( true );
4736
4737 m_uniformRowHeight = -1;
4738 }
4739
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)4740 bool wxDataViewCtrl::Create(wxWindow *parent,
4741 wxWindowID id,
4742 const wxPoint& pos,
4743 const wxSize& size,
4744 long style,
4745 const wxValidator& validator,
4746 const wxString& name)
4747 {
4748 if (!PreCreation( parent, pos, size ) ||
4749 !CreateBase( parent, id, pos, size, style, validator, name ))
4750 {
4751 wxFAIL_MSG( wxT("wxDataViewCtrl creation failed") );
4752 return false;
4753 }
4754
4755 m_widget = gtk_scrolled_window_new (NULL, NULL);
4756 g_object_ref(m_widget);
4757
4758 GTKScrolledWindowSetBorder(m_widget, style);
4759
4760 m_treeview = gtk_tree_view_new();
4761 gtk_container_add (GTK_CONTAINER (m_widget), m_treeview);
4762
4763 m_focusWidget = GTK_WIDGET(m_treeview);
4764
4765 bool fixed = (style & wxDV_VARIABLE_LINE_HEIGHT) == 0;
4766 gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW(m_treeview), fixed );
4767
4768 if (style & wxDV_MULTIPLE)
4769 {
4770 GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
4771 gtk_tree_selection_set_mode( selection, GTK_SELECTION_MULTIPLE );
4772 }
4773
4774 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW(m_treeview), (style & wxDV_NO_HEADER) == 0 );
4775
4776 #ifdef __WXGTK210__
4777 if (wx_is_at_least_gtk2(10))
4778 {
4779 GtkTreeViewGridLines grid = GTK_TREE_VIEW_GRID_LINES_NONE;
4780
4781 if ((style & wxDV_HORIZ_RULES) != 0 &&
4782 (style & wxDV_VERT_RULES) != 0)
4783 grid = GTK_TREE_VIEW_GRID_LINES_BOTH;
4784 else if (style & wxDV_VERT_RULES)
4785 grid = GTK_TREE_VIEW_GRID_LINES_VERTICAL;
4786 else if (style & wxDV_HORIZ_RULES)
4787 grid = GTK_TREE_VIEW_GRID_LINES_HORIZONTAL;
4788
4789 if (grid != GTK_TREE_VIEW_GRID_LINES_NONE)
4790 gtk_tree_view_set_grid_lines( GTK_TREE_VIEW(m_treeview), grid );
4791 }
4792 #endif
4793 #ifndef __WXGTK4__
4794 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
4795 gtk_tree_view_set_rules_hint( GTK_TREE_VIEW(m_treeview), (style & wxDV_ROW_LINES) != 0 );
4796 wxGCC_WARNING_RESTORE()
4797 #endif
4798
4799 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (m_widget),
4800 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
4801 gtk_widget_show (m_treeview);
4802
4803 m_parent->DoAddChild( this );
4804
4805 PostCreation(size);
4806
4807 g_signal_connect_after(gtk_tree_view_get_selection(GTK_TREE_VIEW(m_treeview)),
4808 "changed", G_CALLBACK(wxdataview_selection_changed_callback), this);
4809
4810 g_signal_connect_after (m_treeview, "row-activated",
4811 G_CALLBACK (wxdataview_row_activated_callback), this);
4812
4813 g_signal_connect (m_treeview, "test-collapse-row",
4814 G_CALLBACK (wxdataview_test_collapse_row_callback), this);
4815
4816 g_signal_connect_after (m_treeview, "row-collapsed",
4817 G_CALLBACK (wxdataview_row_collapsed_callback), this);
4818
4819 g_signal_connect (m_treeview, "test-expand-row",
4820 G_CALLBACK (wxdataview_test_expand_row_callback), this);
4821
4822 g_signal_connect_after (m_treeview, "row-expanded",
4823 G_CALLBACK (wxdataview_row_expanded_callback), this);
4824
4825 g_signal_connect (m_treeview, "motion_notify_event",
4826 G_CALLBACK (gtk_dataview_motion_notify_callback), this);
4827
4828 g_signal_connect (m_treeview, "button_press_event",
4829 G_CALLBACK (gtk_dataview_button_press_callback), this);
4830
4831 return true;
4832 }
4833
GTKGetWindow(wxArrayGdkWindows &) const4834 GdkWindow* wxDataViewCtrl::GTKGetWindow(wxArrayGdkWindows& /* windows */) const
4835 {
4836 return gtk_tree_view_get_bin_window(GTK_TREE_VIEW(m_treeview));
4837 }
4838
GTKPathToItem(GtkTreePath * path) const4839 wxDataViewItem wxDataViewCtrl::GTKPathToItem(GtkTreePath *path) const
4840 {
4841 GtkTreeIter iter;
4842 return wxDataViewItem(path && m_internal->get_iter(&iter, path)
4843 ? iter.user_data
4844 : NULL);
4845 }
4846
OnInternalIdle()4847 void wxDataViewCtrl::OnInternalIdle()
4848 {
4849 wxWindow::OnInternalIdle();
4850
4851 if ( !m_internal )
4852 return;
4853
4854 m_internal->OnInternalIdle();
4855
4856 unsigned int cols = GetColumnCount();
4857 unsigned int i;
4858 for (i = 0; i < cols; i++)
4859 {
4860 wxDataViewColumn *col = GetColumn( i );
4861 col->OnInternalIdle();
4862 }
4863
4864 if (m_ensureVisibleDefered.IsOk())
4865 {
4866 ExpandAncestors(m_ensureVisibleDefered);
4867 GtkTreeIter iter;
4868 iter.user_data = (gpointer) m_ensureVisibleDefered.GetID();
4869 wxGtkTreePath path(m_internal->get_path( &iter ));
4870 gtk_tree_view_scroll_to_cell( GTK_TREE_VIEW(m_treeview), path, NULL, false, 0.0, 0.0 );
4871 m_ensureVisibleDefered = wxDataViewItem(0);
4872 }
4873 }
4874
AssociateModel(wxDataViewModel * model)4875 bool wxDataViewCtrl::AssociateModel( wxDataViewModel *model )
4876 {
4877 wxDELETE(m_internal);
4878
4879 if (!wxDataViewCtrlBase::AssociateModel( model ))
4880 return false;
4881
4882 if ( model )
4883 m_internal = new wxDataViewCtrlInternal( this, model );
4884
4885 return true;
4886 }
4887
EnableDragSource(const wxDataFormat & format)4888 bool wxDataViewCtrl::EnableDragSource( const wxDataFormat &format )
4889 {
4890 wxCHECK_MSG( m_internal, false, "model must be associated before calling EnableDragSource" );
4891 return m_internal->EnableDragSource( format );
4892 }
4893
EnableDropTarget(const wxDataFormat & format)4894 bool wxDataViewCtrl::EnableDropTarget( const wxDataFormat &format )
4895 {
4896 wxCHECK_MSG( m_internal, false, "model must be associated before calling EnableDragTarget" );
4897 return m_internal->EnableDropTarget( format );
4898 }
4899
AppendColumn(wxDataViewColumn * col)4900 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
4901 {
4902 if (!wxDataViewCtrlBase::AppendColumn(col))
4903 return false;
4904
4905 m_cols.Append( col );
4906
4907 if (gtk_tree_view_column_get_sizing( GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()) ) !=
4908 GTK_TREE_VIEW_COLUMN_FIXED)
4909 {
4910 gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW(m_treeview), FALSE );
4911 }
4912
4913 gtk_tree_view_append_column( GTK_TREE_VIEW(m_treeview),
4914 GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()) );
4915
4916 return true;
4917 }
4918
PrependColumn(wxDataViewColumn * col)4919 bool wxDataViewCtrl::PrependColumn( wxDataViewColumn *col )
4920 {
4921 if (!wxDataViewCtrlBase::PrependColumn(col))
4922 return false;
4923
4924 m_cols.Insert( col );
4925
4926 if (gtk_tree_view_column_get_sizing( GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()) ) !=
4927 GTK_TREE_VIEW_COLUMN_FIXED)
4928 {
4929 gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW(m_treeview), FALSE );
4930 }
4931
4932 gtk_tree_view_insert_column( GTK_TREE_VIEW(m_treeview),
4933 GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()), 0 );
4934
4935 return true;
4936 }
4937
InsertColumn(unsigned int pos,wxDataViewColumn * col)4938 bool wxDataViewCtrl::InsertColumn( unsigned int pos, wxDataViewColumn *col )
4939 {
4940 if (!wxDataViewCtrlBase::InsertColumn(pos,col))
4941 return false;
4942
4943 m_cols.Insert( pos, col );
4944
4945 if (gtk_tree_view_column_get_sizing( GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()) ) !=
4946 GTK_TREE_VIEW_COLUMN_FIXED)
4947 {
4948 gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW(m_treeview), FALSE );
4949 }
4950
4951 gtk_tree_view_insert_column( GTK_TREE_VIEW(m_treeview),
4952 GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()), pos );
4953
4954 return true;
4955 }
4956
GetColumnCount() const4957 unsigned int wxDataViewCtrl::GetColumnCount() const
4958 {
4959 return m_cols.GetCount();
4960 }
4961
GTKColumnToWX(GtkTreeViewColumn * gtk_col) const4962 wxDataViewColumn* wxDataViewCtrl::GTKColumnToWX(GtkTreeViewColumn *gtk_col) const
4963 {
4964 if ( !gtk_col )
4965 return NULL;
4966
4967 wxDataViewColumnList::const_iterator iter;
4968 for (iter = m_cols.begin(); iter != m_cols.end(); ++iter)
4969 {
4970 wxDataViewColumn *col = *iter;
4971 if (GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()) == gtk_col)
4972 {
4973 return col;
4974 }
4975 }
4976
4977 wxFAIL_MSG( "No matching column?" );
4978
4979 return NULL;
4980 }
4981
GetColumn(unsigned int pos) const4982 wxDataViewColumn* wxDataViewCtrl::GetColumn( unsigned int pos ) const
4983 {
4984 GtkTreeViewColumn *gtk_col = gtk_tree_view_get_column( GTK_TREE_VIEW(m_treeview), pos );
4985
4986 return GTKColumnToWX(gtk_col);
4987 }
4988
DeleteColumn(wxDataViewColumn * column)4989 bool wxDataViewCtrl::DeleteColumn( wxDataViewColumn *column )
4990 {
4991 gtk_tree_view_remove_column( GTK_TREE_VIEW(m_treeview),
4992 GTK_TREE_VIEW_COLUMN(column->GetGtkHandle()) );
4993
4994 m_cols.DeleteObject( column );
4995
4996 return true;
4997 }
4998
ClearColumns()4999 bool wxDataViewCtrl::ClearColumns()
5000 {
5001 wxDataViewColumnList::iterator iter;
5002 for (iter = m_cols.begin(); iter != m_cols.end(); ++iter)
5003 {
5004 wxDataViewColumn *col = *iter;
5005 gtk_tree_view_remove_column( GTK_TREE_VIEW(m_treeview),
5006 GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()) );
5007 }
5008
5009 m_cols.Clear();
5010
5011 return true;
5012 }
5013
GetColumnPosition(const wxDataViewColumn * column) const5014 int wxDataViewCtrl::GetColumnPosition( const wxDataViewColumn *column ) const
5015 {
5016 GtkTreeViewColumn *gtk_column = GTK_TREE_VIEW_COLUMN(column->GetGtkHandle());
5017
5018 wxGtkList list(gtk_tree_view_get_columns(GTK_TREE_VIEW(m_treeview)));
5019
5020 return g_list_index( list, (gconstpointer) gtk_column );
5021 }
5022
GetSortingColumn() const5023 wxDataViewColumn *wxDataViewCtrl::GetSortingColumn() const
5024 {
5025 wxCHECK_MSG( m_internal, NULL, "model must be associated before calling GetSortingColumn" );
5026 return m_internal->GetDataViewSortColumn();
5027 }
5028
DoExpand(const wxDataViewItem & item,bool expandChildren)5029 void wxDataViewCtrl::DoExpand( const wxDataViewItem & item, bool expandChildren )
5030 {
5031 GtkTreeIter iter;
5032 iter.user_data = item.GetID();
5033 wxGtkTreePath path(m_internal->get_path( &iter ));
5034 gtk_tree_view_expand_row( GTK_TREE_VIEW(m_treeview), path,
5035 expandChildren ? TRUE : FALSE );
5036 }
5037
Collapse(const wxDataViewItem & item)5038 void wxDataViewCtrl::Collapse( const wxDataViewItem & item )
5039 {
5040 wxCHECK_RET( m_internal, "model must be associated before calling Collapse" );
5041
5042 GtkTreeIter iter;
5043 iter.user_data = item.GetID();
5044 wxGtkTreePath path(m_internal->get_path( &iter ));
5045 gtk_tree_view_collapse_row( GTK_TREE_VIEW(m_treeview), path );
5046 }
5047
IsExpanded(const wxDataViewItem & item) const5048 bool wxDataViewCtrl::IsExpanded( const wxDataViewItem & item ) const
5049 {
5050 wxCHECK_MSG( m_internal, false, "model must be associated before calling IsExpanded" );
5051
5052 GtkTreeIter iter;
5053 iter.user_data = item.GetID();
5054 wxGtkTreePath path(m_internal->get_path( &iter ));
5055 return gtk_tree_view_row_expanded( GTK_TREE_VIEW(m_treeview), path ) != 0;
5056 }
5057
DoGetCurrentItem() const5058 wxDataViewItem wxDataViewCtrl::DoGetCurrentItem() const
5059 {
5060 // The tree doesn't have any current item if it hadn't been created yet but
5061 // it's arguably not an error to call this function in this case so just
5062 // return an invalid item without asserting.
5063 if ( !m_treeview || !m_internal )
5064 return wxDataViewItem();
5065
5066 wxGtkTreePath path;
5067 gtk_tree_view_get_cursor(GTK_TREE_VIEW(m_treeview), path.ByRef(), NULL);
5068
5069 return GTKPathToItem(path);
5070 }
5071
DoSetCurrentItem(const wxDataViewItem & item)5072 void wxDataViewCtrl::DoSetCurrentItem(const wxDataViewItem& item)
5073 {
5074 wxCHECK_RET( m_treeview,
5075 "Current item can't be set before creating the control." );
5076 wxCHECK_RET( m_internal, "model must be associated before setting current item" );
5077
5078 // We need to make sure the model knows about this item or the path would
5079 // be invalid and gtk_tree_view_set_cursor() would silently do nothing.
5080 ExpandAncestors(item);
5081
5082 // We also need to preserve the existing selection from changing.
5083 // Unfortunately the only way to do it seems to use our own selection
5084 // function and forbid any selection changes during set cursor call.
5085 wxGtkTreeSelectionLock
5086 lock(gtk_tree_view_get_selection(GTK_TREE_VIEW(m_treeview)),
5087 m_internal->m_selectionFuncSet);
5088
5089 // Do move the cursor now.
5090 GtkTreeIter iter;
5091 iter.user_data = item.GetID();
5092 wxGtkTreePath path(m_internal->get_path( &iter ));
5093
5094 gtk_tree_view_set_cursor(GTK_TREE_VIEW(m_treeview), path, NULL, FALSE);
5095 }
5096
GetTopItem() const5097 wxDataViewItem wxDataViewCtrl::GetTopItem() const
5098 {
5099 #if GTK_CHECK_VERSION(2,8,0)
5100 if (!wx_is_at_least_gtk2(8))
5101 return wxDataViewItem();
5102
5103 wxGtkTreePath start;
5104 if ( gtk_tree_view_get_visible_range
5105 (
5106 GTK_TREE_VIEW(m_treeview),
5107 start.ByRef(),
5108 NULL
5109 ) )
5110 {
5111 return GTKPathToItem(start);
5112 }
5113 #endif
5114
5115 return wxDataViewItem();
5116 }
5117
GetCountPerPage() const5118 int wxDataViewCtrl::GetCountPerPage() const
5119 {
5120 wxGtkTreePath path;
5121 GtkTreeViewColumn *column;
5122
5123 if ( !gtk_tree_view_get_path_at_pos
5124 (
5125 GTK_TREE_VIEW(m_treeview),
5126 0,
5127 0,
5128 path.ByRef(),
5129 &column,
5130 NULL,
5131 NULL
5132 ) )
5133 {
5134 return -1;
5135 }
5136
5137 GdkRectangle rect;
5138 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(m_treeview), path, column, &rect);
5139
5140 if ( !rect.height )
5141 return -1;
5142
5143 GdkRectangle vis;
5144 gtk_tree_view_get_visible_rect(GTK_TREE_VIEW(m_treeview), &vis);
5145
5146 return vis.height / rect.height;
5147 }
5148
GetCurrentColumn() const5149 wxDataViewColumn *wxDataViewCtrl::GetCurrentColumn() const
5150 {
5151 // The tree doesn't have any current item if it hadn't been created yet but
5152 // it's arguably not an error to call this function in this case so just
5153 // return NULL without asserting.
5154 if ( !m_treeview )
5155 return NULL;
5156
5157 GtkTreeViewColumn *col;
5158 gtk_tree_view_get_cursor(GTK_TREE_VIEW(m_treeview), NULL, &col);
5159 return GTKColumnToWX(col);
5160 }
5161
EditItem(const wxDataViewItem & item,const wxDataViewColumn * column)5162 void wxDataViewCtrl::EditItem(const wxDataViewItem& item, const wxDataViewColumn *column)
5163 {
5164 wxCHECK_RET( m_treeview,
5165 "item can't be edited before creating the control." );
5166 wxCHECK_RET( m_internal, "model must be associated before editing an item" );
5167 wxCHECK_RET( item.IsOk(), "invalid item" );
5168 wxCHECK_RET( column, "no column provided" );
5169
5170 // We need to make sure the model knows about this item or the path would
5171 // be invalid and gtk_tree_view_set_cursor() would silently do nothing.
5172 ExpandAncestors(item);
5173
5174 GtkTreeViewColumn *gcolumn = GTK_TREE_VIEW_COLUMN(column->GetGtkHandle());
5175
5176 // We also need to preserve the existing selection from changing.
5177 // Unfortunately the only way to do it seems to use our own selection
5178 // function and forbid any selection changes during set cursor call.
5179 wxGtkTreeSelectionLock
5180 lock(gtk_tree_view_get_selection(GTK_TREE_VIEW(m_treeview)),
5181 m_internal->m_selectionFuncSet);
5182
5183 // Do move the cursor now.
5184 GtkTreeIter iter;
5185 iter.user_data = item.GetID();
5186 wxGtkTreePath path(m_internal->get_path( &iter ));
5187
5188 gtk_tree_view_set_cursor(GTK_TREE_VIEW(m_treeview), path, gcolumn, TRUE);
5189 }
5190
GetSelectedItemsCount() const5191 int wxDataViewCtrl::GetSelectedItemsCount() const
5192 {
5193 GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
5194
5195 return gtk_tree_selection_count_selected_rows(selection);
5196 }
5197
GetSelections(wxDataViewItemArray & sel) const5198 int wxDataViewCtrl::GetSelections( wxDataViewItemArray & sel ) const
5199 {
5200 wxCHECK_MSG( m_internal, 0, "model must be associated before calling GetSelections" );
5201
5202 sel.Clear();
5203
5204 GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
5205 if (HasFlag(wxDV_MULTIPLE))
5206 {
5207 GtkTreeModel *model;
5208 wxGtkTreePathList list(gtk_tree_selection_get_selected_rows(selection, &model));
5209
5210 for ( GList* current = list; current; current = g_list_next(current) )
5211 {
5212 GtkTreePath *path = (GtkTreePath*) current->data;
5213
5214 sel.Add(GTKPathToItem(path));
5215 }
5216 }
5217 else
5218 {
5219 GtkTreeIter iter;
5220 if (gtk_tree_selection_get_selected( selection, NULL, &iter ))
5221 {
5222 sel.Add( wxDataViewItem(iter.user_data) );
5223 }
5224 }
5225
5226 return sel.size();
5227 }
5228
SetSelections(const wxDataViewItemArray & sel)5229 void wxDataViewCtrl::SetSelections( const wxDataViewItemArray & sel )
5230 {
5231 wxCHECK_RET( m_internal, "model must be associated before calling SetSelections" );
5232
5233 SelectionEventsSuppressor noSelection(this);
5234
5235 GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
5236
5237 gtk_tree_selection_unselect_all( selection );
5238
5239 wxDataViewItem last_parent;
5240
5241 size_t i;
5242 for (i = 0; i < sel.GetCount(); i++)
5243 {
5244 wxDataViewItem item = sel[i];
5245 wxDataViewItem parent = GetModel()->GetParent( item );
5246 if (parent)
5247 {
5248 if (parent != last_parent)
5249 ExpandAncestors(item);
5250 }
5251 last_parent = parent;
5252
5253 GtkTreeIter iter;
5254 iter.stamp = m_internal->GetGtkModel()->stamp;
5255 iter.user_data = (gpointer) item.GetID();
5256 gtk_tree_selection_select_iter( selection, &iter );
5257 }
5258 }
5259
Select(const wxDataViewItem & item)5260 void wxDataViewCtrl::Select( const wxDataViewItem & item )
5261 {
5262 wxCHECK_RET( m_internal, "model must be associated before calling Select" );
5263
5264 ExpandAncestors(item);
5265
5266 SelectionEventsSuppressor noSelection(this);
5267
5268 GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
5269
5270 GtkTreeIter iter;
5271 iter.stamp = m_internal->GetGtkModel()->stamp;
5272 iter.user_data = (gpointer) item.GetID();
5273 gtk_tree_selection_select_iter( selection, &iter );
5274 }
5275
Unselect(const wxDataViewItem & item)5276 void wxDataViewCtrl::Unselect( const wxDataViewItem & item )
5277 {
5278 wxCHECK_RET( m_internal, "model must be associated before calling Unselect" );
5279
5280 SelectionEventsSuppressor noSelection(this);
5281
5282 GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
5283
5284 GtkTreeIter iter;
5285 iter.stamp = m_internal->GetGtkModel()->stamp;
5286 iter.user_data = (gpointer) item.GetID();
5287 gtk_tree_selection_unselect_iter( selection, &iter );
5288 }
5289
IsSelected(const wxDataViewItem & item) const5290 bool wxDataViewCtrl::IsSelected( const wxDataViewItem & item ) const
5291 {
5292 wxCHECK_MSG( m_internal, false, "model must be associated before calling IsSelected" );
5293
5294 GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
5295
5296 GtkTreeIter iter;
5297 iter.stamp = m_internal->GetGtkModel()->stamp;
5298 iter.user_data = (gpointer) item.GetID();
5299
5300 return gtk_tree_selection_iter_is_selected( selection, &iter ) != 0;
5301 }
5302
SelectAll()5303 void wxDataViewCtrl::SelectAll()
5304 {
5305 SelectionEventsSuppressor noSelection(this);
5306
5307 GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
5308
5309 gtk_tree_selection_select_all( selection );
5310 }
5311
UnselectAll()5312 void wxDataViewCtrl::UnselectAll()
5313 {
5314 SelectionEventsSuppressor noSelection(this);
5315
5316 GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
5317
5318 gtk_tree_selection_unselect_all( selection );
5319 }
5320
EnsureVisible(const wxDataViewItem & item,const wxDataViewColumn * WXUNUSED (column))5321 void wxDataViewCtrl::EnsureVisible(const wxDataViewItem& item,
5322 const wxDataViewColumn *WXUNUSED(column))
5323 {
5324 wxCHECK_RET( m_internal, "model must be associated before calling EnsureVisible" );
5325
5326 m_ensureVisibleDefered = item;
5327 ExpandAncestors(item);
5328
5329 GtkTreeIter iter;
5330 iter.user_data = (gpointer) item.GetID();
5331 wxGtkTreePath path(m_internal->get_path( &iter ));
5332 gtk_tree_view_scroll_to_cell( GTK_TREE_VIEW(m_treeview), path, NULL, false, 0.0, 0.0 );
5333 }
5334
HitTest(const wxPoint & point,wxDataViewItem & item,wxDataViewColumn * & column) const5335 void wxDataViewCtrl::HitTest(const wxPoint& point,
5336 wxDataViewItem& item,
5337 wxDataViewColumn *& column) const
5338 {
5339 wxCHECK_RET( m_internal, "model must be associated before calling HitTest" );
5340
5341 // gtk_tree_view_get_dest_row_at_pos() is the right one. But it does not tell the column.
5342 // gtk_tree_view_get_path_at_pos() is the wrong function. It doesn't mind the header but returns column.
5343 // See http://mail.gnome.org/archives/gtkmm-list/2005-January/msg00080.html
5344 // So we have to use both of them.
5345 item = wxDataViewItem(0);
5346 column = NULL;
5347 wxGtkTreePath path, pathScratch;
5348 GtkTreeViewColumn* GtkColumn = NULL;
5349 GtkTreeViewDropPosition pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
5350 gint cell_x = 0;
5351 gint cell_y = 0;
5352
5353 // cannot directly call GtkGetTreeView(), HitTest is const and so is this pointer
5354 wxDataViewCtrl* self = const_cast<wxDataViewCtrl *>(this); // ugly workaround, self is NOT const
5355 GtkTreeView* treeView = GTK_TREE_VIEW(self->GtkGetTreeView());
5356
5357 // is there possibly a better suited function to get the column?
5358 gtk_tree_view_get_path_at_pos( // and this is the wrong call but it delivers the column
5359 treeView,
5360 (int) point.x, (int) point.y,
5361 pathScratch.ByRef(),
5362 &GtkColumn, // here we get the GtkColumn
5363 &cell_x,
5364 &cell_y );
5365
5366 if ( GtkColumn != NULL )
5367 {
5368 // we got GTK column
5369 // the right call now which takes the header into account
5370 gtk_tree_view_get_dest_row_at_pos( treeView, (int) point.x, (int) point.y, path.ByRef(), &pos);
5371
5372 if (path)
5373 item = wxDataViewItem(GTKPathToItem(path));
5374 // else we got a GTK column but the position is not over an item, e.g. below last item
5375 for ( unsigned int i=0, cols=GetColumnCount(); i<cols; ++i ) // search the wx column
5376 {
5377 wxDataViewColumn* col = GetColumn(i);
5378 if ( GTK_TREE_VIEW_COLUMN(col->GetGtkHandle()) == GtkColumn )
5379 {
5380 column = col; // here we get the wx column
5381 break;
5382 }
5383 }
5384 }
5385 // else no column and thus no item, both null
5386 }
5387
5388 wxRect
GetItemRect(const wxDataViewItem & item,const wxDataViewColumn * column) const5389 wxDataViewCtrl::GetItemRect(const wxDataViewItem& item,
5390 const wxDataViewColumn *column) const
5391 {
5392 if ( !item )
5393 return wxRect();
5394
5395 GtkTreeViewColumn *gcolumn = NULL ;
5396 if (column)
5397 gcolumn = GTK_TREE_VIEW_COLUMN(column->GetGtkHandle());
5398
5399 GtkTreeIter iter;
5400 iter.user_data = item.GetID();
5401 wxGtkTreePath path(m_internal->get_path( &iter ));
5402
5403 GdkRectangle item_rect;
5404 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(m_treeview), path, gcolumn, &item_rect);
5405
5406 // GTK returns rectangles with the position and height, but not width, for
5407 // some reason, set to 0 if the item is not currently shown, so an explicit
5408 // check is needed as this rectangle is not quite the empty rectangle we're
5409 // supposed to return in this case.
5410 if ( item_rect.height == 0 )
5411 return wxRect();
5412
5413 // If column is NULL we compute the combined width of all the columns
5414 if ( !column )
5415 {
5416 unsigned int cols = GetColumnCount();
5417 int width = 0;
5418 for (unsigned int i = 0; i < cols; ++i)
5419 {
5420 wxDataViewColumn * col = GetColumn(i);
5421 if ( !col->IsHidden() )
5422 width += col->GetWidth();
5423 }
5424 item_rect.width = width;
5425 }
5426
5427 // We need to convert logical coordinates to physical ones, i.e. the
5428 // rectangle of the topmost item should start at ~0, even if it's a 100th
5429 // item shown on top only because the window is scrolled.
5430 #if GTK_CHECK_VERSION(2, 12, 0)
5431 if ( wx_is_at_least_gtk2(12) )
5432 {
5433 gtk_tree_view_convert_bin_window_to_widget_coords
5434 (
5435 GTK_TREE_VIEW(m_treeview),
5436 item_rect.x, item_rect.y,
5437 &item_rect.x, &item_rect.y
5438 );
5439
5440 if ( item_rect.y > GetClientSize().y ||
5441 item_rect.y + item_rect.height < 0 )
5442 {
5443 // If it turns out that the item is not visible at all, indicate it
5444 // by returning an empty rectangle for it.
5445 return wxRect();
5446 }
5447 }
5448 //else: There doesn't seem to be anything reasonable to do here, so we'll
5449 // just return wrong values with the very old GTK+ versions if the
5450 // window is scrolled.
5451 #endif // GTK+ 2.12+
5452
5453 return wxRectFromGDKRect(&item_rect);
5454 }
5455
SetRowHeight(int rowHeight)5456 bool wxDataViewCtrl::SetRowHeight(int rowHeight)
5457 {
5458 m_uniformRowHeight = rowHeight;
5459 return true;
5460 }
5461
DoSetExpanderColumn()5462 void wxDataViewCtrl::DoSetExpanderColumn()
5463 {
5464 gtk_tree_view_set_expander_column( GTK_TREE_VIEW(m_treeview),
5465 GTK_TREE_VIEW_COLUMN( GetExpanderColumn()->GetGtkHandle() ) );
5466 }
5467
DoSetIndent()5468 void wxDataViewCtrl::DoSetIndent()
5469 {
5470 #if GTK_CHECK_VERSION(2, 12, 0)
5471 if ( wx_is_at_least_gtk2(12) )
5472 {
5473 gtk_tree_view_set_level_indentation(GTK_TREE_VIEW(m_treeview), GetIndent());
5474 }
5475 #endif // GTK+ 2.12+
5476 }
5477
GtkDisableSelectionEvents()5478 void wxDataViewCtrl::GtkDisableSelectionEvents()
5479 {
5480 GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
5481 g_signal_handlers_block_by_func(
5482 selection, (void*)wxdataview_selection_changed_callback, this);
5483 }
5484
GtkEnableSelectionEvents()5485 void wxDataViewCtrl::GtkEnableSelectionEvents()
5486 {
5487 GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
5488 g_signal_handlers_unblock_by_func(
5489 selection, (void*)wxdataview_selection_changed_callback, this);
5490 }
5491
5492 // ----------------------------------------------------------------------------
5493 // visual attributes stuff
5494 // ----------------------------------------------------------------------------
5495
5496 // static
5497 wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant WXUNUSED (variant))5498 wxDataViewCtrl::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
5499 {
5500 return GetDefaultAttributesFromGTKWidget(gtk_tree_view_new());
5501 }
5502
DoApplyWidgetStyle(GtkRcStyle * style)5503 void wxDataViewCtrl::DoApplyWidgetStyle(GtkRcStyle *style)
5504 {
5505 wxDataViewCtrlBase::DoApplyWidgetStyle(style);
5506 GTKApplyStyle(m_treeview, style);
5507 }
5508
5509 #endif // !wxHAS_GENERIC_DATAVIEWCTRL
5510
5511 #endif // wxUSE_DATAVIEWCTRL
5512