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