]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/dataview.cpp
change wxDataViewItem id to void*
[wxWidgets.git] / src / gtk / dataview.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/dataview.cpp
3 // Purpose: wxDataViewCtrl GTK+2 implementation
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #if wxUSE_DATAVIEWCTRL
14
15 #include "wx/dataview.h"
16
17 #ifndef wxUSE_GENERICDATAVIEWCTRL
18
19 #ifndef WX_PRECOMP
20 #include "wx/log.h"
21 #include "wx/dcclient.h"
22 #include "wx/sizer.h"
23 #endif
24
25 #include "wx/stockitem.h"
26 #include "wx/calctrl.h"
27 #include "wx/popupwin.h"
28 #include "wx/icon.h"
29
30
31 #include "wx/gtk/private.h"
32 #include "wx/gtk/win_gtk.h"
33
34 #include <gobject/gvaluecollector.h>
35 #include <gtk/gtktreemodel.h>
36 #include <gtk/gtktreesortable.h>
37 #include <gtk/gtktreednd.h>
38
39 #include <gdk/gdkkeysyms.h>
40
41 //-----------------------------------------------------------------------------
42 // classes
43 //-----------------------------------------------------------------------------
44
45 //-----------------------------------------------------------------------------
46 // wxGtkTreeModelNode
47 //-----------------------------------------------------------------------------
48
49 class wxGtkTreeModelNode;
50 WX_DEFINE_ARRAY_PTR( wxGtkTreeModelNode*, wxGtkTreeModelNodes );
51
52 class wxGtkTreeModelNode
53 {
54 public:
55 wxGtkTreeModelNode( wxGtkTreeModelNode* parent, const wxDataViewItem &item )
56 {
57 m_parent = parent;
58 m_item = item;
59 }
60
61 ~wxGtkTreeModelNode()
62 {
63 size_t count = m_children.GetCount();
64 size_t i;
65 for (i = 0; i < count; i++)
66 {
67 wxGtkTreeModelNode *child = m_children[i];
68 delete child;
69 }
70 }
71
72 wxGtkTreeModelNode* GetParent()
73 { return m_parent; }
74 wxGtkTreeModelNodes &GetChildren()
75 { return m_children; }
76 wxGtkTreeModelNode* GetNthChild( unsigned int n )
77 { return m_children.Item( n ); }
78 void Insert( wxGtkTreeModelNode* child, unsigned int n)
79 { m_children.Insert( child, n); }
80 void Append( wxGtkTreeModelNode* child )
81 { m_children.Add( child ); }
82
83 unsigned int GetChildCount() { return m_children.GetCount(); }
84
85 wxDataViewItem &GetItem() { return m_item; }
86
87 bool HasChildren() { return m_hasChildren; }
88 void SetHasChildren( bool has ) { m_hasChildren = has; }
89
90 private:
91 wxGtkTreeModelNode *m_parent;
92 wxGtkTreeModelNodes m_children;
93 wxDataViewItem m_item;
94 bool m_hasChildren;
95 };
96
97
98 extern "C" {
99 typedef struct _GtkWxTreeModel GtkWxTreeModel;
100 }
101
102 class wxGtkTreeModel
103 {
104 public:
105 wxGtkTreeModel( wxDataViewModel *wx_model, GtkWxTreeModel *owner );
106 ~wxGtkTreeModel();
107
108 gboolean get_iter( GtkTreeIter *iter, GtkTreePath *path );
109 GtkTreePath *get_path( GtkTreeIter *iter);
110 gboolean iter_next( GtkTreeIter *iter );
111 gboolean iter_children( GtkTreeIter *iter, GtkTreeIter *parent);
112 gboolean iter_has_child( GtkTreeIter *iter );
113 gint iter_n_children( GtkTreeIter *iter );
114 gboolean iter_nth_child( GtkTreeIter *iter, GtkTreeIter *parent, gint n );
115 gboolean iter_parent( GtkTreeIter *iter, GtkTreeIter *child );
116
117 wxDataViewModel* GetDataViewModel() { return m_wx_model; }
118 GtkWxTreeModel* GetOwner() { return m_owner; }
119
120 bool ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item );
121 bool ItemDeleted( const wxDataViewItem &item );
122
123 protected:
124 void InitTree();
125 wxGtkTreeModelNode *FindNode( const wxDataViewItem &item );
126 wxGtkTreeModelNode *FindNode( GtkTreeIter *iter );
127 void BuildBranch( wxGtkTreeModelNode *branch );
128
129 private:
130 wxGtkTreeModelNode *m_root;
131 wxDataViewModel *m_wx_model;
132 GtkWxTreeModel *m_owner;
133 };
134
135 //-----------------------------------------------------------------------------
136 // data
137 //-----------------------------------------------------------------------------
138
139 extern bool g_blockEventsOnDrag;
140
141 //-----------------------------------------------------------------------------
142 // define new GTK+ class wxGtkTreeModel
143 //-----------------------------------------------------------------------------
144
145 extern "C" {
146
147 #define GTK_TYPE_WX_TREE_MODEL (gtk_wx_tree_model_get_type ())
148 #define GTK_WX_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_WX_TREE_MODEL, GtkWxTreeModel))
149 #define GTK_WX_TREE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_WX_TREE_MODEL, GtkWxTreeModelClass))
150 #define GTK_IS_WX_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_WX_TREE_MODEL))
151 #define GTK_IS_WX_TREE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_WX_TREE_MODEL))
152 #define GTK_WX_TREE_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_WX_TREE_MODEL, GtkWxTreeModelClass))
153
154 GType gtk_wx_tree_model_get_type (void);
155
156 typedef struct _GtkWxTreeModelClass GtkWxTreeModelClass;
157
158 struct _GtkWxTreeModel
159 {
160 GObject parent;
161
162 /*< private >*/
163 gint stamp;
164 wxGtkTreeModel *model;
165
166 gint sort_column_id;
167 GtkSortType order;
168 };
169
170 struct _GtkWxTreeModelClass
171 {
172 GObjectClass list_parent_class;
173 };
174
175 static GtkWxTreeModel *wxgtk_tree_model_new (void);
176 static void wxgtk_tree_model_init (GtkWxTreeModel *tree_model);
177 static void wxgtk_tree_model_class_init (GtkWxTreeModelClass *klass);
178 static void wxgtk_tree_model_tree_model_init (GtkTreeModelIface *iface);
179 static void wxgtk_tree_model_sortable_init (GtkTreeSortableIface *iface);
180 static void wxgtk_tree_model_finalize (GObject *object);
181 static GtkTreeModelFlags wxgtk_tree_model_get_flags (GtkTreeModel *tree_model);
182 static gint wxgtk_tree_model_get_n_columns (GtkTreeModel *tree_model);
183 static GType wxgtk_tree_model_get_column_type (GtkTreeModel *tree_model,
184 gint index);
185 static gboolean wxgtk_tree_model_get_iter (GtkTreeModel *tree_model,
186 GtkTreeIter *iter,
187 GtkTreePath *path);
188 static GtkTreePath *wxgtk_tree_model_get_path (GtkTreeModel *tree_model,
189 GtkTreeIter *iter);
190 static void wxgtk_tree_model_get_value (GtkTreeModel *tree_model,
191 GtkTreeIter *iter,
192 gint column,
193 GValue *value);
194 static gboolean wxgtk_tree_model_iter_next (GtkTreeModel *tree_model,
195 GtkTreeIter *iter);
196 static gboolean wxgtk_tree_model_iter_children (GtkTreeModel *tree_model,
197 GtkTreeIter *iter,
198 GtkTreeIter *parent);
199 static gboolean wxgtk_tree_model_iter_has_child (GtkTreeModel *tree_model,
200 GtkTreeIter *iter);
201 static gint wxgtk_tree_model_iter_n_children (GtkTreeModel *tree_model,
202 GtkTreeIter *iter);
203 static gboolean wxgtk_tree_model_iter_nth_child (GtkTreeModel *tree_model,
204 GtkTreeIter *iter,
205 GtkTreeIter *parent,
206 gint n);
207 static gboolean wxgtk_tree_model_iter_parent (GtkTreeModel *tree_model,
208 GtkTreeIter *iter,
209 GtkTreeIter *child);
210
211 /* sortable */
212 static gboolean wxgtk_tree_model_get_sort_column_id (GtkTreeSortable *sortable,
213 gint *sort_column_id,
214 GtkSortType *order);
215 static void wxgtk_tree_model_set_sort_column_id (GtkTreeSortable *sortable,
216 gint sort_column_id,
217 GtkSortType order);
218 static void wxgtk_tree_model_set_sort_func (GtkTreeSortable *sortable,
219 gint sort_column_id,
220 GtkTreeIterCompareFunc func,
221 gpointer data,
222 GtkDestroyNotify destroy);
223 static void wxgtk_tree_model_set_default_sort_func (GtkTreeSortable *sortable,
224 GtkTreeIterCompareFunc func,
225 gpointer data,
226 GtkDestroyNotify destroy);
227 static gboolean wxgtk_tree_model_has_default_sort_func (GtkTreeSortable *sortable);
228
229
230
231 static GObjectClass *list_parent_class = NULL;
232
233 GType
234 gtk_wx_tree_model_get_type (void)
235 {
236 static GType tree_model_type = 0;
237
238 if (!tree_model_type)
239 {
240 const GTypeInfo tree_model_info =
241 {
242 sizeof (GtkWxTreeModelClass),
243 NULL, /* base_init */
244 NULL, /* base_finalize */
245 (GClassInitFunc) wxgtk_tree_model_class_init,
246 NULL, /* class_finalize */
247 NULL, /* class_data */
248 sizeof (GtkWxTreeModel),
249 0,
250 (GInstanceInitFunc) wxgtk_tree_model_init,
251 };
252
253 static const GInterfaceInfo tree_model_iface_info =
254 {
255 (GInterfaceInitFunc) wxgtk_tree_model_tree_model_init,
256 NULL,
257 NULL
258 };
259
260 static const GInterfaceInfo sortable_iface_info =
261 {
262 (GInterfaceInitFunc) wxgtk_tree_model_sortable_init,
263 NULL,
264 NULL
265 };
266
267 tree_model_type = g_type_register_static (G_TYPE_OBJECT, "GtkWxTreeModel",
268 &tree_model_info, (GTypeFlags)0 );
269
270 g_type_add_interface_static (tree_model_type,
271 GTK_TYPE_TREE_MODEL,
272 &tree_model_iface_info);
273 g_type_add_interface_static (tree_model_type,
274 GTK_TYPE_TREE_SORTABLE,
275 &sortable_iface_info);
276 }
277
278 return tree_model_type;
279 }
280
281 static GtkWxTreeModel *
282 wxgtk_tree_model_new(void)
283 {
284 GtkWxTreeModel *retval = (GtkWxTreeModel *) g_object_new (GTK_TYPE_WX_TREE_MODEL, NULL);
285 return retval;
286 }
287
288 static void
289 wxgtk_tree_model_class_init (GtkWxTreeModelClass *klass)
290 {
291 list_parent_class = (GObjectClass*) g_type_class_peek_parent (klass);
292 GObjectClass *object_class = (GObjectClass*) klass;
293 object_class->finalize = wxgtk_tree_model_finalize;
294 }
295
296 static void
297 wxgtk_tree_model_tree_model_init (GtkTreeModelIface *iface)
298 {
299 iface->get_flags = wxgtk_tree_model_get_flags;
300 iface->get_n_columns = wxgtk_tree_model_get_n_columns;
301 iface->get_column_type = wxgtk_tree_model_get_column_type;
302 iface->get_iter = wxgtk_tree_model_get_iter;
303 iface->get_path = wxgtk_tree_model_get_path;
304 iface->get_value = wxgtk_tree_model_get_value;
305 iface->iter_next = wxgtk_tree_model_iter_next;
306 iface->iter_children = wxgtk_tree_model_iter_children;
307 iface->iter_has_child = wxgtk_tree_model_iter_has_child;
308 iface->iter_n_children = wxgtk_tree_model_iter_n_children;
309 iface->iter_nth_child = wxgtk_tree_model_iter_nth_child;
310 iface->iter_parent = wxgtk_tree_model_iter_parent;
311 }
312
313 static void
314 wxgtk_tree_model_sortable_init (GtkTreeSortableIface *iface)
315 {
316 iface->get_sort_column_id = wxgtk_tree_model_get_sort_column_id;
317 iface->set_sort_column_id = wxgtk_tree_model_set_sort_column_id;
318 iface->set_sort_func = wxgtk_tree_model_set_sort_func;
319 iface->set_default_sort_func = wxgtk_tree_model_set_default_sort_func;
320 iface->has_default_sort_func = wxgtk_tree_model_has_default_sort_func;
321 }
322
323 static void
324 wxgtk_tree_model_init (GtkWxTreeModel *tree_model)
325 {
326 tree_model->model = NULL;
327 tree_model->stamp = g_random_int();
328 tree_model->sort_column_id = -2;
329 tree_model->order = GTK_SORT_ASCENDING;
330 }
331
332 static void
333 wxgtk_tree_model_finalize (GObject *object)
334 {
335 GtkWxTreeModel *tree_model = GTK_WX_TREE_MODEL(object);
336
337 /* we need to sort out, which class deletes what */
338 delete tree_model->model;
339
340 /* must chain up */
341 (* list_parent_class->finalize) (object);
342 }
343
344 } // extern "C"
345
346 //-----------------------------------------------------------------------------
347 // implement callbacks from wxGtkTreeModel class by letting
348 // them call the methods of wxWidgets' wxDataViewModel
349 //-----------------------------------------------------------------------------
350
351 static GtkTreeModelFlags
352 wxgtk_tree_model_get_flags (GtkTreeModel *tree_model)
353 {
354 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (tree_model), (GtkTreeModelFlags)0 );
355
356 return GTK_TREE_MODEL_ITERS_PERSIST;
357 }
358
359 static gint
360 wxgtk_tree_model_get_n_columns (GtkTreeModel *tree_model)
361 {
362 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
363 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), 0);
364
365 return wxtree_model->model->GetDataViewModel()->GetColumnCount();
366 }
367
368 static GType
369 wxgtk_tree_model_get_column_type (GtkTreeModel *tree_model,
370 gint index)
371 {
372 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
373 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), G_TYPE_INVALID);
374
375 GType gtype = G_TYPE_INVALID;
376
377 wxString wxtype = wxtree_model->model->GetDataViewModel()->GetColumnType( (unsigned int) index );
378
379 if (wxtype == wxT("string"))
380 gtype = G_TYPE_STRING;
381 else
382 {
383 wxFAIL_MSG( _T("non-string columns not supported yet") );
384 }
385
386 return gtype;
387 }
388
389 static gboolean
390 wxgtk_tree_model_get_iter (GtkTreeModel *tree_model,
391 GtkTreeIter *iter,
392 GtkTreePath *path)
393 {
394 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
395 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
396 g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
397
398 return wxtree_model->model->get_iter( iter, path );
399 }
400
401 static GtkTreePath *
402 wxgtk_tree_model_get_path (GtkTreeModel *tree_model,
403 GtkTreeIter *iter)
404 {
405 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
406 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), NULL);
407 g_return_val_if_fail (iter->stamp == GTK_WX_TREE_MODEL (wxtree_model)->stamp, NULL);
408
409 return wxtree_model->model->get_path( iter );
410 }
411
412 static void
413 wxgtk_tree_model_get_value (GtkTreeModel *tree_model,
414 GtkTreeIter *iter,
415 gint column,
416 GValue *value)
417 {
418 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
419 g_return_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model) );
420
421 wxDataViewModel *model = wxtree_model->model->GetDataViewModel();
422 wxString mtype = model->GetColumnType( (unsigned int) column );
423 if (mtype == wxT("string"))
424 {
425 wxVariant variant;
426 g_value_init( value, G_TYPE_STRING );
427 wxDataViewItem item( (void*) iter->user_data );
428 model->GetValue( variant, item, (unsigned int) column );
429
430 g_value_set_string( value, variant.GetString().utf8_str() );
431 }
432 else
433 {
434 wxFAIL_MSG( _T("non-string columns not supported yet") );
435 }
436 }
437
438 static gboolean
439 wxgtk_tree_model_iter_next (GtkTreeModel *tree_model,
440 GtkTreeIter *iter)
441 {
442 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
443 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
444 g_return_val_if_fail (wxtree_model->stamp == iter->stamp, FALSE);
445
446 return wxtree_model->model->iter_next( iter );
447 }
448
449 static gboolean
450 wxgtk_tree_model_iter_children (GtkTreeModel *tree_model,
451 GtkTreeIter *iter,
452 GtkTreeIter *parent)
453 {
454 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
455 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
456 g_return_val_if_fail (wxtree_model->stamp == parent->stamp, FALSE);
457
458 return wxtree_model->model->iter_children( iter, parent );
459 }
460
461 static gboolean
462 wxgtk_tree_model_iter_has_child (GtkTreeModel *tree_model,
463 GtkTreeIter *iter)
464 {
465 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
466 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
467 g_return_val_if_fail (wxtree_model->stamp == iter->stamp, FALSE);
468
469 return wxtree_model->model->iter_has_child( iter );
470 }
471
472 static gint
473 wxgtk_tree_model_iter_n_children (GtkTreeModel *tree_model,
474 GtkTreeIter *iter)
475 {
476 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
477 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
478 g_return_val_if_fail (wxtree_model->stamp == iter->stamp, 0);
479
480 return wxtree_model->model->iter_n_children( iter );
481 }
482
483 static gboolean
484 wxgtk_tree_model_iter_nth_child (GtkTreeModel *tree_model,
485 GtkTreeIter *iter,
486 GtkTreeIter *parent,
487 gint n)
488 {
489 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
490 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
491
492 return wxtree_model->model->iter_nth_child( iter, parent, n );
493 }
494
495 static gboolean
496 wxgtk_tree_model_iter_parent (GtkTreeModel *tree_model,
497 GtkTreeIter *iter,
498 GtkTreeIter *child)
499 {
500 GtkWxTreeModel *wxtree_model = (GtkWxTreeModel *) tree_model;
501 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (wxtree_model), FALSE);
502 g_return_val_if_fail (wxtree_model->stamp == child->stamp, FALSE);
503
504 return wxtree_model->model->iter_parent( iter, child );
505 }
506
507 /* sortable */
508 gboolean wxgtk_tree_model_get_sort_column_id (GtkTreeSortable *sortable,
509 gint *sort_column_id,
510 GtkSortType *order)
511 {
512 GtkWxTreeModel *tree_model = (GtkWxTreeModel *) sortable;
513
514 g_return_val_if_fail (GTK_IS_WX_TREE_MODEL (sortable), FALSE);
515
516 if (tree_model->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
517 return FALSE;
518
519 if (sort_column_id)
520 *sort_column_id = tree_model->sort_column_id;
521
522 if (order)
523 *order = tree_model->order;
524
525 return TRUE;
526 }
527
528 void wxgtk_tree_model_set_sort_column_id (GtkTreeSortable *sortable,
529 gint sort_column_id,
530 GtkSortType order)
531 {
532 GtkWxTreeModel *tree_model = (GtkWxTreeModel *) sortable;
533
534 g_return_if_fail (GTK_IS_WX_TREE_MODEL (sortable) );
535
536 if ((tree_model->sort_column_id == sort_column_id) &&
537 (tree_model->order == order))
538 return;
539
540 tree_model->sort_column_id = sort_column_id;
541 tree_model->order = order;
542
543 gtk_tree_sortable_sort_column_changed (sortable);
544
545 wxPrintf( "wxgtk_tree_model_set_column_id, sort_column_id = %d, order = %d\n", sort_column_id, (int)order );
546 // sort
547 }
548
549 void wxgtk_tree_model_set_sort_func (GtkTreeSortable *sortable,
550 gint sort_column_id,
551 GtkTreeIterCompareFunc func,
552 gpointer data,
553 GtkDestroyNotify destroy)
554 {
555 g_return_if_fail (GTK_IS_WX_TREE_MODEL (sortable) );
556 g_return_if_fail (func != NULL);
557
558 wxPrintf( "wxgtk_tree_model_set_sort_func, sort_column_id = %d\n", sort_column_id );
559 // sort
560 }
561
562 void wxgtk_tree_model_set_default_sort_func (GtkTreeSortable *sortable,
563 GtkTreeIterCompareFunc func,
564 gpointer data,
565 GtkDestroyNotify destroy)
566 {
567 g_return_if_fail (GTK_IS_WX_TREE_MODEL (sortable) );
568 g_return_if_fail (func != NULL);
569
570 wxPrintf( "wxgtk_tree_model_set_default_sort_func\n" );
571 }
572
573 gboolean wxgtk_tree_model_has_default_sort_func (GtkTreeSortable *sortable)
574 {
575 return FALSE;
576 }
577
578
579 //-----------------------------------------------------------------------------
580 // wxGtkTreeModel
581 //-----------------------------------------------------------------------------
582
583 wxGtkTreeModel::wxGtkTreeModel( wxDataViewModel *wx_model, GtkWxTreeModel *owner )
584 {
585 m_wx_model = wx_model;
586 m_owner = owner;
587 m_root = NULL;
588 InitTree();
589 }
590
591 wxGtkTreeModel::~wxGtkTreeModel()
592 {
593 }
594
595 void wxGtkTreeModel::BuildBranch( wxGtkTreeModelNode *node )
596 {
597 if (node->GetChildCount() == 0)
598 {
599 wxDataViewItem child = m_wx_model->GetFirstChild( node->GetItem() );
600 while (child.IsOk())
601 {
602 // wxPrintf( "AddItem %d\n", (int) child.GetID() );
603 node->Append( new wxGtkTreeModelNode( node, child ) );
604 child = m_wx_model->GetNextSibling( child );
605 }
606 }
607 }
608
609 bool wxGtkTreeModel::ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item )
610 {
611 wxGtkTreeModelNode *parent_node = FindNode( parent );
612 parent_node->Append( new wxGtkTreeModelNode( parent_node, item ) );
613 return true;
614 }
615
616 bool wxGtkTreeModel::ItemDeleted( const wxDataViewItem &item )
617 {
618 wxGtkTreeModelNode *node = FindNode( item );
619 wxGtkTreeModelNode *parent = node->GetParent();
620 parent->GetChildren().Remove( node );
621 delete node;
622
623 return true;
624 }
625
626 gboolean wxGtkTreeModel::get_iter( GtkTreeIter *iter, GtkTreePath *path )
627 {
628 int depth = gtk_tree_path_get_depth( path );
629
630 wxGtkTreeModelNode *node = m_root;
631
632 int i;
633 for (i = 0; i < depth; i++)
634 {
635 BuildBranch( node );
636
637 gint pos = gtk_tree_path_get_indices (path)[i];
638 if (pos < 0) return FALSE;
639 if ((size_t)pos >= node->GetChildCount()) return FALSE;
640
641 node = node->GetChildren().Item( (size_t) pos );
642 }
643
644 iter->stamp = m_owner->stamp;
645 iter->user_data = (gpointer) node->GetItem().GetID();
646
647 return TRUE;
648 }
649
650 GtkTreePath *wxGtkTreeModel::get_path( GtkTreeIter *iter )
651 {
652 GtkTreePath *retval = gtk_tree_path_new ();
653
654 wxGtkTreeModelNode *node = FindNode( iter );
655 while (node->GetParent())
656 {
657 wxGtkTreeModelNode *parent = node->GetParent();
658 int pos = parent->GetChildren().Index( node );
659
660 gtk_tree_path_prepend_index( retval, pos );
661
662 node = parent;
663 }
664
665 return retval;
666 }
667
668 gboolean wxGtkTreeModel::iter_next( GtkTreeIter *iter )
669 {
670 wxDataViewItem item( (void*) iter->user_data );
671 item = m_wx_model->GetNextSibling( item );
672 if (!item.IsOk())
673 return FALSE;
674
675 iter->user_data = (gpointer) item.GetID();
676
677 return TRUE;
678 }
679
680 gboolean wxGtkTreeModel::iter_children( GtkTreeIter *iter, GtkTreeIter *parent )
681 {
682 wxDataViewItem item( (void*) parent->user_data );
683
684 if (!m_wx_model->HasChildren( item ))
685 return FALSE;
686
687 wxGtkTreeModelNode *parent_node = FindNode( parent );
688 BuildBranch( parent_node );
689
690 wxGtkTreeModelNode *first_child_node = parent_node->GetChildren().Item( 0 );
691
692 iter->stamp = m_owner->stamp;
693 iter->user_data = (gpointer) first_child_node->GetItem().GetID();
694
695 return TRUE;
696 }
697
698 gboolean wxGtkTreeModel::iter_has_child( GtkTreeIter *iter )
699 {
700 wxDataViewItem item( (void*) iter->user_data );
701
702 return m_wx_model->HasChildren( item );
703 }
704
705 gint wxGtkTreeModel::iter_n_children( GtkTreeIter *iter )
706 {
707 wxDataViewItem item( (void*) iter->user_data );
708
709 if (!m_wx_model->HasChildren( item ))
710 return 0;
711
712 wxGtkTreeModelNode *parent_node = FindNode( iter );
713 BuildBranch( parent_node );
714
715 return parent_node->GetChildCount();
716 }
717
718 gboolean wxGtkTreeModel::iter_nth_child( GtkTreeIter *iter, GtkTreeIter *parent, gint n )
719 {
720 void* id = NULL;
721 if (parent) id = (void*) parent->user_data;
722 wxDataViewItem item( id );
723
724 if (!m_wx_model->HasChildren( item ))
725 return FALSE;
726
727 wxGtkTreeModelNode *parent_node = FindNode( parent );
728 BuildBranch( parent_node );
729
730 wxGtkTreeModelNode *child_node = parent_node->GetChildren().Item( (size_t) n );
731 if (!child_node)
732 return FALSE;
733
734 iter->stamp = m_owner->stamp;
735 iter->user_data = (gpointer) child_node->GetItem().GetID();
736
737 return TRUE;
738 }
739
740 gboolean wxGtkTreeModel::iter_parent( GtkTreeIter *iter, GtkTreeIter *child )
741 {
742 wxDataViewItem item( (void*) child->user_data );
743
744 wxGtkTreeModelNode *node = FindNode( child );
745 node = node->GetParent();
746 if (!node)
747 return FALSE;
748
749 iter->stamp = m_owner->stamp;
750 iter->user_data = (gpointer) node->GetItem().GetID();
751
752 return TRUE;
753 }
754
755 void wxGtkTreeModel::InitTree()
756 {
757 wxDataViewItem item;
758 m_root = new wxGtkTreeModelNode( NULL, item );
759
760 BuildBranch( m_root );
761 }
762
763 static wxGtkTreeModelNode*
764 wxGtkTreeModel_FindNode( wxGtkTreeModelNode *node, const wxDataViewItem &item )
765 {
766 if (!node) return NULL;
767
768 size_t count = node->GetChildCount();
769 size_t i;
770 for (i = 0; i < count; i++)
771 {
772 wxGtkTreeModelNode *child = node->GetChildren().Item( i );
773 if (child->GetItem().GetID() == item.GetID())
774 return child;
775
776 wxGtkTreeModelNode *node2 = wxGtkTreeModel_FindNode( child, item );
777 if (node2)
778 return node2;
779 }
780
781 return NULL;
782 }
783
784 wxGtkTreeModelNode *wxGtkTreeModel::FindNode( GtkTreeIter *iter )
785 {
786 if (!iter)
787 return m_root;
788
789 wxDataViewItem item( (void*) iter->user_data );
790
791 wxGtkTreeModelNode *result = wxGtkTreeModel_FindNode( m_root, item );
792
793 if (!result)
794 {
795 wxPrintf( "Not found %d\n", (int) iter->user_data );
796 char *crash = NULL;
797 *crash = 0;
798 }
799
800 return result;
801 }
802
803 wxGtkTreeModelNode *wxGtkTreeModel::FindNode( const wxDataViewItem &item )
804 {
805 wxGtkTreeModelNode *result = wxGtkTreeModel_FindNode( m_root, item );
806
807 if (!result)
808 {
809 wxPrintf( "Not found %d\n", (int) item.GetID() );
810 char *crash = NULL;
811 *crash = 0;
812 }
813
814 return result;
815 }
816
817 //-----------------------------------------------------------------------------
818 // define new GTK+ class wxGtkRendererRenderer
819 //-----------------------------------------------------------------------------
820
821 extern "C" {
822
823 #define GTK_TYPE_WX_CELL_RENDERER (gtk_wx_cell_renderer_get_type ())
824 #define GTK_WX_CELL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_WX_CELL_RENDERER, GtkWxCellRenderer))
825 #define GTK_WX_CELL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_WX_CELL_RENDERER, GtkWxCellRendererClass))
826 #define GTK_IS_WX_CELL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_WX_CELL_RENDERER))
827 #define GTK_IS_WX_CELL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_WX_CELL_RENDERER))
828 #define GTK_WX_CELL_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_WX_CELL_RENDERER, GtkWxCellRendererClass))
829
830 GType gtk_wx_cell_renderer_get_type (void);
831
832 typedef struct _GtkWxCellRenderer GtkWxCellRenderer;
833 typedef struct _GtkWxCellRendererClass GtkWxCellRendererClass;
834
835 struct _GtkWxCellRenderer
836 {
837 GtkCellRenderer parent;
838
839 /*< private >*/
840 wxDataViewCustomRenderer *cell;
841 guint32 last_click;
842 };
843
844 struct _GtkWxCellRendererClass
845 {
846 GtkCellRendererClass cell_parent_class;
847 };
848
849
850 static GtkCellRenderer *gtk_wx_cell_renderer_new (void);
851 static void gtk_wx_cell_renderer_init (
852 GtkWxCellRenderer *cell );
853 static void gtk_wx_cell_renderer_class_init(
854 GtkWxCellRendererClass *klass );
855 static void gtk_wx_cell_renderer_finalize (
856 GObject *object );
857 static void gtk_wx_cell_renderer_get_size (
858 GtkCellRenderer *cell,
859 GtkWidget *widget,
860 GdkRectangle *rectangle,
861 gint *x_offset,
862 gint *y_offset,
863 gint *width,
864 gint *height );
865 static void gtk_wx_cell_renderer_render (
866 GtkCellRenderer *cell,
867 GdkWindow *window,
868 GtkWidget *widget,
869 GdkRectangle *background_area,
870 GdkRectangle *cell_area,
871 GdkRectangle *expose_area,
872 GtkCellRendererState flags );
873 static gboolean gtk_wx_cell_renderer_activate(
874 GtkCellRenderer *cell,
875 GdkEvent *event,
876 GtkWidget *widget,
877 const gchar *path,
878 GdkRectangle *background_area,
879 GdkRectangle *cell_area,
880 GtkCellRendererState flags );
881 static GtkCellEditable *gtk_wx_cell_renderer_start_editing(
882 GtkCellRenderer *cell,
883 GdkEvent *event,
884 GtkWidget *widget,
885 const gchar *path,
886 GdkRectangle *background_area,
887 GdkRectangle *cell_area,
888 GtkCellRendererState flags );
889
890
891 static GObjectClass *cell_parent_class = NULL;
892
893 } // extern "C"
894
895 GType
896 gtk_wx_cell_renderer_get_type (void)
897 {
898 static GType cell_wx_type = 0;
899
900 if (!cell_wx_type)
901 {
902 const GTypeInfo cell_wx_info =
903 {
904 sizeof (GtkWxCellRendererClass),
905 NULL, /* base_init */
906 NULL, /* base_finalize */
907 (GClassInitFunc) gtk_wx_cell_renderer_class_init,
908 NULL, /* class_finalize */
909 NULL, /* class_data */
910 sizeof (GtkWxCellRenderer),
911 0, /* n_preallocs */
912 (GInstanceInitFunc) gtk_wx_cell_renderer_init,
913 };
914
915 cell_wx_type = g_type_register_static( GTK_TYPE_CELL_RENDERER,
916 "GtkWxCellRenderer", &cell_wx_info, (GTypeFlags)0 );
917 }
918
919 return cell_wx_type;
920 }
921
922 static void
923 gtk_wx_cell_renderer_init (GtkWxCellRenderer *cell)
924 {
925 cell->cell = NULL;
926 cell->last_click = 0;
927 }
928
929 static void
930 gtk_wx_cell_renderer_class_init (GtkWxCellRendererClass *klass)
931 {
932 GObjectClass *object_class = G_OBJECT_CLASS (klass);
933 GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
934
935 cell_parent_class = (GObjectClass*) g_type_class_peek_parent (klass);
936
937 object_class->finalize = gtk_wx_cell_renderer_finalize;
938
939 cell_class->get_size = gtk_wx_cell_renderer_get_size;
940 cell_class->render = gtk_wx_cell_renderer_render;
941 cell_class->activate = gtk_wx_cell_renderer_activate;
942 cell_class->start_editing = gtk_wx_cell_renderer_start_editing;
943 }
944
945 static void
946 gtk_wx_cell_renderer_finalize (GObject *object)
947 {
948 /* must chain up */
949 (* G_OBJECT_CLASS (cell_parent_class)->finalize) (object);
950 }
951
952 GtkCellRenderer*
953 gtk_wx_cell_renderer_new (void)
954 {
955 return (GtkCellRenderer*) g_object_new (GTK_TYPE_WX_CELL_RENDERER, NULL);
956 }
957
958
959
960 static GtkCellEditable *gtk_wx_cell_renderer_start_editing(
961 GtkCellRenderer *renderer,
962 GdkEvent *event,
963 GtkWidget *widget,
964 const gchar *path,
965 GdkRectangle *background_area,
966 GdkRectangle *cell_area,
967 GtkCellRendererState flags )
968 {
969 GtkWxCellRenderer *wxrenderer = (GtkWxCellRenderer *) renderer;
970 wxDataViewCustomRenderer *cell = wxrenderer->cell;
971 if (!cell->HasEditorCtrl())
972 return NULL;
973
974 GdkRectangle rect;
975 gtk_wx_cell_renderer_get_size (renderer, widget, cell_area,
976 &rect.x,
977 &rect.y,
978 &rect.width,
979 &rect.height);
980
981 rect.x += cell_area->x;
982 rect.y += cell_area->y;
983 // rect.width -= renderer->xpad * 2;
984 // rect.height -= renderer->ypad * 2;
985
986 // wxRect renderrect( rect.x, rect.y, rect.width, rect.height );
987 wxRect renderrect( cell_area->x, cell_area->y, cell_area->width, cell_area->height );
988
989 // wxDataViewListModel *model = cell->GetOwner()->GetOwner()->GetModel();
990
991 GtkTreePath *treepath = gtk_tree_path_new_from_string( path );
992 // TODO
993 wxDataViewItem item;
994 gtk_tree_path_free( treepath );
995
996 cell->StartEditing( item, renderrect );
997
998 return NULL;
999 }
1000
1001 static void
1002 gtk_wx_cell_renderer_get_size (GtkCellRenderer *renderer,
1003 GtkWidget *widget,
1004 GdkRectangle *cell_area,
1005 gint *x_offset,
1006 gint *y_offset,
1007 gint *width,
1008 gint *height)
1009 {
1010 GtkWxCellRenderer *wxrenderer = (GtkWxCellRenderer *) renderer;
1011 wxDataViewCustomRenderer *cell = wxrenderer->cell;
1012
1013 wxSize size = cell->GetSize();
1014
1015 gint calc_width = (gint) renderer->xpad * 2 + size.x;
1016 gint calc_height = (gint) renderer->ypad * 2 + size.y;
1017
1018 if (x_offset)
1019 *x_offset = 0;
1020 if (y_offset)
1021 *y_offset = 0;
1022
1023 if (cell_area && size.x > 0 && size.y > 0)
1024 {
1025 if (x_offset)
1026 {
1027 *x_offset = (gint)((renderer->xalign *
1028 (cell_area->width - calc_width - 2 * renderer->xpad)));
1029 *x_offset = MAX (*x_offset, 0) + renderer->xpad;
1030 }
1031 if (y_offset)
1032 {
1033 *y_offset = (gint)((renderer->yalign *
1034 (cell_area->height - calc_height - 2 * renderer->ypad)));
1035 *y_offset = MAX (*y_offset, 0) + renderer->ypad;
1036 }
1037 }
1038
1039 if (width)
1040 *width = calc_width;
1041
1042 if (height)
1043 *height = calc_height;
1044 }
1045
1046 static void
1047 gtk_wx_cell_renderer_render (GtkCellRenderer *renderer,
1048 GdkWindow *window,
1049 GtkWidget *widget,
1050 GdkRectangle *background_area,
1051 GdkRectangle *cell_area,
1052 GdkRectangle *expose_area,
1053 GtkCellRendererState flags)
1054
1055 {
1056 GtkWxCellRenderer *wxrenderer = (GtkWxCellRenderer *) renderer;
1057 wxDataViewCustomRenderer *cell = wxrenderer->cell;
1058
1059 GdkRectangle rect;
1060 gtk_wx_cell_renderer_get_size (renderer, widget, cell_area,
1061 &rect.x,
1062 &rect.y,
1063 &rect.width,
1064 &rect.height);
1065
1066 rect.x += cell_area->x;
1067 rect.y += cell_area->y;
1068 rect.width -= renderer->xpad * 2;
1069 rect.height -= renderer->ypad * 2;
1070
1071 GdkRectangle dummy;
1072 if (gdk_rectangle_intersect (expose_area, &rect, &dummy))
1073 {
1074 wxRect renderrect( rect.x, rect.y, rect.width, rect.height );
1075 wxWindowDC* dc = (wxWindowDC*) cell->GetDC();
1076 if (dc->m_window == NULL)
1077 {
1078 dc->m_window = window;
1079 dc->SetUpDC();
1080 }
1081
1082 int state = 0;
1083 if (flags & GTK_CELL_RENDERER_SELECTED)
1084 state |= wxDATAVIEW_CELL_SELECTED;
1085 if (flags & GTK_CELL_RENDERER_PRELIT)
1086 state |= wxDATAVIEW_CELL_PRELIT;
1087 if (flags & GTK_CELL_RENDERER_INSENSITIVE)
1088 state |= wxDATAVIEW_CELL_INSENSITIVE;
1089 if (flags & GTK_CELL_RENDERER_INSENSITIVE)
1090 state |= wxDATAVIEW_CELL_INSENSITIVE;
1091 if (flags & GTK_CELL_RENDERER_FOCUSED)
1092 state |= wxDATAVIEW_CELL_FOCUSED;
1093 cell->Render( renderrect, dc, state );
1094 }
1095 }
1096
1097 static gboolean
1098 gtk_wx_cell_renderer_activate(
1099 GtkCellRenderer *renderer,
1100 GdkEvent *event,
1101 GtkWidget *widget,
1102 const gchar *path,
1103 GdkRectangle *background_area,
1104 GdkRectangle *cell_area,
1105 GtkCellRendererState flags )
1106 {
1107 GtkWxCellRenderer *wxrenderer = (GtkWxCellRenderer *) renderer;
1108 wxDataViewCustomRenderer *cell = wxrenderer->cell;
1109
1110 GdkRectangle rect;
1111 gtk_wx_cell_renderer_get_size (renderer, widget, cell_area,
1112 &rect.x,
1113 &rect.y,
1114 &rect.width,
1115 &rect.height);
1116
1117 rect.x += cell_area->x;
1118 rect.y += cell_area->y;
1119 rect.width -= renderer->xpad * 2;
1120 rect.height -= renderer->ypad * 2;
1121
1122 wxRect renderrect( rect.x, rect.y, rect.width, rect.height );
1123
1124 wxDataViewModel *model = cell->GetOwner()->GetOwner()->GetModel();
1125
1126 GtkTreePath *treepath = gtk_tree_path_new_from_string( path );
1127 // TODO
1128 wxDataViewItem item;
1129 gtk_tree_path_free( treepath );
1130
1131 unsigned int model_col = cell->GetOwner()->GetModelColumn();
1132
1133 if (!event)
1134 {
1135 bool ret = false;
1136
1137 // activated by <ENTER>
1138 if (cell->Activate( renderrect, model, item, model_col ))
1139 ret = true;
1140
1141 return ret;
1142 }
1143 else if (event->type == GDK_BUTTON_PRESS)
1144 {
1145 GdkEventButton *button_event = (GdkEventButton*) event;
1146 wxPoint pt( ((int) button_event->x) - renderrect.x,
1147 ((int) button_event->y) - renderrect.y );
1148
1149 bool ret = false;
1150 if (button_event->button == 1)
1151 {
1152 if (cell->LeftClick( pt, renderrect, model, item, model_col ))
1153 ret = true;
1154 // TODO: query system double-click time
1155 if (button_event->time - wxrenderer->last_click < 400)
1156 if (cell->Activate( renderrect, model, item, model_col ))
1157 ret = true;
1158 }
1159 if (button_event->button == 3)
1160 {
1161 if (cell->RightClick( pt, renderrect, model, item, model_col ))
1162 ret = true;
1163 }
1164
1165 wxrenderer->last_click = button_event->time;
1166
1167 return ret;
1168 }
1169
1170 return false;
1171 }
1172
1173 // ---------------------------------------------------------
1174 // wxGtkDataViewModelNotifier
1175 // ---------------------------------------------------------
1176
1177 class wxGtkDataViewModelNotifier: public wxDataViewModelNotifier
1178 {
1179 public:
1180 wxGtkDataViewModelNotifier( GtkWxTreeModel *wxgtk_model,
1181 wxDataViewModel *wx_model,
1182 wxDataViewCtrl *ctrl );
1183 ~wxGtkDataViewModelNotifier();
1184
1185 virtual bool ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item );
1186 virtual bool ItemDeleted( const wxDataViewItem &item );
1187 virtual bool ItemChanged( const wxDataViewItem &item );
1188 virtual bool ValueChanged( const wxDataViewItem &item, unsigned int col );
1189 virtual bool Cleared();
1190
1191 GtkWxTreeModel *m_wxgtk_model;
1192 wxDataViewModel *m_wx_model;
1193 wxDataViewCtrl *m_owner;
1194 };
1195
1196 // ---------------------------------------------------------
1197 // wxGtkDataViewListModelNotifier
1198 // ---------------------------------------------------------
1199
1200 wxGtkDataViewModelNotifier::wxGtkDataViewModelNotifier(
1201 GtkWxTreeModel* wxgtk_model, wxDataViewModel *wx_model,
1202 wxDataViewCtrl *ctrl )
1203 {
1204 m_wxgtk_model = wxgtk_model;
1205 m_wx_model = wx_model;
1206 m_owner = ctrl;
1207 }
1208
1209 wxGtkDataViewModelNotifier::~wxGtkDataViewModelNotifier()
1210 {
1211 m_wx_model = NULL;
1212 m_wxgtk_model = NULL;
1213 }
1214
1215 bool wxGtkDataViewModelNotifier::ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item )
1216 {
1217 m_wxgtk_model->model->ItemAdded( parent, item );
1218
1219 GtkTreeIter iter;
1220 iter.stamp = m_wxgtk_model->stamp;
1221 iter.user_data = (gpointer) item.GetID();
1222
1223 GtkTreePath *path = wxgtk_tree_model_get_path(
1224 GTK_TREE_MODEL(m_wxgtk_model), &iter );
1225 gtk_tree_model_row_inserted(
1226 GTK_TREE_MODEL(m_wxgtk_model), path, &iter);
1227 gtk_tree_path_free (path);
1228
1229 return true;
1230 }
1231
1232 bool wxGtkDataViewModelNotifier::ItemDeleted( const wxDataViewItem &item )
1233 {
1234 GtkTreeIter iter;
1235 iter.stamp = m_wxgtk_model->stamp;
1236 iter.user_data = (gpointer) item.GetID();
1237
1238 GtkTreePath *path = wxgtk_tree_model_get_path(
1239 GTK_TREE_MODEL(m_wxgtk_model), &iter );
1240 gtk_tree_model_row_deleted(
1241 GTK_TREE_MODEL(m_wxgtk_model), path );
1242 gtk_tree_path_free (path);
1243
1244 m_wxgtk_model->model->ItemDeleted( item );
1245
1246 return true;
1247 }
1248
1249 bool wxGtkDataViewModelNotifier::ItemChanged( const wxDataViewItem &item )
1250 {
1251 GtkTreeIter iter;
1252 iter.stamp = m_wxgtk_model->stamp;
1253 iter.user_data = (gpointer) item.GetID();
1254
1255 GtkTreePath *path = wxgtk_tree_model_get_path(
1256 GTK_TREE_MODEL(m_wxgtk_model), &iter );
1257 gtk_tree_model_row_changed(
1258 GTK_TREE_MODEL(m_wxgtk_model), path, &iter );
1259 gtk_tree_path_free (path);
1260
1261 return true;
1262 }
1263
1264 bool wxGtkDataViewModelNotifier::ValueChanged( const wxDataViewItem &item, unsigned int model_col )
1265 {
1266 // This adds GTK+'s missing MVC logic for ValueChanged
1267 unsigned int index;
1268 for (index = 0; index < m_owner->GetColumnCount(); index++)
1269 {
1270 wxDataViewColumn *column = m_owner->GetColumn( index );
1271 if (column->GetModelColumn() == model_col)
1272 {
1273 GtkTreeView *widget = GTK_TREE_VIEW(m_owner->m_treeview);
1274 GtkTreeViewColumn *gcolumn = GTK_TREE_VIEW_COLUMN(column->GetGtkHandle());
1275
1276 // Get cell area
1277 GtkTreeIter iter;
1278 iter.stamp = m_wxgtk_model->stamp;
1279 iter.user_data = (gpointer) item.GetID();
1280 GtkTreePath *path = wxgtk_tree_model_get_path(
1281 GTK_TREE_MODEL(m_wxgtk_model), &iter );
1282 GdkRectangle cell_area;
1283 gtk_tree_view_get_cell_area( widget, path, gcolumn, &cell_area );
1284 gtk_tree_path_free( path );
1285
1286 GtkAdjustment* hadjust = gtk_tree_view_get_hadjustment( widget );
1287 double d = gtk_adjustment_get_value( hadjust );
1288 int xdiff = (int) d;
1289
1290 int ydiff = gcolumn->button->allocation.height;
1291 // Redraw
1292 gtk_widget_queue_draw_area( GTK_WIDGET(widget),
1293 cell_area.x - xdiff, ydiff + cell_area.y, cell_area.width, cell_area.height );
1294 }
1295 }
1296
1297 return true;
1298 }
1299
1300 bool wxGtkDataViewModelNotifier::Cleared()
1301 {
1302 return false;
1303 }
1304
1305 // ---------------------------------------------------------
1306 // wxDataViewRenderer
1307 // ---------------------------------------------------------
1308
1309 IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase)
1310
1311 wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype, wxDataViewCellMode mode,
1312 int align ) :
1313 wxDataViewRendererBase( varianttype, mode, align )
1314 {
1315 m_renderer = NULL;
1316
1317 // NOTE: SetMode() and SetAlignment() needs to be called in the renderer's ctor,
1318 // after the m_renderer pointer has been initialized
1319 }
1320
1321 void wxDataViewRenderer::SetMode( wxDataViewCellMode mode )
1322 {
1323 GtkCellRendererMode gtkMode;
1324 switch (mode)
1325 {
1326 case wxDATAVIEW_CELL_INERT:
1327 gtkMode = GTK_CELL_RENDERER_MODE_INERT;
1328 break;
1329 case wxDATAVIEW_CELL_ACTIVATABLE:
1330 gtkMode = GTK_CELL_RENDERER_MODE_ACTIVATABLE;
1331 break;
1332 case wxDATAVIEW_CELL_EDITABLE:
1333 gtkMode = GTK_CELL_RENDERER_MODE_EDITABLE;
1334 break;
1335 }
1336
1337 GValue gvalue = { 0, };
1338 g_value_init( &gvalue, gtk_cell_renderer_mode_get_type() );
1339 g_value_set_enum( &gvalue, gtkMode );
1340 g_object_set_property( G_OBJECT(m_renderer), "mode", &gvalue );
1341 g_value_unset( &gvalue );
1342 }
1343
1344 wxDataViewCellMode wxDataViewRenderer::GetMode() const
1345 {
1346 wxDataViewCellMode ret;
1347
1348 GValue gvalue;
1349 g_object_get( G_OBJECT(m_renderer), "mode", &gvalue, NULL);
1350
1351 switch (g_value_get_enum(&gvalue))
1352 {
1353 case GTK_CELL_RENDERER_MODE_INERT:
1354 ret = wxDATAVIEW_CELL_INERT;
1355 break;
1356 case GTK_CELL_RENDERER_MODE_ACTIVATABLE:
1357 ret = wxDATAVIEW_CELL_ACTIVATABLE;
1358 break;
1359 case GTK_CELL_RENDERER_MODE_EDITABLE:
1360 ret = wxDATAVIEW_CELL_EDITABLE;
1361 break;
1362 }
1363
1364 g_value_unset( &gvalue );
1365
1366 return ret;
1367 }
1368
1369 void wxDataViewRenderer::SetAlignment( int align )
1370 {
1371 // horizontal alignment:
1372
1373 gfloat xalign = 0.0;
1374 if (align & wxALIGN_RIGHT)
1375 xalign = 1.0;
1376 else if (align & wxALIGN_CENTER_HORIZONTAL)
1377 xalign = 0.5;
1378
1379 GValue gvalue = { 0, };
1380 g_value_init( &gvalue, G_TYPE_FLOAT );
1381 g_value_set_float( &gvalue, xalign );
1382 g_object_set_property( G_OBJECT(m_renderer), "xalign", &gvalue );
1383 g_value_unset( &gvalue );
1384
1385 // vertical alignment:
1386
1387 gfloat yalign = 0.0;
1388 if (align & wxALIGN_BOTTOM)
1389 yalign = 1.0;
1390 else if (align & wxALIGN_CENTER_VERTICAL)
1391 yalign = 0.5;
1392
1393 GValue gvalue2 = { 0, };
1394 g_value_init( &gvalue2, G_TYPE_FLOAT );
1395 g_value_set_float( &gvalue2, yalign );
1396 g_object_set_property( G_OBJECT(m_renderer), "yalign", &gvalue2 );
1397 g_value_unset( &gvalue2 );
1398 }
1399
1400 int wxDataViewRenderer::GetAlignment() const
1401 {
1402 int ret = 0;
1403 GValue gvalue;
1404
1405 // horizontal alignment:
1406
1407 g_object_get( G_OBJECT(m_renderer), "xalign", &gvalue, NULL );
1408 float xalign = g_value_get_float( &gvalue );
1409 if (xalign < 0.5)
1410 ret |= wxALIGN_LEFT;
1411 else if (xalign == 0.5)
1412 ret |= wxALIGN_CENTER_HORIZONTAL;
1413 else
1414 ret |= wxALIGN_RIGHT;
1415 g_value_unset( &gvalue );
1416
1417
1418 // vertical alignment:
1419
1420 g_object_get( G_OBJECT(m_renderer), "yalign", &gvalue, NULL );
1421 float yalign = g_value_get_float( &gvalue );
1422 if (yalign < 0.5)
1423 ret |= wxALIGN_TOP;
1424 else if (yalign == 0.5)
1425 ret |= wxALIGN_CENTER_VERTICAL;
1426 else
1427 ret |= wxALIGN_BOTTOM;
1428 g_value_unset( &gvalue );
1429
1430 return ret;
1431 }
1432
1433
1434
1435 // ---------------------------------------------------------
1436 // wxDataViewTextRenderer
1437 // ---------------------------------------------------------
1438
1439 extern "C" {
1440 static void wxGtkTextRendererEditedCallback( GtkCellRendererText *renderer,
1441 gchar *arg1, gchar *arg2, gpointer user_data );
1442 }
1443
1444 static void wxGtkTextRendererEditedCallback( GtkCellRendererText *renderer,
1445 gchar *arg1, gchar *arg2, gpointer user_data )
1446 {
1447 wxDataViewTextRenderer *cell = (wxDataViewTextRenderer*) user_data;
1448
1449 wxString tmp = wxGTK_CONV_BACK_FONT(arg2, cell->GetOwner()->GetOwner()->GetFont());
1450 wxVariant value = tmp;
1451 if (!cell->Validate( value ))
1452 return;
1453
1454 wxDataViewModel *model = cell->GetOwner()->GetOwner()->GetModel();
1455
1456 GtkTreePath *path = gtk_tree_path_new_from_string( arg1 );
1457 GtkTreeIter iter;
1458 // TODO
1459 wxDataViewItem item;
1460 gtk_tree_path_free( path );
1461
1462 unsigned int model_col = cell->GetOwner()->GetModelColumn();
1463
1464 model->SetValue( value, item, model_col );
1465 model->ValueChanged( item, model_col );
1466 }
1467
1468 IMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewRenderer)
1469
1470 wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype, wxDataViewCellMode mode,
1471 int align ) :
1472 wxDataViewRenderer( varianttype, mode, align )
1473 {
1474 m_renderer = (GtkCellRenderer*) gtk_cell_renderer_text_new();
1475
1476 if (mode & wxDATAVIEW_CELL_EDITABLE)
1477 {
1478 GValue gvalue = { 0, };
1479 g_value_init( &gvalue, G_TYPE_BOOLEAN );
1480 g_value_set_boolean( &gvalue, true );
1481 g_object_set_property( G_OBJECT(m_renderer), "editable", &gvalue );
1482 g_value_unset( &gvalue );
1483
1484 g_signal_connect_after( m_renderer, "edited", G_CALLBACK(wxGtkTextRendererEditedCallback), this );
1485 }
1486
1487 SetMode(mode);
1488 SetAlignment(align);
1489 }
1490
1491 bool wxDataViewTextRenderer::SetValue( const wxVariant &value )
1492 {
1493 wxString tmp = value;
1494
1495 GValue gvalue = { 0, };
1496 g_value_init( &gvalue, G_TYPE_STRING );
1497 g_value_set_string( &gvalue, wxGTK_CONV_FONT( tmp, GetOwner()->GetOwner()->GetFont() ) );
1498 g_object_set_property( G_OBJECT(m_renderer), "text", &gvalue );
1499 g_value_unset( &gvalue );
1500
1501 return true;
1502 }
1503
1504 bool wxDataViewTextRenderer::GetValue( wxVariant &value ) const
1505 {
1506 GValue gvalue = { 0, };
1507 g_value_init( &gvalue, G_TYPE_STRING );
1508 g_object_get_property( G_OBJECT(m_renderer), "text", &gvalue );
1509 wxString tmp = wxGTK_CONV_BACK_FONT( g_value_get_string( &gvalue ),
1510 wx_const_cast(wxDataViewTextRenderer*, this)->GetOwner()->GetOwner()->GetFont() );
1511 g_value_unset( &gvalue );
1512
1513 value = tmp;
1514
1515 return true;
1516 }
1517
1518 void wxDataViewTextRenderer::SetAlignment( int align )
1519 {
1520 wxDataViewRenderer::SetAlignment(align);
1521
1522 if (gtk_check_version(2,10,0))
1523 return;
1524
1525 // horizontal alignment:
1526 PangoAlignment pangoAlign = PANGO_ALIGN_LEFT;
1527 if (align & wxALIGN_RIGHT)
1528 pangoAlign = PANGO_ALIGN_RIGHT;
1529 else if (align & wxALIGN_CENTER_HORIZONTAL)
1530 pangoAlign = PANGO_ALIGN_CENTER;
1531
1532 GValue gvalue = { 0, };
1533 g_value_init( &gvalue, gtk_cell_renderer_mode_get_type() );
1534 g_value_set_enum( &gvalue, pangoAlign );
1535 g_object_set_property( G_OBJECT(m_renderer), "alignment", &gvalue );
1536 g_value_unset( &gvalue );
1537 }
1538
1539 // ---------------------------------------------------------
1540 // wxDataViewBitmapRenderer
1541 // ---------------------------------------------------------
1542
1543 IMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewRenderer)
1544
1545 wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype, wxDataViewCellMode mode,
1546 int align ) :
1547 wxDataViewRenderer( varianttype, mode, align )
1548 {
1549 m_renderer = (GtkCellRenderer*) gtk_cell_renderer_pixbuf_new();
1550
1551 SetMode(mode);
1552 SetAlignment(align);
1553 }
1554
1555 bool wxDataViewBitmapRenderer::SetValue( const wxVariant &value )
1556 {
1557 if (value.GetType() == wxT("wxBitmap"))
1558 {
1559 wxBitmap bitmap;
1560 bitmap << value;
1561
1562 // This may create a Pixbuf representation in the
1563 // wxBitmap object (and it will stay there)
1564 GdkPixbuf *pixbuf = bitmap.GetPixbuf();
1565
1566 GValue gvalue = { 0, };
1567 g_value_init( &gvalue, G_TYPE_OBJECT );
1568 g_value_set_object( &gvalue, pixbuf );
1569 g_object_set_property( G_OBJECT(m_renderer), "pixbuf", &gvalue );
1570 g_value_unset( &gvalue );
1571
1572 return true;
1573 }
1574
1575 if (value.GetType() == wxT("wxIcon"))
1576 {
1577 wxIcon bitmap;
1578 bitmap << value;
1579
1580 // This may create a Pixbuf representation in the
1581 // wxBitmap object (and it will stay there)
1582 GdkPixbuf *pixbuf = bitmap.GetPixbuf();
1583
1584 GValue gvalue = { 0, };
1585 g_value_init( &gvalue, G_TYPE_OBJECT );
1586 g_value_set_object( &gvalue, pixbuf );
1587 g_object_set_property( G_OBJECT(m_renderer), "pixbuf", &gvalue );
1588 g_value_unset( &gvalue );
1589
1590 return true;
1591 }
1592
1593 return false;
1594 }
1595
1596 bool wxDataViewBitmapRenderer::GetValue( wxVariant &value ) const
1597 {
1598 return false;
1599 }
1600
1601 // ---------------------------------------------------------
1602 // wxDataViewToggleRenderer
1603 // ---------------------------------------------------------
1604
1605 extern "C" {
1606 static void wxGtkToggleRendererToggledCallback( GtkCellRendererToggle *renderer,
1607 gchar *path, gpointer user_data );
1608 }
1609
1610 static void wxGtkToggleRendererToggledCallback( GtkCellRendererToggle *renderer,
1611 gchar *path, gpointer user_data )
1612 {
1613 wxDataViewToggleRenderer *cell = (wxDataViewToggleRenderer*) user_data;
1614
1615 // get old value
1616 GValue gvalue = { 0, };
1617 g_value_init( &gvalue, G_TYPE_BOOLEAN );
1618 g_object_get_property( G_OBJECT(renderer), "active", &gvalue );
1619 bool tmp = g_value_get_boolean( &gvalue );
1620 g_value_unset( &gvalue );
1621 // invert it
1622 tmp = !tmp;
1623
1624 wxVariant value = tmp;
1625 if (!cell->Validate( value ))
1626 return;
1627
1628 wxDataViewModel *model = cell->GetOwner()->GetOwner()->GetModel();
1629
1630 GtkTreePath *gtk_path = gtk_tree_path_new_from_string( path );
1631 // TODO
1632 wxDataViewItem item;
1633 gtk_tree_path_free( gtk_path );
1634
1635 unsigned int model_col = cell->GetOwner()->GetModelColumn();
1636
1637 model->SetValue( value, item, model_col );
1638 model->ValueChanged( item, model_col );
1639 }
1640
1641 IMPLEMENT_CLASS(wxDataViewToggleRenderer, wxDataViewRenderer)
1642
1643 wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString &varianttype,
1644 wxDataViewCellMode mode, int align ) :
1645 wxDataViewRenderer( varianttype, mode, align )
1646 {
1647 m_renderer = (GtkCellRenderer*) gtk_cell_renderer_toggle_new();
1648
1649 if (mode & wxDATAVIEW_CELL_ACTIVATABLE)
1650 {
1651 g_signal_connect_after( m_renderer, "toggled",
1652 G_CALLBACK(wxGtkToggleRendererToggledCallback), this );
1653 }
1654 else
1655 {
1656 GValue gvalue = { 0, };
1657 g_value_init( &gvalue, G_TYPE_BOOLEAN );
1658 g_value_set_boolean( &gvalue, false );
1659 g_object_set_property( G_OBJECT(m_renderer), "activatable", &gvalue );
1660 g_value_unset( &gvalue );
1661 }
1662
1663 SetMode(mode);
1664 SetAlignment(align);
1665 }
1666
1667 bool wxDataViewToggleRenderer::SetValue( const wxVariant &value )
1668 {
1669 bool tmp = value;
1670
1671 GValue gvalue = { 0, };
1672 g_value_init( &gvalue, G_TYPE_BOOLEAN );
1673 g_value_set_boolean( &gvalue, tmp );
1674 g_object_set_property( G_OBJECT(m_renderer), "active", &gvalue );
1675 g_value_unset( &gvalue );
1676
1677 return true;
1678 }
1679
1680 bool wxDataViewToggleRenderer::GetValue( wxVariant &value ) const
1681 {
1682 GValue gvalue = { 0, };
1683 g_value_init( &gvalue, G_TYPE_BOOLEAN );
1684 g_object_get_property( G_OBJECT(m_renderer), "active", &gvalue );
1685 bool tmp = g_value_get_boolean( &gvalue );
1686 g_value_unset( &gvalue );
1687
1688 value = tmp;
1689
1690 return true;
1691 }
1692
1693 // ---------------------------------------------------------
1694 // wxDataViewCustomRenderer
1695 // ---------------------------------------------------------
1696
1697 class wxDataViewCtrlDC: public wxWindowDC
1698 {
1699 public:
1700 wxDataViewCtrlDC( wxDataViewCtrl *window )
1701 {
1702 GtkWidget *widget = window->m_treeview;
1703 // Set later
1704 m_window = NULL;
1705
1706 m_context = window->GtkGetPangoDefaultContext();
1707 m_layout = pango_layout_new( m_context );
1708 m_fontdesc = pango_font_description_copy( widget->style->font_desc );
1709
1710 m_cmap = gtk_widget_get_colormap( widget ? widget : window->m_widget );
1711
1712 // Set m_window later
1713 // SetUpDC();
1714 // m_owner = window;
1715 }
1716 };
1717
1718 // ---------------------------------------------------------
1719 // wxDataViewCustomRenderer
1720 // ---------------------------------------------------------
1721
1722 IMPLEMENT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer)
1723
1724 wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype,
1725 wxDataViewCellMode mode, int align,
1726 bool no_init ) :
1727 wxDataViewRenderer( varianttype, mode, align )
1728 {
1729 m_dc = NULL;
1730
1731 if (no_init)
1732 m_renderer = NULL;
1733 else
1734 Init(mode, align);
1735 }
1736
1737 bool wxDataViewCustomRenderer::Init(wxDataViewCellMode mode, int align)
1738 {
1739 GtkWxCellRenderer *renderer = (GtkWxCellRenderer *) gtk_wx_cell_renderer_new();
1740 renderer->cell = this;
1741
1742 m_renderer = (GtkCellRenderer*) renderer;
1743
1744 SetMode(mode);
1745 SetAlignment(align);
1746
1747 return true;
1748 }
1749
1750 wxDataViewCustomRenderer::~wxDataViewCustomRenderer()
1751 {
1752 if (m_dc)
1753 delete m_dc;
1754 }
1755
1756 wxDC *wxDataViewCustomRenderer::GetDC()
1757 {
1758 if (m_dc == NULL)
1759 {
1760 if (GetOwner() == NULL)
1761 return NULL;
1762 if (GetOwner()->GetOwner() == NULL)
1763 return NULL;
1764 m_dc = new wxDataViewCtrlDC( GetOwner()->GetOwner() );
1765 }
1766
1767 return m_dc;
1768 }
1769
1770 // ---------------------------------------------------------
1771 // wxDataViewProgressRenderer
1772 // ---------------------------------------------------------
1773
1774 IMPLEMENT_CLASS(wxDataViewProgressRenderer, wxDataViewCustomRenderer)
1775
1776 wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString &label,
1777 const wxString &varianttype, wxDataViewCellMode mode, int align ) :
1778 wxDataViewCustomRenderer( varianttype, mode, align, true )
1779 {
1780 m_label = label;
1781 m_value = 0;
1782
1783 #ifdef __WXGTK26__
1784 if (!gtk_check_version(2,6,0))
1785 {
1786 m_renderer = (GtkCellRenderer*) gtk_cell_renderer_progress_new();
1787
1788 GValue gvalue = { 0, };
1789 g_value_init( &gvalue, G_TYPE_STRING );
1790
1791 // FIXME: font encoding support
1792 g_value_set_string( &gvalue, wxGTK_CONV_SYS(m_label) );
1793 g_object_set_property( G_OBJECT(m_renderer), "text", &gvalue );
1794 g_value_unset( &gvalue );
1795
1796 SetMode(mode);
1797 SetAlignment(align);
1798 }
1799 else
1800 #endif
1801 {
1802 // Use custom cell code
1803 wxDataViewCustomRenderer::Init(mode, align);
1804 }
1805 }
1806
1807 wxDataViewProgressRenderer::~wxDataViewProgressRenderer()
1808 {
1809 }
1810
1811 bool wxDataViewProgressRenderer::SetValue( const wxVariant &value )
1812 {
1813 #ifdef __WXGTK26__
1814 if (!gtk_check_version(2,6,0))
1815 {
1816 gint tmp = (long) value;
1817 GValue gvalue = { 0, };
1818 g_value_init( &gvalue, G_TYPE_INT );
1819 g_value_set_int( &gvalue, tmp );
1820 g_object_set_property( G_OBJECT(m_renderer), "value", &gvalue );
1821 g_value_unset( &gvalue );
1822 }
1823 else
1824 #endif
1825 {
1826 m_value = (long) value;
1827
1828 if (m_value < 0) m_value = 0;
1829 if (m_value > 100) m_value = 100;
1830 }
1831
1832 return true;
1833 }
1834
1835 bool wxDataViewProgressRenderer::GetValue( wxVariant &value ) const
1836 {
1837 return false;
1838 }
1839
1840 bool wxDataViewProgressRenderer::Render( wxRect cell, wxDC *dc, int state )
1841 {
1842 double pct = (double)m_value / 100.0;
1843 wxRect bar = cell;
1844 bar.width = (int)(cell.width * pct);
1845 dc->SetPen( *wxTRANSPARENT_PEN );
1846 dc->SetBrush( *wxBLUE_BRUSH );
1847 dc->DrawRectangle( bar );
1848
1849 dc->SetBrush( *wxTRANSPARENT_BRUSH );
1850 dc->SetPen( *wxBLACK_PEN );
1851 dc->DrawRectangle( cell );
1852
1853 return true;
1854 }
1855
1856 wxSize wxDataViewProgressRenderer::GetSize() const
1857 {
1858 return wxSize(40,12);
1859 }
1860
1861 // ---------------------------------------------------------
1862 // wxDataViewDateRenderer
1863 // ---------------------------------------------------------
1864
1865 class wxDataViewDateRendererPopupTransient: public wxPopupTransientWindow
1866 {
1867 public:
1868 wxDataViewDateRendererPopupTransient( wxWindow* parent, wxDateTime *value,
1869 wxDataViewModel *model, const wxDataViewItem &item, unsigned int col ) :
1870 wxPopupTransientWindow( parent, wxBORDER_SIMPLE )
1871 {
1872 m_model = model;
1873 m_item = item;
1874 m_col = col;
1875 m_cal = new wxCalendarCtrl( this, -1, *value );
1876 wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
1877 sizer->Add( m_cal, 1, wxGROW );
1878 SetSizer( sizer );
1879 sizer->Fit( this );
1880 }
1881
1882 virtual void OnDismiss()
1883 {
1884 }
1885
1886 void OnCalendar( wxCalendarEvent &event );
1887
1888 wxCalendarCtrl *m_cal;
1889 wxDataViewModel *m_model;
1890 wxDataViewItem m_item;
1891 unsigned int m_col;
1892
1893 private:
1894 DECLARE_EVENT_TABLE()
1895 };
1896
1897 BEGIN_EVENT_TABLE(wxDataViewDateRendererPopupTransient,wxPopupTransientWindow)
1898 EVT_CALENDAR( -1, wxDataViewDateRendererPopupTransient::OnCalendar )
1899 END_EVENT_TABLE()
1900
1901 void wxDataViewDateRendererPopupTransient::OnCalendar( wxCalendarEvent &event )
1902 {
1903 wxDateTime date = event.GetDate();
1904 wxVariant value = date;
1905 m_model->SetValue( value, m_item, m_col );
1906 m_model->ValueChanged( m_item, m_col );
1907 DismissAndNotify();
1908 }
1909
1910 IMPLEMENT_CLASS(wxDataViewDateRenderer, wxDataViewCustomRenderer)
1911
1912 wxDataViewDateRenderer::wxDataViewDateRenderer( const wxString &varianttype,
1913 wxDataViewCellMode mode, int align ) :
1914 wxDataViewCustomRenderer( varianttype, mode, align )
1915 {
1916 SetMode(mode);
1917 SetAlignment(align);
1918 }
1919
1920 bool wxDataViewDateRenderer::SetValue( const wxVariant &value )
1921 {
1922 m_date = value.GetDateTime();
1923
1924 return true;
1925 }
1926
1927 bool wxDataViewDateRenderer::GetValue( wxVariant &value ) const
1928 {
1929 return false;
1930 }
1931
1932 bool wxDataViewDateRenderer::Render( wxRect cell, wxDC *dc, int state )
1933 {
1934 dc->SetFont( GetOwner()->GetOwner()->GetFont() );
1935 wxString tmp = m_date.FormatDate();
1936 dc->DrawText( tmp, cell.x, cell.y );
1937
1938 return true;
1939 }
1940
1941 wxSize wxDataViewDateRenderer::GetSize() const
1942 {
1943 wxString tmp = m_date.FormatDate();
1944 wxCoord x,y,d;
1945 GetView()->GetTextExtent( tmp, &x, &y, &d );
1946 return wxSize(x,y+d);
1947 }
1948
1949 bool wxDataViewDateRenderer::Activate( wxRect cell, wxDataViewModel *model,
1950 const wxDataViewItem &item, unsigned int col )
1951 {
1952 wxVariant variant;
1953 model->GetValue( variant, item, col );
1954 wxDateTime value = variant.GetDateTime();
1955
1956 wxDataViewDateRendererPopupTransient *popup = new wxDataViewDateRendererPopupTransient(
1957 GetOwner()->GetOwner()->GetParent(), &value, model, item, col );
1958 wxPoint pos = wxGetMousePosition();
1959 popup->Move( pos );
1960 popup->Layout();
1961 popup->Popup( popup->m_cal );
1962
1963 return true;
1964 }
1965
1966 // ---------------------------------------------------------
1967 // wxDataViewColumn
1968 // ---------------------------------------------------------
1969
1970
1971 static gboolean
1972 gtk_dataview_header_button_press_callback( GtkWidget *widget,
1973 GdkEventButton *gdk_event,
1974 wxDataViewColumn *column )
1975 {
1976 if (gdk_event->type != GDK_BUTTON_PRESS)
1977 return FALSE;
1978
1979 if (gdk_event->button == 1)
1980 {
1981 wxDataViewCtrl *dv = column->GetOwner();
1982 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK, dv->GetId() );
1983 event.SetDataViewColumn( column );
1984 event.SetModel( dv->GetModel() );
1985 if (dv->GetEventHandler()->ProcessEvent( event ))
1986 return TRUE;
1987 }
1988
1989 return FALSE;
1990 }
1991
1992 extern "C" {
1993 static void wxGtkTreeCellDataFunc( GtkTreeViewColumn *column,
1994 GtkCellRenderer *cell,
1995 GtkTreeModel *model,
1996 GtkTreeIter *iter,
1997 gpointer data );
1998 }
1999
2000
2001 static void wxGtkTreeCellDataFunc( GtkTreeViewColumn *column,
2002 GtkCellRenderer *renderer,
2003 GtkTreeModel *model,
2004 GtkTreeIter *iter,
2005 gpointer data )
2006 {
2007 g_return_if_fail (GTK_IS_WX_TREE_MODEL (model));
2008 GtkWxTreeModel *tree_model = (GtkWxTreeModel *) model;
2009
2010 wxDataViewRenderer *cell = (wxDataViewRenderer*) data;
2011
2012 wxDataViewItem item( (void*) iter->user_data );
2013
2014 wxVariant value;
2015 tree_model->model->GetDataViewModel()->GetValue( value, item, cell->GetOwner()->GetModelColumn() );
2016
2017 if (value.GetType() != cell->GetVariantType())
2018 wxLogError( wxT("Wrong type, required: %s but: %s"),
2019 value.GetType().c_str(),
2020 cell->GetVariantType().c_str() );
2021
2022 cell->SetValue( value );
2023
2024 #if 0
2025 wxListItemAttr attr;
2026 tree_model->model->GetAttr( attr, cell->GetOwner()->GetModelColumn(), model_row );
2027
2028 if (attr.HasBackgroundColour())
2029 {
2030 wxColour colour = attr.GetBackgroundColour();
2031 const GdkColor * const gcol = colour.GetColor();
2032
2033 GValue gvalue = { 0, };
2034 g_value_init( &gvalue, GDK_TYPE_COLOR );
2035 g_value_set_boxed( &gvalue, gcol );
2036 g_object_set_property( G_OBJECT(renderer), "cell-background_gdk", &gvalue );
2037 g_value_unset( &gvalue );
2038 }
2039 else
2040 {
2041 GValue gvalue = { 0, };
2042 g_value_init( &gvalue, G_TYPE_BOOLEAN );
2043 g_value_set_boolean( &gvalue, FALSE );
2044 g_object_set_property( G_OBJECT(renderer), "cell-background-set", &gvalue );
2045 g_value_unset( &gvalue );
2046 }
2047 #endif
2048
2049 }
2050
2051 IMPLEMENT_CLASS(wxDataViewColumn, wxDataViewColumnBase)
2052
2053 wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewRenderer *cell,
2054 unsigned int model_column, int width,
2055 wxAlignment align, int flags ) :
2056 wxDataViewColumnBase( title, cell, model_column, width, align, flags )
2057 {
2058 Init( align, flags, width );
2059
2060 gtk_tree_view_column_set_clickable( GTK_TREE_VIEW_COLUMN(m_column), TRUE );
2061 SetTitle( title );
2062 }
2063
2064 wxDataViewColumn::wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *cell,
2065 unsigned int model_column, int width,
2066 wxAlignment align, int flags ) :
2067 wxDataViewColumnBase( bitmap, cell, model_column, width, align, flags )
2068 {
2069 Init( align, flags, width );
2070
2071 SetBitmap( bitmap );
2072 }
2073
2074 void wxDataViewColumn::Init(wxAlignment align, int flags, int width)
2075 {
2076 m_isConnected = false;
2077
2078 GtkCellRenderer *renderer = (GtkCellRenderer *) GetRenderer()->GetGtkHandle();
2079 GtkTreeViewColumn *column = gtk_tree_view_column_new();
2080 m_column = (GtkWidget*) column;
2081
2082 SetFlags( flags );
2083 SetAlignment( align );
2084
2085 // NOTE: we prefer not to call SetMinWidth(wxDVC_DEFAULT_MINWIDTH);
2086 // as GTK+ is smart and unless explicitely told, will set the minimal
2087 // width to the title's lenght, which is a better default
2088
2089 // the GTK_TREE_VIEW_COLUMN_FIXED is required by the "fixed height" mode
2090 // that we use for the wxDataViewCtrl
2091 gtk_tree_view_column_set_fixed_width( column, width < 0 ? wxDVC_DEFAULT_WIDTH : width );
2092 gtk_tree_view_column_set_sizing( column, GTK_TREE_VIEW_COLUMN_FIXED );
2093
2094 gtk_tree_view_column_pack_end( column, renderer, TRUE );
2095
2096 gtk_tree_view_column_set_cell_data_func( column, renderer,
2097 wxGtkTreeCellDataFunc, (gpointer) GetRenderer(), NULL );
2098 }
2099
2100 wxDataViewColumn::~wxDataViewColumn()
2101 {
2102 }
2103
2104 void wxDataViewColumn::OnInternalIdle()
2105 {
2106 if (m_isConnected)
2107 return;
2108
2109 if (GTK_WIDGET_REALIZED(GetOwner()->m_treeview))
2110 {
2111 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
2112 if (column->button)
2113 {
2114 g_signal_connect(column->button, "button_press_event",
2115 G_CALLBACK (gtk_dataview_header_button_press_callback), this);
2116
2117 m_isConnected = true;
2118 }
2119 }
2120 }
2121
2122 void wxDataViewColumn::SetOwner( wxDataViewCtrl *owner )
2123 {
2124 wxDataViewColumnBase::SetOwner( owner );
2125
2126 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
2127
2128 gtk_tree_view_column_set_title( column, wxGTK_CONV_FONT(GetTitle(), GetOwner()->GetFont() ) );
2129 }
2130
2131 void wxDataViewColumn::SetTitle( const wxString &title )
2132 {
2133 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
2134
2135 if (m_isConnected)
2136 {
2137 // disconnect before column->button gets recreated
2138 g_signal_handlers_disconnect_by_func( column->button,
2139 (GtkWidget*) gtk_dataview_header_button_press_callback, this);
2140
2141 m_isConnected = false;
2142 }
2143
2144 // FIXME: can it really happen that we don't have the owner here??
2145 wxDataViewCtrl *ctrl = GetOwner();
2146 gtk_tree_view_column_set_title( column, ctrl ? wxGTK_CONV_FONT(title, ctrl->GetFont())
2147 : wxGTK_CONV_SYS(title) );
2148
2149 gtk_tree_view_column_set_widget( column, NULL );
2150 }
2151
2152 wxString wxDataViewColumn::GetTitle() const
2153 {
2154 const gchar *str = gtk_tree_view_column_get_title( GTK_TREE_VIEW_COLUMN(m_column) );
2155 return wxConvFileName->cMB2WX(str);
2156 }
2157
2158 void wxDataViewColumn::SetBitmap( const wxBitmap &bitmap )
2159 {
2160 wxDataViewColumnBase::SetBitmap( bitmap );
2161
2162 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
2163 if (bitmap.Ok())
2164 {
2165 GtkImage *gtk_image = GTK_IMAGE( gtk_image_new() );
2166
2167 GdkBitmap *mask = (GdkBitmap *) NULL;
2168 if (bitmap.GetMask())
2169 mask = bitmap.GetMask()->GetBitmap();
2170
2171 if (bitmap.HasPixbuf())
2172 {
2173 gtk_image_set_from_pixbuf(GTK_IMAGE(gtk_image),
2174 bitmap.GetPixbuf());
2175 }
2176 else
2177 {
2178 gtk_image_set_from_pixmap(GTK_IMAGE(gtk_image),
2179 bitmap.GetPixmap(), mask);
2180 }
2181 gtk_widget_show( GTK_WIDGET(gtk_image) );
2182
2183 gtk_tree_view_column_set_widget( column, GTK_WIDGET(gtk_image) );
2184 }
2185 else
2186 {
2187 gtk_tree_view_column_set_widget( column, NULL );
2188 }
2189 }
2190
2191 void wxDataViewColumn::SetHidden( bool hidden )
2192 {
2193 gtk_tree_view_column_set_visible( GTK_TREE_VIEW_COLUMN(m_column), !hidden );
2194 }
2195
2196 void wxDataViewColumn::SetResizeable( bool resizeable )
2197 {
2198 gtk_tree_view_column_set_resizable( GTK_TREE_VIEW_COLUMN(m_column), resizeable );
2199 }
2200
2201 void wxDataViewColumn::SetAlignment( wxAlignment align )
2202 {
2203 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
2204
2205 gfloat xalign = 0.0;
2206 if (align == wxALIGN_RIGHT)
2207 xalign = 1.0;
2208 if (align == wxALIGN_CENTER_HORIZONTAL ||
2209 align == wxALIGN_CENTER)
2210 xalign = 0.5;
2211
2212 gtk_tree_view_column_set_alignment( column, xalign );
2213 }
2214
2215 wxAlignment wxDataViewColumn::GetAlignment() const
2216 {
2217 gfloat xalign = gtk_tree_view_column_get_alignment( GTK_TREE_VIEW_COLUMN(m_column) );
2218
2219 if (xalign == 1.0)
2220 return wxALIGN_RIGHT;
2221 if (xalign == 0.5)
2222 return wxALIGN_CENTER_HORIZONTAL;
2223
2224 return wxALIGN_LEFT;
2225 }
2226
2227 void wxDataViewColumn::SetSortable( bool sortable )
2228 {
2229 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
2230
2231 if (sortable)
2232 gtk_tree_view_column_set_sort_column_id( column, GetModelColumn() );
2233 else
2234 gtk_tree_view_column_set_sort_column_id( column, -1 );
2235 }
2236
2237 bool wxDataViewColumn::IsSortable() const
2238 {
2239 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
2240 return (gtk_tree_view_column_get_sort_column_id( column ) != -1);
2241 }
2242
2243 bool wxDataViewColumn::IsResizeable() const
2244 {
2245 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
2246 return gtk_tree_view_column_get_resizable( column );
2247 }
2248
2249 bool wxDataViewColumn::IsHidden() const
2250 {
2251 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
2252 return !gtk_tree_view_column_get_visible( column );
2253 }
2254
2255 void wxDataViewColumn::SetSortOrder( bool ascending )
2256 {
2257 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
2258
2259 if (ascending)
2260 gtk_tree_view_column_set_sort_order( column, GTK_SORT_ASCENDING );
2261 else
2262 gtk_tree_view_column_set_sort_order( column, GTK_SORT_DESCENDING );
2263 }
2264
2265 bool wxDataViewColumn::IsSortOrderAscending() const
2266 {
2267 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
2268
2269 return (gtk_tree_view_column_get_sort_order( column ) != GTK_SORT_DESCENDING);
2270 }
2271
2272 void wxDataViewColumn::SetMinWidth( int width )
2273 {
2274 gtk_tree_view_column_set_min_width( GTK_TREE_VIEW_COLUMN(m_column), width );
2275 }
2276
2277 int wxDataViewColumn::GetMinWidth() const
2278 {
2279 return gtk_tree_view_column_get_min_width( GTK_TREE_VIEW_COLUMN(m_column) );
2280 }
2281
2282 int wxDataViewColumn::GetWidth() const
2283 {
2284 return gtk_tree_view_column_get_width( GTK_TREE_VIEW_COLUMN(m_column) );
2285 }
2286
2287 void wxDataViewColumn::SetWidth( int width )
2288 {
2289 gtk_tree_view_column_set_fixed_width( GTK_TREE_VIEW_COLUMN(m_column), width );
2290 }
2291
2292
2293 //-----------------------------------------------------------------------------
2294 // wxDataViewCtrl signal callbacks
2295 //-----------------------------------------------------------------------------
2296
2297 static void
2298 wxdataview_selection_changed_callback( GtkTreeSelection* selection, wxDataViewCtrl *dv )
2299 {
2300 if (!GTK_WIDGET_REALIZED(dv->m_widget))
2301 return;
2302
2303 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_SELECTED, dv->GetId() );
2304 // TODO: item
2305 event.SetModel( dv->GetModel() );
2306 dv->GetEventHandler()->ProcessEvent( event );
2307 }
2308
2309 static void
2310 wxdataview_row_activated_callback( GtkTreeView* treeview, GtkTreePath *path,
2311 GtkTreeViewColumn *column, wxDataViewCtrl *dv )
2312 {
2313 wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, dv->GetId() );
2314 // TODO: item
2315 event.SetModel( dv->GetModel() );
2316 dv->GetEventHandler()->ProcessEvent( event );
2317 }
2318
2319 //-----------------------------------------------------------------------------
2320 // wxDataViewCtrl
2321 //-----------------------------------------------------------------------------
2322
2323 //-----------------------------------------------------------------------------
2324 // InsertChild for wxDataViewCtrl
2325 //-----------------------------------------------------------------------------
2326
2327 static void wxInsertChildInDataViewCtrl( wxWindowGTK* parent, wxWindowGTK* child )
2328 {
2329 wxDataViewCtrl * dvc = (wxDataViewCtrl*) parent;
2330 GtkWidget *treeview = dvc->GtkGetTreeView();
2331
2332 // Insert widget in GtkTreeView
2333 if (GTK_WIDGET_REALIZED(treeview))
2334 gtk_widget_set_parent_window( child->m_widget,
2335 gtk_tree_view_get_bin_window( GTK_TREE_VIEW(treeview) ) );
2336 gtk_widget_set_parent( child->m_widget, treeview );
2337 }
2338
2339 static
2340 void gtk_dataviewctrl_size_callback( GtkWidget *WXUNUSED(widget),
2341 GtkAllocation *alloc,
2342 wxDataViewCtrl *win )
2343 {
2344
2345 wxWindowList::Node *node = win->GetChildren().GetFirst();
2346 while (node)
2347 {
2348 wxWindow *child = node->GetData();
2349
2350 GtkRequisition req;
2351 gtk_widget_size_request( child->m_widget, &req );
2352
2353 GtkAllocation alloc;
2354 alloc.x = child->m_x;
2355 alloc.y = child->m_y;
2356 alloc.width = child->m_width;
2357 alloc.height = child->m_height;
2358 gtk_widget_size_allocate( child->m_widget, &alloc );
2359
2360 node = node->GetNext();
2361 }
2362 }
2363
2364
2365
2366 IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
2367
2368 wxDataViewCtrl::~wxDataViewCtrl()
2369 {
2370 if (m_notifier)
2371 GetModel()->RemoveNotifier( m_notifier );
2372
2373 // remove the model from the GtkTreeView before it gets destroyed by the
2374 // wxDataViewCtrlBase's dtor
2375 gtk_tree_view_set_model( GTK_TREE_VIEW(m_treeview), NULL );
2376 }
2377
2378 void wxDataViewCtrl::Init()
2379 {
2380 m_notifier = NULL;
2381 }
2382
2383 bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
2384 const wxPoint& pos, const wxSize& size,
2385 long style, const wxValidator& validator )
2386 {
2387 Init();
2388
2389 if (!PreCreation( parent, pos, size ) ||
2390 !CreateBase( parent, id, pos, size, style, validator ))
2391 {
2392 wxFAIL_MSG( wxT("wxDataViewCtrl creation failed") );
2393 return false;
2394 }
2395
2396 m_insertCallback = wxInsertChildInDataViewCtrl;
2397
2398 m_widget = gtk_scrolled_window_new (NULL, NULL);
2399
2400 GtkScrolledWindowSetBorder(m_widget, style);
2401
2402 m_treeview = gtk_tree_view_new();
2403 gtk_container_add (GTK_CONTAINER (m_widget), m_treeview);
2404
2405 g_signal_connect (m_treeview, "size_allocate",
2406 G_CALLBACK (gtk_dataviewctrl_size_callback), this);
2407
2408 #ifdef __WXGTK26__
2409 if (!gtk_check_version(2,6,0))
2410 gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW(m_treeview), TRUE );
2411 #endif
2412
2413 if (style & wxDV_MULTIPLE)
2414 {
2415 GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
2416 gtk_tree_selection_set_mode( selection, GTK_SELECTION_MULTIPLE );
2417 }
2418
2419 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW(m_treeview), (style & wxDV_NO_HEADER) == 0 );
2420
2421 #ifdef __WXGTK210__
2422 if (!gtk_check_version(2,10,0))
2423 {
2424 GtkTreeViewGridLines grid = GTK_TREE_VIEW_GRID_LINES_NONE;
2425
2426 if ((style & wxDV_HORIZ_RULES) != 0 &&
2427 (style & wxDV_VERT_RULES) != 0)
2428 grid = GTK_TREE_VIEW_GRID_LINES_BOTH;
2429 else if (style & wxDV_VERT_RULES)
2430 grid = GTK_TREE_VIEW_GRID_LINES_VERTICAL;
2431 else if (style & wxDV_HORIZ_RULES)
2432 grid = GTK_TREE_VIEW_GRID_LINES_HORIZONTAL;
2433
2434 gtk_tree_view_set_grid_lines( GTK_TREE_VIEW(m_treeview), grid );
2435 }
2436 else
2437 #endif
2438 {
2439 gtk_tree_view_set_rules_hint( GTK_TREE_VIEW(m_treeview), (style & wxDV_HORIZ_RULES) != 0 );
2440 }
2441
2442 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (m_widget),
2443 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
2444 gtk_widget_show (m_treeview);
2445
2446 m_parent->DoAddChild( this );
2447
2448 PostCreation(size);
2449
2450 GtkEnableSelectionEvents();
2451
2452 g_signal_connect_after (m_treeview, "row_activated",
2453 G_CALLBACK (wxdataview_row_activated_callback), this);
2454
2455 return true;
2456 }
2457
2458 void wxDataViewCtrl::OnInternalIdle()
2459 {
2460 wxWindow::OnInternalIdle();
2461
2462 unsigned int cols = GetColumnCount();
2463 unsigned int i;
2464 for (i = 0; i < cols; i++)
2465 {
2466 wxDataViewColumn *col = GetColumn( i );
2467 col->OnInternalIdle();
2468 }
2469 }
2470
2471 bool wxDataViewCtrl::AssociateModel( wxDataViewModel *model )
2472 {
2473 if (!wxDataViewCtrlBase::AssociateModel( model ))
2474 return false;
2475
2476 GtkWxTreeModel *gtkwx_tree_model = wxgtk_tree_model_new();
2477 wxGtkTreeModel *wxgtk_tree_model = new wxGtkTreeModel( model, gtkwx_tree_model );
2478
2479 gtkwx_tree_model->model = wxgtk_tree_model;
2480
2481 m_notifier = new wxGtkDataViewModelNotifier( gtkwx_tree_model, model, this );
2482
2483 model->AddNotifier( m_notifier );
2484
2485 gtk_tree_view_set_model( GTK_TREE_VIEW(m_treeview), GTK_TREE_MODEL(gtkwx_tree_model) );
2486 g_object_unref( gtkwx_tree_model );
2487
2488 return true;
2489 }
2490
2491 bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
2492 {
2493 if (!wxDataViewCtrlBase::AppendColumn(col))
2494 return false;
2495
2496 GtkTreeViewColumn *column = (GtkTreeViewColumn *)col->GetGtkHandle();
2497
2498 gtk_tree_view_append_column( GTK_TREE_VIEW(m_treeview), column );
2499
2500 return true;
2501 }
2502
2503 wxDataViewItem wxDataViewCtrl::GetSelection()
2504 {
2505 GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
2506
2507 if (m_windowStyle & wxDV_MULTIPLE)
2508 {
2509 }
2510 else
2511 {
2512 GtkTreeIter iter;
2513 if (gtk_tree_selection_get_selected( selection, NULL, &iter ))
2514 {
2515 wxDataViewItem item( (void*) iter.user_data );
2516 return item;
2517 }
2518 }
2519
2520 return wxDataViewItem(0);
2521 }
2522
2523 void wxDataViewCtrl::DoSetExpanderColumn()
2524 {
2525 }
2526
2527 void wxDataViewCtrl::DoSetIndent()
2528 {
2529 }
2530
2531 void wxDataViewCtrl::GtkDisableSelectionEvents()
2532 {
2533 GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
2534 g_signal_connect_after (selection, "changed",
2535 G_CALLBACK (wxdataview_selection_changed_callback), this);
2536 }
2537
2538 void wxDataViewCtrl::GtkEnableSelectionEvents()
2539 {
2540 GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
2541 g_signal_handlers_disconnect_by_func( selection,
2542 (gpointer) (wxdataview_selection_changed_callback), this);
2543 }
2544
2545 // static
2546 wxVisualAttributes
2547 wxDataViewCtrl::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
2548 {
2549 return GetDefaultAttributesFromGTKWidget(gtk_tree_view_new);
2550 }
2551
2552
2553 #endif
2554 // !wxUSE_GENERICDATAVIEWCTRL
2555
2556 #endif
2557 // wxUSE_DATAVIEWCTRL