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