+
+ return cell_wx_type;
+}
+
+static void
+gtk_wx_cell_renderer_init (GtkWxCellRenderer *cell)
+{
+ cell->cell = NULL;
+ cell->last_click = 0;
+}
+
+static void
+gtk_wx_cell_renderer_class_init (GtkWxCellRendererClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
+
+ cell_parent_class = (GObjectClass*) g_type_class_peek_parent (klass);
+
+ object_class->finalize = gtk_wx_cell_renderer_finalize;
+
+ cell_class->get_size = gtk_wx_cell_renderer_get_size;
+ cell_class->render = gtk_wx_cell_renderer_render;
+ cell_class->activate = gtk_wx_cell_renderer_activate;
+}
+
+static void
+gtk_wx_cell_renderer_finalize (GObject *object)
+{
+ /* must chain up */
+ (* G_OBJECT_CLASS (cell_parent_class)->finalize) (object);
+}
+
+GtkCellRenderer*
+gtk_wx_cell_renderer_new (void)
+{
+ return (GtkCellRenderer*) g_object_new (GTK_TYPE_WX_CELL_RENDERER, NULL);
+}
+
+static void
+gtk_wx_cell_renderer_get_size (GtkCellRenderer *renderer,
+ GtkWidget *widget,
+ GdkRectangle *cell_area,
+ gint *x_offset,
+ gint *y_offset,
+ gint *width,
+ gint *height)
+{
+ GtkWxCellRenderer *wxrenderer = (GtkWxCellRenderer *) renderer;
+ wxDataViewCustomCell *cell = wxrenderer->cell;
+
+ wxSize size = cell->GetSize();
+
+ gint calc_width = (gint) renderer->xpad * 2 + size.x;
+ gint calc_height = (gint) renderer->ypad * 2 + size.y;
+
+ if (x_offset)
+ *x_offset = 0;
+ if (y_offset)
+ *y_offset = 0;
+
+ if (cell_area && size.x > 0 && size.y > 0)
+ {
+ if (x_offset)
+ {
+ *x_offset = (gint)((renderer->xalign *
+ (cell_area->width - calc_width - 2 * renderer->xpad)));
+ *x_offset = MAX (*x_offset, 0) + renderer->xpad;
+ }
+ if (y_offset)
+ {
+ *y_offset = (gint)((renderer->yalign *
+ (cell_area->height - calc_height - 2 * renderer->ypad)));
+ *y_offset = MAX (*y_offset, 0) + renderer->ypad;
+ }
+ }
+
+ if (width)
+ *width = calc_width;
+
+ if (height)
+ *height = calc_height;
+}
+
+static void
+gtk_wx_cell_renderer_render (GtkCellRenderer *renderer,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ GtkCellRendererState flags)
+
+{
+ GtkWxCellRenderer *wxrenderer = (GtkWxCellRenderer *) renderer;
+ wxDataViewCustomCell *cell = wxrenderer->cell;
+
+ GdkRectangle rect;
+ gtk_wx_cell_renderer_get_size (renderer, widget, cell_area,
+ &rect.x,
+ &rect.y,
+ &rect.width,
+ &rect.height);
+
+ rect.x += cell_area->x;
+ rect.y += cell_area->y;
+ rect.width -= renderer->xpad * 2;
+ rect.height -= renderer->ypad * 2;
+
+ GdkRectangle dummy;
+ if (gdk_rectangle_intersect (expose_area, &rect, &dummy))
+ {
+ wxRect renderrect( rect.x, rect.y, rect.width, rect.height );
+ wxWindowDC* dc = (wxWindowDC*) cell->GetDC();
+ if (dc->m_window == NULL)
+ {
+ dc->m_window = window;
+ dc->SetUpDC();
+ }
+
+ int state = 0;
+ if (flags & GTK_CELL_RENDERER_SELECTED)
+ state |= wxDATAVIEW_CELL_SELECTED;
+ if (flags & GTK_CELL_RENDERER_PRELIT)
+ state |= wxDATAVIEW_CELL_PRELIT;
+ if (flags & GTK_CELL_RENDERER_INSENSITIVE)
+ state |= wxDATAVIEW_CELL_INSENSITIVE;
+ if (flags & GTK_CELL_RENDERER_INSENSITIVE)
+ state |= wxDATAVIEW_CELL_INSENSITIVE;
+ if (flags & GTK_CELL_RENDERER_FOCUSED)
+ state |= wxDATAVIEW_CELL_FOCUSED;
+ cell->Render( renderrect, dc, state );
+ }
+}
+
+static gboolean
+gtk_wx_cell_renderer_activate(
+ GtkCellRenderer *renderer,
+ GdkEvent *event,
+ GtkWidget *widget,
+ const gchar *path,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GtkCellRendererState flags )
+{
+ GtkWxCellRenderer *wxrenderer = (GtkWxCellRenderer *) renderer;
+ wxDataViewCustomCell *cell = wxrenderer->cell;
+
+ GdkRectangle rect;
+ gtk_wx_cell_renderer_get_size (renderer, widget, cell_area,
+ &rect.x,
+ &rect.y,
+ &rect.width,
+ &rect.height);
+
+ rect.x += cell_area->x;
+ rect.y += cell_area->y;
+ rect.width -= renderer->xpad * 2;
+ rect.height -= renderer->ypad * 2;
+
+ wxRect renderrect( rect.x, rect.y, rect.width, rect.height );
+
+ wxDataViewListModel *model = cell->GetOwner()->GetOwner()->GetModel();
+
+ GtkTreePath *treepath = gtk_tree_path_new_from_string( path );
+ size_t model_row = (size_t)gtk_tree_path_get_indices (treepath)[0];
+ gtk_tree_path_free( treepath );
+
+ size_t model_col = cell->GetOwner()->GetModelColumn();
+
+ if (event->type == GDK_BUTTON_PRESS)
+ {
+ GdkEventButton *button_event = (GdkEventButton*) event;
+ wxPoint pt( ((int) button_event->x) - renderrect.x,
+ ((int) button_event->y) - renderrect.y );
+
+ bool ret = false;
+ if (button_event->button == 1)
+ {
+ if (cell->LeftClick( pt, renderrect, model, model_col, model_row ))
+ ret = true;
+ // TODO: query system double-click time
+ if (button_event->time - wxrenderer->last_click < 400)
+ if (cell->Activate( renderrect, model, model_col, model_row ))
+ ret = true;
+ }
+ if (button_event->button == 3)
+ {
+ if (cell->RightClick( pt, renderrect, model, model_col, model_row ))
+ ret = true;
+ }
+
+ wxrenderer->last_click = button_event->time;
+
+ return ret;
+ }
+
+ return false;
+}
+
+// ---------------------------------------------------------
+// wxGtkDataViewListModelNotifier
+// ---------------------------------------------------------
+
+class wxGtkDataViewListModelNotifier: public wxDataViewListModelNotifier
+{
+public:
+ wxGtkDataViewListModelNotifier( GtkWxListStore* gtk_store, wxDataViewListModel *wx_model );
+
+ virtual bool RowAppended();
+ virtual bool RowPrepended();
+ virtual bool RowInserted( size_t before );
+ virtual bool RowDeleted( size_t row );
+ virtual bool RowChanged( size_t row );
+ virtual bool ValueChanged( size_t col, size_t row );
+ virtual bool RowsReordered( size_t *new_order );
+ virtual bool Cleared();
+
+ GtkWxListStore *m_gtk_store;
+ wxDataViewListModel *m_wx_model;
+};
+
+// ---------------------------------------------------------
+// wxGtkDataViewListModelNotifier
+// ---------------------------------------------------------
+
+wxGtkDataViewListModelNotifier::wxGtkDataViewListModelNotifier(
+ GtkWxListStore* gtk_store, wxDataViewListModel *wx_model )
+{
+ m_gtk_store = gtk_store;
+ m_wx_model = wx_model;
+}
+
+bool wxGtkDataViewListModelNotifier::RowAppended()
+{
+ size_t pos = m_wx_model->GetNumberOfRows()-1;
+
+ GtkTreeIter iter;
+ iter.stamp = m_gtk_store->stamp;
+ iter.user_data = (gpointer) pos;
+
+ GtkTreePath *path = gtk_tree_path_new ();
+ gtk_tree_path_append_index (path, (gint) pos);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (m_gtk_store), path, &iter);
+ gtk_tree_path_free (path);
+
+ return true;
+}
+
+bool wxGtkDataViewListModelNotifier::RowPrepended()
+{
+ GtkTreeIter iter;
+ iter.stamp = m_gtk_store->stamp;
+ iter.user_data = (gpointer) 0;
+
+ GtkTreePath *path = gtk_tree_path_new ();
+ gtk_tree_path_append_index (path, (gint) 0);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (m_gtk_store), path, &iter);
+ gtk_tree_path_free (path);
+
+ return true;
+}
+
+bool wxGtkDataViewListModelNotifier::RowInserted( size_t before )
+{
+ return false;
+}
+
+bool wxGtkDataViewListModelNotifier::RowDeleted( size_t row )
+{
+ return false;
+}
+
+bool wxGtkDataViewListModelNotifier::RowChanged( size_t row )
+{
+ GtkTreeIter iter;
+ iter.stamp = m_gtk_store->stamp;
+ iter.user_data = (gpointer) row;
+ GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (m_gtk_store), &iter);
+ gtk_tree_model_row_changed (GTK_TREE_MODEL (m_gtk_store), path, &iter);
+ gtk_tree_path_free (path);
+
+ return true;
+}
+
+bool wxGtkDataViewListModelNotifier::ValueChanged( size_t model_col, size_t model_row )
+{
+ // This adds GTK+'s missing MVC logic for ValueChanged
+ wxNode *node = GetOwner()->m_viewingColumns.GetFirst();
+ while (node)
+ {
+ wxDataViewViewingColumn* viewing_column = (wxDataViewViewingColumn*) node->GetData();
+ if (viewing_column->m_modelColumn == model_col)
+ {
+ GtkTreeView *widget = GTK_TREE_VIEW(viewing_column->m_viewColumn->GetOwner()->m_treeview);
+ GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(viewing_column->m_viewColumn->GetGtkHandle());
+
+ // Get cell area
+ GtkTreePath *path = gtk_tree_path_new();
+ gtk_tree_path_append_index( path, model_row );
+ GdkRectangle cell_area;
+ gtk_tree_view_get_cell_area( widget, path, column, &cell_area );
+ gtk_tree_path_free( path );
+
+ int ydiff = column->button->allocation.height;
+ // Redraw
+ gtk_widget_queue_draw_area( GTK_WIDGET(widget),
+ cell_area.x, ydiff + cell_area.y, cell_area.width, cell_area.height );
+ }
+
+ node = node->GetNext();
+ }
+
+ return true;
+}
+
+bool wxGtkDataViewListModelNotifier::RowsReordered( size_t *new_order )
+{
+ // Assume sizeof(size_t)= == sizeof(gint)
+
+ GtkTreePath *path = gtk_tree_path_new ();
+ gtk_tree_model_rows_reordered (GTK_TREE_MODEL (m_gtk_store), path, NULL, (gint*)new_order);
+ gtk_tree_path_free (path);
+
+ // This adds GTK+'s missing MVC logic for RowsReordered
+ wxNode *node = GetOwner()->m_viewingColumns.GetFirst();
+ while (node)
+ {
+ wxDataViewViewingColumn* viewing_column = (wxDataViewViewingColumn*) node->GetData();
+ GtkTreeView *widget = GTK_TREE_VIEW(viewing_column->m_viewColumn->GetOwner()->m_treeview);
+ // Doesn't work yet...
+ gtk_widget_queue_draw( GTK_WIDGET(widget) );
+
+ node = node->GetNext();
+ }
+
+ return true;
+}
+
+bool wxGtkDataViewListModelNotifier::Cleared()
+{
+ return false;
+}
+
+// ---------------------------------------------------------
+// wxDataViewCell
+// ---------------------------------------------------------
+
+IMPLEMENT_ABSTRACT_CLASS(wxDataViewCell, wxDataViewCellBase)
+
+wxDataViewCell::wxDataViewCell( const wxString &varianttype, wxDataViewCellMode mode ) :
+ wxDataViewCellBase( varianttype, mode )
+{
+ m_renderer = NULL;
+}
+
+// ---------------------------------------------------------
+// wxDataViewTextCell
+// ---------------------------------------------------------
+
+extern "C" {
+static void wxGtkTextRendererEditedCallback( GtkCellRendererText *renderer,
+ gchar *arg1, gchar *arg2, gpointer user_data );
+}
+
+static void wxGtkTextRendererEditedCallback( GtkCellRendererText *renderer,
+ gchar *arg1, gchar *arg2, gpointer user_data )
+{
+ wxDataViewTextCell *cell = (wxDataViewTextCell*) user_data;
+
+ wxString tmp = wxGTK_CONV_BACK( arg2 );
+ wxVariant value = tmp;
+ if (!cell->Validate( value ))
+ return;
+
+ wxDataViewListModel *model = cell->GetOwner()->GetOwner()->GetModel();
+
+ GtkTreePath *path = gtk_tree_path_new_from_string( arg1 );
+ size_t model_row = (size_t)gtk_tree_path_get_indices (path)[0];
+ gtk_tree_path_free( path );
+
+ size_t model_col = cell->GetOwner()->GetModelColumn();
+
+ model->SetValue( value, model_col, model_row );
+ model->ValueChanged( model_col, model_row );
+}
+
+IMPLEMENT_ABSTRACT_CLASS(wxDataViewTextCell, wxDataViewCell)
+
+wxDataViewTextCell::wxDataViewTextCell( const wxString &varianttype, wxDataViewCellMode mode ) :
+ wxDataViewCell( varianttype, mode )
+{
+ m_renderer = (void*) gtk_cell_renderer_text_new();
+
+ if (m_mode & wxDATAVIEW_CELL_EDITABLE)
+ {
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, G_TYPE_BOOLEAN );
+ g_value_set_boolean( &gvalue, true );
+ g_object_set_property( G_OBJECT(m_renderer), "editable", &gvalue );
+ g_value_unset( &gvalue );
+
+ g_signal_connect_after( m_renderer, "edited", G_CALLBACK(wxGtkTextRendererEditedCallback), this );
+ }
+}
+
+bool wxDataViewTextCell::SetValue( const wxVariant &value )
+{
+ wxString tmp = value;
+
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, G_TYPE_STRING );
+ g_value_set_string( &gvalue, wxGTK_CONV( tmp ) );
+ g_object_set_property( G_OBJECT(m_renderer), "text", &gvalue );
+ g_value_unset( &gvalue );
+
+ return true;
+}
+
+bool wxDataViewTextCell::GetValue( wxVariant &value )
+{
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, G_TYPE_STRING );
+ g_object_get_property( G_OBJECT(m_renderer), "text", &gvalue );
+ wxString tmp = wxGTK_CONV_BACK( g_value_get_string( &gvalue ) );
+ g_value_unset( &gvalue );
+
+ value = tmp;
+
+ return true;
+}
+
+// ---------------------------------------------------------
+// wxDataViewToggleCell
+// ---------------------------------------------------------
+
+extern "C" {
+static void wxGtkToggleRendererToggledCallback( GtkCellRendererToggle *renderer,
+ gchar *path, gpointer user_data );
+}
+
+static void wxGtkToggleRendererToggledCallback( GtkCellRendererToggle *renderer,
+ gchar *path, gpointer user_data )
+{
+ wxDataViewToggleCell *cell = (wxDataViewToggleCell*) user_data;
+
+ // get old value
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, G_TYPE_BOOLEAN );
+ g_object_get_property( G_OBJECT(renderer), "active", &gvalue );
+ bool tmp = g_value_get_boolean( &gvalue );
+ g_value_unset( &gvalue );
+ // invert it
+ tmp = !tmp;
+
+ wxVariant value = tmp;
+ if (!cell->Validate( value ))
+ return;
+
+ wxDataViewListModel *model = cell->GetOwner()->GetOwner()->GetModel();
+
+ GtkTreePath *gtk_path = gtk_tree_path_new_from_string( path );
+ size_t model_row = (size_t)gtk_tree_path_get_indices (gtk_path)[0];
+ gtk_tree_path_free( gtk_path );
+
+ size_t model_col = cell->GetOwner()->GetModelColumn();
+
+ model->SetValue( value, model_col, model_row );
+ model->ValueChanged( model_col, model_row );
+}
+
+IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleCell, wxDataViewCell)
+
+wxDataViewToggleCell::wxDataViewToggleCell( const wxString &varianttype,
+ wxDataViewCellMode mode ) :
+ wxDataViewCell( varianttype, mode )
+{
+ m_renderer = (void*) gtk_cell_renderer_toggle_new();
+
+ if (m_mode & wxDATAVIEW_CELL_EDITABLE)
+ {
+ g_signal_connect_after( m_renderer, "toggled", G_CALLBACK(wxGtkToggleRendererToggledCallback), this );
+ }
+ else
+ {
+
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, G_TYPE_BOOLEAN );
+ g_value_set_boolean( &gvalue, false );
+ g_object_set_property( G_OBJECT(m_renderer), "activatable", &gvalue );
+ g_value_unset( &gvalue );
+
+ GValue gvalue2 = { 0, };
+ g_value_init( &gvalue2, gtk_cell_renderer_mode_get_type() );
+ g_value_set_enum( &gvalue2, GTK_CELL_RENDERER_MODE_INERT );
+ g_object_set_property( G_OBJECT(m_renderer), "mode", &gvalue2 );
+ g_value_unset( &gvalue2 );
+
+ }
+}
+
+bool wxDataViewToggleCell::SetValue( const wxVariant &value )
+{
+ bool tmp = value;
+
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, G_TYPE_BOOLEAN );
+ g_value_set_boolean( &gvalue, tmp );
+ g_object_set_property( G_OBJECT(m_renderer), "active", &gvalue );
+ g_value_unset( &gvalue );
+
+ return true;
+}
+
+bool wxDataViewToggleCell::GetValue( wxVariant &value )
+{
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, G_TYPE_BOOLEAN );
+ g_object_get_property( G_OBJECT(m_renderer), "active", &gvalue );
+ bool tmp = g_value_get_boolean( &gvalue );
+ g_value_unset( &gvalue );
+
+ value = tmp;
+
+ return true;
+}
+
+// ---------------------------------------------------------
+// wxDataViewCustomCell
+// ---------------------------------------------------------
+
+class wxDataViewCtrlDC: public wxWindowDC
+{
+public:
+ wxDataViewCtrlDC( wxDataViewCtrl *window )
+ {
+ GtkWidget *widget = window->m_treeview;
+ // Set later
+ m_window = NULL;
+
+ m_context = window->GtkGetPangoDefaultContext();
+ m_layout = pango_layout_new( m_context );
+ m_fontdesc = pango_font_description_copy( widget->style->font_desc );
+
+ m_cmap = gtk_widget_get_colormap( widget ? widget : window->m_widget );
+
+ // Set m_window later
+ // SetUpDC();
+ // m_owner = window;
+ }
+};
+
+// ---------------------------------------------------------
+// wxDataViewCustomCell
+// ---------------------------------------------------------
+
+IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomCell, wxDataViewCell)
+
+wxDataViewCustomCell::wxDataViewCustomCell( const wxString &varianttype,
+ wxDataViewCellMode mode, bool no_init ) :
+ wxDataViewCell( varianttype, mode )
+{
+ m_dc = NULL;
+
+ if (no_init)
+ m_renderer = NULL;
+ else
+ Init();
+}
+
+bool wxDataViewCustomCell::Init()
+{
+ GtkWxCellRenderer *renderer = (GtkWxCellRenderer *) gtk_wx_cell_renderer_new();
+ renderer->cell = this;
+
+ m_renderer = (void*) renderer;
+
+ if (m_mode & wxDATAVIEW_CELL_ACTIVATABLE)
+ {
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, gtk_cell_renderer_mode_get_type() );
+ g_value_set_enum( &gvalue, GTK_CELL_RENDERER_MODE_ACTIVATABLE );
+ g_object_set_property( G_OBJECT(m_renderer), "mode", &gvalue );
+ g_value_unset( &gvalue );
+ }
+
+ return true;
+}
+
+wxDataViewCustomCell::~wxDataViewCustomCell()
+{
+ if (m_dc)
+ delete m_dc;
+}
+
+wxDC *wxDataViewCustomCell::GetDC()
+{
+ if (m_dc == NULL)
+ {
+ if (GetOwner() == NULL)
+ return NULL;
+ if (GetOwner()->GetOwner() == NULL)
+ return NULL;
+ m_dc = new wxDataViewCtrlDC( GetOwner()->GetOwner() );
+ }
+
+ return m_dc;
+}
+
+// ---------------------------------------------------------
+// wxDataViewProgressCell
+// ---------------------------------------------------------
+
+IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressCell, wxDataViewCustomCell)
+
+wxDataViewProgressCell::wxDataViewProgressCell( const wxString &label,
+ const wxString &varianttype, wxDataViewCellMode mode ) :
+ wxDataViewCustomCell( varianttype, mode, true )
+{
+ m_label = label;
+ m_value = 0;
+
+#ifdef __WXGTK26__
+ if (!gtk_check_version(2,6,0))
+ {
+ m_renderer = (void*) gtk_cell_renderer_progress_new();
+
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, G_TYPE_STRING );
+ g_value_set_boolean( &gvalue, wxGTK_CONV(m_label) );
+ g_object_set_property( G_OBJECT(m_renderer), "text", &gvalue );
+ g_value_unset( &gvalue );
+ }
+ else