+ GtkWxTreeModel *m_wxgtk_model;
+ wxDataViewModel *m_wx_model;
+ wxDataViewCtrl *m_owner;
+};
+
+// ---------------------------------------------------------
+// wxGtkDataViewListModelNotifier
+// ---------------------------------------------------------
+
+wxGtkDataViewModelNotifier::wxGtkDataViewModelNotifier(
+ GtkWxTreeModel* wxgtk_model, wxDataViewModel *wx_model,
+ wxDataViewCtrl *ctrl )
+{
+ m_wxgtk_model = wxgtk_model;
+ m_wx_model = wx_model;
+ m_owner = ctrl;
+}
+
+wxGtkDataViewModelNotifier::~wxGtkDataViewModelNotifier()
+{
+ m_wx_model = NULL;
+ m_wxgtk_model = NULL;
+}
+
+bool wxGtkDataViewModelNotifier::ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item )
+{
+ m_owner->GtkGetInternal()->ItemAdded( parent, item );
+
+ GtkTreeIter iter;
+ iter.stamp = m_wxgtk_model->stamp;
+ iter.user_data = (gpointer) item.GetID();
+
+ GtkTreePath *path = wxgtk_tree_model_get_path(
+ GTK_TREE_MODEL(m_wxgtk_model), &iter );
+ gtk_tree_model_row_inserted(
+ GTK_TREE_MODEL(m_wxgtk_model), path, &iter);
+ gtk_tree_path_free (path);
+
+ return true;
+}
+
+bool wxGtkDataViewModelNotifier::ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item )
+{
+ GtkTreeIter iter;
+ iter.stamp = m_wxgtk_model->stamp;
+ iter.user_data = (gpointer) item.GetID();
+
+ GtkTreePath *path = wxgtk_tree_model_get_path_safe(
+ GTK_TREE_MODEL(m_wxgtk_model), &iter );
+ gtk_tree_model_row_deleted(
+ GTK_TREE_MODEL(m_wxgtk_model), path );
+ gtk_tree_path_free (path);
+
+ m_owner->GtkGetInternal()->ItemDeleted( parent, item );
+
+ return true;
+}
+
+void wxGtkDataViewModelNotifier::Resort()
+{
+ m_owner->GtkGetInternal()->Resort();
+}
+
+bool wxGtkDataViewModelNotifier::ItemChanged( const wxDataViewItem &item )
+{
+ GtkTreeIter iter;
+ iter.stamp = m_wxgtk_model->stamp;
+ iter.user_data = (gpointer) item.GetID();
+
+ GtkTreePath *path = wxgtk_tree_model_get_path(
+ GTK_TREE_MODEL(m_wxgtk_model), &iter );
+ gtk_tree_model_row_changed(
+ GTK_TREE_MODEL(m_wxgtk_model), path, &iter );
+ gtk_tree_path_free (path);
+
+ m_owner->GtkGetInternal()->ItemChanged( item );
+
+ return true;
+}
+
+bool wxGtkDataViewModelNotifier::ValueChanged( const wxDataViewItem &item, unsigned int model_col )
+{
+ // This adds GTK+'s missing MVC logic for ValueChanged
+ unsigned int index;
+ for (index = 0; index < m_owner->GetColumnCount(); index++)
+ {
+ wxDataViewColumn *column = m_owner->GetColumn( index );
+ if (column->GetModelColumn() == model_col)
+ {
+ GtkTreeView *widget = GTK_TREE_VIEW(m_owner->m_treeview);
+ GtkTreeViewColumn *gcolumn = GTK_TREE_VIEW_COLUMN(column->GetGtkHandle());
+
+ // Get cell area
+ GtkTreeIter iter;
+ iter.stamp = m_wxgtk_model->stamp;
+ iter.user_data = (gpointer) item.GetID();
+ GtkTreePath *path = wxgtk_tree_model_get_path(
+ GTK_TREE_MODEL(m_wxgtk_model), &iter );
+ GdkRectangle cell_area;
+ gtk_tree_view_get_cell_area( widget, path, gcolumn, &cell_area );
+ gtk_tree_path_free( path );
+
+ GtkAdjustment* hadjust = gtk_tree_view_get_hadjustment( widget );
+ double d = gtk_adjustment_get_value( hadjust );
+ int xdiff = (int) d;
+
+ int ydiff = gcolumn->button->allocation.height;
+ // Redraw
+ gtk_widget_queue_draw_area( GTK_WIDGET(widget),
+ cell_area.x - xdiff, ydiff + cell_area.y, cell_area.width, cell_area.height );
+
+ m_owner->GtkGetInternal()->ValueChanged( item, model_col );
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool wxGtkDataViewModelNotifier::Cleared()
+{
+ // TODO: delete everything
+
+ m_owner->GtkGetInternal()->Cleared();
+
+ return false;
+}
+
+// ---------------------------------------------------------
+// wxDataViewRenderer
+// ---------------------------------------------------------
+
+IMPLEMENT_ABSTRACT_CLASS(wxDataViewRenderer, wxDataViewRendererBase)
+
+wxDataViewRenderer::wxDataViewRenderer( const wxString &varianttype, wxDataViewCellMode mode,
+ int align ) :
+ wxDataViewRendererBase( varianttype, mode, align )
+{
+ m_renderer = NULL;
+
+ // NOTE: SetMode() and SetAlignment() needs to be called in the renderer's ctor,
+ // after the m_renderer pointer has been initialized
+}
+
+void wxDataViewRenderer::SetMode( wxDataViewCellMode mode )
+{
+ GtkCellRendererMode gtkMode;
+ switch (mode)
+ {
+ case wxDATAVIEW_CELL_INERT:
+ gtkMode = GTK_CELL_RENDERER_MODE_INERT;
+ break;
+ case wxDATAVIEW_CELL_ACTIVATABLE:
+ gtkMode = GTK_CELL_RENDERER_MODE_ACTIVATABLE;
+ break;
+ case wxDATAVIEW_CELL_EDITABLE:
+ gtkMode = GTK_CELL_RENDERER_MODE_EDITABLE;
+ break;
+ }
+
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, gtk_cell_renderer_mode_get_type() );
+ g_value_set_enum( &gvalue, gtkMode );
+ g_object_set_property( G_OBJECT(m_renderer), "mode", &gvalue );
+ g_value_unset( &gvalue );
+}
+
+wxDataViewCellMode wxDataViewRenderer::GetMode() const
+{
+ wxDataViewCellMode ret;
+
+ GValue gvalue;
+ g_object_get( G_OBJECT(m_renderer), "mode", &gvalue, NULL);
+
+ switch (g_value_get_enum(&gvalue))
+ {
+ case GTK_CELL_RENDERER_MODE_INERT:
+ ret = wxDATAVIEW_CELL_INERT;
+ break;
+ case GTK_CELL_RENDERER_MODE_ACTIVATABLE:
+ ret = wxDATAVIEW_CELL_ACTIVATABLE;
+ break;
+ case GTK_CELL_RENDERER_MODE_EDITABLE:
+ ret = wxDATAVIEW_CELL_EDITABLE;
+ break;
+ }
+
+ g_value_unset( &gvalue );
+
+ return ret;
+}
+
+void wxDataViewRenderer::SetAlignment( int align )
+{
+ // horizontal alignment:
+
+ gfloat xalign = 0.0;
+ if (align & wxALIGN_RIGHT)
+ xalign = 1.0;
+ else if (align & wxALIGN_CENTER_HORIZONTAL)
+ xalign = 0.5;
+
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, G_TYPE_FLOAT );
+ g_value_set_float( &gvalue, xalign );
+ g_object_set_property( G_OBJECT(m_renderer), "xalign", &gvalue );
+ g_value_unset( &gvalue );
+
+ // vertical alignment:
+
+ gfloat yalign = 0.0;
+ if (align & wxALIGN_BOTTOM)
+ yalign = 1.0;
+ else if (align & wxALIGN_CENTER_VERTICAL)
+ yalign = 0.5;
+
+ GValue gvalue2 = { 0, };
+ g_value_init( &gvalue2, G_TYPE_FLOAT );
+ g_value_set_float( &gvalue2, yalign );
+ g_object_set_property( G_OBJECT(m_renderer), "yalign", &gvalue2 );
+ g_value_unset( &gvalue2 );
+}
+
+int wxDataViewRenderer::GetAlignment() const
+{
+ int ret = 0;
+ GValue gvalue;
+
+ // horizontal alignment:
+
+ g_object_get( G_OBJECT(m_renderer), "xalign", &gvalue, NULL );
+ float xalign = g_value_get_float( &gvalue );
+ if (xalign < 0.5)
+ ret |= wxALIGN_LEFT;
+ else if (xalign == 0.5)
+ ret |= wxALIGN_CENTER_HORIZONTAL;
+ else
+ ret |= wxALIGN_RIGHT;
+ g_value_unset( &gvalue );
+
+
+ // vertical alignment:
+
+ g_object_get( G_OBJECT(m_renderer), "yalign", &gvalue, NULL );
+ float yalign = g_value_get_float( &gvalue );
+ if (yalign < 0.5)
+ ret |= wxALIGN_TOP;
+ else if (yalign == 0.5)
+ ret |= wxALIGN_CENTER_VERTICAL;
+ else
+ ret |= wxALIGN_BOTTOM;
+ g_value_unset( &gvalue );
+
+ return ret;
+}
+
+
+
+// ---------------------------------------------------------
+// wxDataViewTextRenderer
+// ---------------------------------------------------------
+
+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 )
+{
+ wxDataViewTextRenderer *cell = (wxDataViewTextRenderer*) user_data;
+
+ wxString tmp = wxGTK_CONV_BACK_FONT(arg2, cell->GetOwner()->GetOwner()->GetFont());
+ wxVariant value = tmp;
+ if (!cell->Validate( value ))
+ return;
+
+ wxDataViewModel *model = cell->GetOwner()->GetOwner()->GetModel();
+
+ GtkTreePath *path = gtk_tree_path_new_from_string( arg1 );
+ GtkTreeIter iter;
+ cell->GetOwner()->GetOwner()->GtkGetInternal()->get_iter( &iter, path );
+ wxDataViewItem item( (void*) iter.user_data );;
+ gtk_tree_path_free( path );
+
+ unsigned int model_col = cell->GetOwner()->GetModelColumn();
+
+ model->SetValue( value, item, model_col );
+ model->ValueChanged( item, model_col );
+}
+
+IMPLEMENT_CLASS(wxDataViewTextRenderer, wxDataViewRenderer)
+
+wxDataViewTextRenderer::wxDataViewTextRenderer( const wxString &varianttype, wxDataViewCellMode mode,
+ int align ) :
+ wxDataViewRenderer( varianttype, mode, align )
+{
+ m_renderer = (GtkCellRenderer*) gtk_cell_renderer_text_new();
+
+ if (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 );
+ }
+
+ SetMode(mode);
+ SetAlignment(align);
+}
+
+bool wxDataViewTextRenderer::SetValue( const wxVariant &value )
+{
+ wxString tmp = value;
+
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, G_TYPE_STRING );
+ g_value_set_string( &gvalue, wxGTK_CONV_FONT( tmp, GetOwner()->GetOwner()->GetFont() ) );
+ g_object_set_property( G_OBJECT(m_renderer), "text", &gvalue );
+ g_value_unset( &gvalue );
+
+ return true;
+}
+
+bool wxDataViewTextRenderer::GetValue( wxVariant &value ) const
+{
+ 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_FONT( g_value_get_string( &gvalue ),
+ wx_const_cast(wxDataViewTextRenderer*, this)->GetOwner()->GetOwner()->GetFont() );
+ g_value_unset( &gvalue );
+
+ value = tmp;
+
+ return true;
+}
+
+void wxDataViewTextRenderer::SetAlignment( int align )
+{
+ wxDataViewRenderer::SetAlignment(align);
+
+ if (gtk_check_version(2,10,0))
+ return;
+
+ // horizontal alignment:
+ PangoAlignment pangoAlign = PANGO_ALIGN_LEFT;
+ if (align & wxALIGN_RIGHT)
+ pangoAlign = PANGO_ALIGN_RIGHT;
+ else if (align & wxALIGN_CENTER_HORIZONTAL)
+ pangoAlign = PANGO_ALIGN_CENTER;
+
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, gtk_cell_renderer_mode_get_type() );
+ g_value_set_enum( &gvalue, pangoAlign );
+ g_object_set_property( G_OBJECT(m_renderer), "alignment", &gvalue );
+ g_value_unset( &gvalue );
+}
+
+// ---------------------------------------------------------
+// wxDataViewBitmapRenderer
+// ---------------------------------------------------------
+
+IMPLEMENT_CLASS(wxDataViewBitmapRenderer, wxDataViewRenderer)
+
+wxDataViewBitmapRenderer::wxDataViewBitmapRenderer( const wxString &varianttype, wxDataViewCellMode mode,
+ int align ) :
+ wxDataViewRenderer( varianttype, mode, align )
+{
+ m_renderer = (GtkCellRenderer*) gtk_cell_renderer_pixbuf_new();
+
+ SetMode(mode);
+ SetAlignment(align);
+}
+
+bool wxDataViewBitmapRenderer::SetValue( const wxVariant &value )
+{
+ if (value.GetType() == wxT("wxBitmap"))
+ {
+ wxBitmap bitmap;
+ bitmap << value;
+
+ // This may create a Pixbuf representation in the
+ // wxBitmap object (and it will stay there)
+ GdkPixbuf *pixbuf = bitmap.GetPixbuf();
+
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, G_TYPE_OBJECT );
+ g_value_set_object( &gvalue, pixbuf );
+ g_object_set_property( G_OBJECT(m_renderer), "pixbuf", &gvalue );
+ g_value_unset( &gvalue );
+
+ return true;
+ }
+
+ if (value.GetType() == wxT("wxIcon"))
+ {
+ wxIcon bitmap;
+ bitmap << value;
+
+ // This may create a Pixbuf representation in the
+ // wxBitmap object (and it will stay there)
+ GdkPixbuf *pixbuf = bitmap.GetPixbuf();
+
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, G_TYPE_OBJECT );
+ g_value_set_object( &gvalue, pixbuf );
+ g_object_set_property( G_OBJECT(m_renderer), "pixbuf", &gvalue );
+ g_value_unset( &gvalue );
+
+ return true;
+ }
+
+ return false;
+}
+
+bool wxDataViewBitmapRenderer::GetValue( wxVariant &value ) const
+{
+ return false;
+}
+
+// ---------------------------------------------------------
+// wxDataViewToggleRenderer
+// ---------------------------------------------------------
+
+extern "C" {
+static void wxGtkToggleRendererToggledCallback( GtkCellRendererToggle *renderer,
+ gchar *path, gpointer user_data );
+}
+
+static void wxGtkToggleRendererToggledCallback( GtkCellRendererToggle *renderer,
+ gchar *path, gpointer user_data )
+{
+ wxDataViewToggleRenderer *cell = (wxDataViewToggleRenderer*) 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;
+
+ wxDataViewModel *model = cell->GetOwner()->GetOwner()->GetModel();
+
+ GtkTreePath *gtk_path = gtk_tree_path_new_from_string( path );
+ GtkTreeIter iter;
+ cell->GetOwner()->GetOwner()->GtkGetInternal()->get_iter( &iter, gtk_path );
+ wxDataViewItem item( (void*) iter.user_data );;
+ gtk_tree_path_free( gtk_path );
+
+ unsigned int model_col = cell->GetOwner()->GetModelColumn();
+
+ model->SetValue( value, item, model_col );
+ model->ValueChanged( item, model_col );
+}
+
+IMPLEMENT_CLASS(wxDataViewToggleRenderer, wxDataViewRenderer)
+
+wxDataViewToggleRenderer::wxDataViewToggleRenderer( const wxString &varianttype,
+ wxDataViewCellMode mode, int align ) :
+ wxDataViewRenderer( varianttype, mode, align )
+{
+ m_renderer = (GtkCellRenderer*) gtk_cell_renderer_toggle_new();
+
+ if (mode & wxDATAVIEW_CELL_ACTIVATABLE)
+ {
+ 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 );
+ }
+
+ SetMode(mode);
+ SetAlignment(align);
+}
+
+bool wxDataViewToggleRenderer::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 wxDataViewToggleRenderer::GetValue( wxVariant &value ) const
+{
+ 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;
+}
+
+// ---------------------------------------------------------
+// wxDataViewCustomRenderer
+// ---------------------------------------------------------
+
+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;
+ }
+};
+
+// ---------------------------------------------------------
+// wxDataViewCustomRenderer
+// ---------------------------------------------------------
+
+IMPLEMENT_CLASS(wxDataViewCustomRenderer, wxDataViewRenderer)
+
+wxDataViewCustomRenderer::wxDataViewCustomRenderer( const wxString &varianttype,
+ wxDataViewCellMode mode, int align,
+ bool no_init ) :
+ wxDataViewRenderer( varianttype, mode, align )
+{
+ m_dc = NULL;
+
+ if (no_init)
+ m_renderer = NULL;
+ else
+ Init(mode, align);
+}
+
+bool wxDataViewCustomRenderer::Init(wxDataViewCellMode mode, int align)
+{
+ GtkWxCellRenderer *renderer = (GtkWxCellRenderer *) gtk_wx_cell_renderer_new();
+ renderer->cell = this;
+
+ m_renderer = (GtkCellRenderer*) renderer;
+
+ SetMode(mode);
+ SetAlignment(align);
+
+ return true;
+}
+
+wxDataViewCustomRenderer::~wxDataViewCustomRenderer()
+{
+ if (m_dc)
+ delete m_dc;
+}
+
+wxDC *wxDataViewCustomRenderer::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;
+}
+
+// ---------------------------------------------------------
+// wxDataViewProgressRenderer
+// ---------------------------------------------------------
+
+IMPLEMENT_CLASS(wxDataViewProgressRenderer, wxDataViewCustomRenderer)
+
+wxDataViewProgressRenderer::wxDataViewProgressRenderer( const wxString &label,
+ const wxString &varianttype, wxDataViewCellMode mode, int align ) :
+ wxDataViewCustomRenderer( varianttype, mode, align, true )
+{
+ m_label = label;
+ m_value = 0;
+
+#ifdef __WXGTK26__
+ if (!gtk_check_version(2,6,0))
+ {
+ m_renderer = (GtkCellRenderer*) gtk_cell_renderer_progress_new();
+
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, G_TYPE_STRING );
+
+ // FIXME: font encoding support
+ g_value_set_string( &gvalue, wxGTK_CONV_SYS(m_label) );
+ g_object_set_property( G_OBJECT(m_renderer), "text", &gvalue );
+ g_value_unset( &gvalue );
+
+ SetMode(mode);
+ SetAlignment(align);
+ }
+ else
+#endif
+ {
+ // Use custom cell code
+ wxDataViewCustomRenderer::Init(mode, align);
+ }
+}
+
+wxDataViewProgressRenderer::~wxDataViewProgressRenderer()
+{
+}
+
+bool wxDataViewProgressRenderer::SetValue( const wxVariant &value )
+{
+#ifdef __WXGTK26__
+ if (!gtk_check_version(2,6,0))
+ {
+ gint tmp = (long) value;
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, G_TYPE_INT );
+ g_value_set_int( &gvalue, tmp );
+ g_object_set_property( G_OBJECT(m_renderer), "value", &gvalue );
+ g_value_unset( &gvalue );
+ }
+ else
+#endif
+ {
+ m_value = (long) value;
+
+ if (m_value < 0) m_value = 0;
+ if (m_value > 100) m_value = 100;
+ }
+
+ return true;
+}
+
+bool wxDataViewProgressRenderer::GetValue( wxVariant &value ) const
+{
+ return false;
+}
+
+bool wxDataViewProgressRenderer::Render( wxRect cell, wxDC *dc, int state )
+{
+ double pct = (double)m_value / 100.0;
+ wxRect bar = cell;
+ bar.width = (int)(cell.width * pct);
+ dc->SetPen( *wxTRANSPARENT_PEN );
+ dc->SetBrush( *wxBLUE_BRUSH );
+ dc->DrawRectangle( bar );
+
+ dc->SetBrush( *wxTRANSPARENT_BRUSH );
+ dc->SetPen( *wxBLACK_PEN );
+ dc->DrawRectangle( cell );
+
+ return true;
+}
+
+wxSize wxDataViewProgressRenderer::GetSize() const
+{
+ return wxSize(40,12);
+}
+
+// ---------------------------------------------------------
+// wxDataViewDateRenderer
+// ---------------------------------------------------------
+
+class wxDataViewDateRendererPopupTransient: public wxPopupTransientWindow
+{
+public:
+ wxDataViewDateRendererPopupTransient( wxWindow* parent, wxDateTime *value,
+ wxDataViewModel *model, const wxDataViewItem &item, unsigned int col ) :
+ wxPopupTransientWindow( parent, wxBORDER_SIMPLE )
+ {
+ m_model = model;
+ m_item = item;
+ m_col = col;
+ m_cal = new wxCalendarCtrl( this, -1, *value );
+ wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
+ sizer->Add( m_cal, 1, wxGROW );
+ SetSizer( sizer );
+ sizer->Fit( this );
+ }
+
+ virtual void OnDismiss()
+ {
+ }
+
+ void OnCalendar( wxCalendarEvent &event );
+
+ wxCalendarCtrl *m_cal;
+ wxDataViewModel *m_model;
+ wxDataViewItem m_item;
+ unsigned int m_col;
+
+private:
+ DECLARE_EVENT_TABLE()
+};
+
+BEGIN_EVENT_TABLE(wxDataViewDateRendererPopupTransient,wxPopupTransientWindow)
+ EVT_CALENDAR( -1, wxDataViewDateRendererPopupTransient::OnCalendar )
+END_EVENT_TABLE()
+
+void wxDataViewDateRendererPopupTransient::OnCalendar( wxCalendarEvent &event )
+{
+ wxDateTime date = event.GetDate();
+ wxVariant value = date;
+ m_model->SetValue( value, m_item, m_col );
+ m_model->ValueChanged( m_item, m_col );
+ DismissAndNotify();
+}
+
+IMPLEMENT_CLASS(wxDataViewDateRenderer, wxDataViewCustomRenderer)
+
+wxDataViewDateRenderer::wxDataViewDateRenderer( const wxString &varianttype,
+ wxDataViewCellMode mode, int align ) :
+ wxDataViewCustomRenderer( varianttype, mode, align )
+{
+ SetMode(mode);
+ SetAlignment(align);
+}
+
+bool wxDataViewDateRenderer::SetValue( const wxVariant &value )
+{
+ m_date = value.GetDateTime();
+
+ return true;
+}
+
+bool wxDataViewDateRenderer::GetValue( wxVariant &value ) const
+{
+ return false;
+}
+
+bool wxDataViewDateRenderer::Render( wxRect cell, wxDC *dc, int state )
+{
+ dc->SetFont( GetOwner()->GetOwner()->GetFont() );
+ wxString tmp = m_date.FormatDate();
+ dc->DrawText( tmp, cell.x, cell.y );
+
+ return true;
+}
+
+wxSize wxDataViewDateRenderer::GetSize() const
+{
+ wxString tmp = m_date.FormatDate();
+ wxCoord x,y,d;
+ GetView()->GetTextExtent( tmp, &x, &y, &d );
+ return wxSize(x,y+d);
+}
+
+bool wxDataViewDateRenderer::Activate( wxRect cell, wxDataViewModel *model,
+ const wxDataViewItem &item, unsigned int col )
+{
+ wxVariant variant;
+ model->GetValue( variant, item, col );
+ wxDateTime value = variant.GetDateTime();
+
+ wxDataViewDateRendererPopupTransient *popup = new wxDataViewDateRendererPopupTransient(
+ GetOwner()->GetOwner()->GetParent(), &value, model, item, col );
+ wxPoint pos = wxGetMousePosition();
+ popup->Move( pos );
+ popup->Layout();
+ popup->Popup( popup->m_cal );
+
+ return true;
+}
+
+// ---------------------------------------------------------
+// wxDataViewColumn
+// ---------------------------------------------------------
+
+
+static gboolean
+gtk_dataview_header_button_press_callback( GtkWidget *widget,
+ GdkEventButton *gdk_event,
+ wxDataViewColumn *column )
+{
+ if (gdk_event->type != GDK_BUTTON_PRESS)
+ return FALSE;
+
+ if (gdk_event->button == 1)
+ {
+ wxDataViewCtrl *dv = column->GetOwner();
+ wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_COLUMN_HEADER_CLICK, dv->GetId() );
+ event.SetDataViewColumn( column );
+ event.SetModel( dv->GetModel() );
+ if (dv->GetEventHandler()->ProcessEvent( event ))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+extern "C" {
+static void wxGtkTreeCellDataFunc( GtkTreeViewColumn *column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data );
+}
+
+
+static void wxGtkTreeCellDataFunc( GtkTreeViewColumn *column,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data )
+{
+ g_return_if_fail (GTK_IS_WX_TREE_MODEL (model));
+ GtkWxTreeModel *tree_model = (GtkWxTreeModel *) model;
+
+ wxDataViewRenderer *cell = (wxDataViewRenderer*) data;
+
+ wxDataViewItem item( (void*) iter->user_data );
+
+ wxVariant value;
+ tree_model->internal->GetDataViewModel()->GetValue( value, item, cell->GetOwner()->GetModelColumn() );
+
+ if (value.GetType() != cell->GetVariantType())
+ wxLogError( wxT("Wrong type, required: %s but: %s"),
+ value.GetType().c_str(),
+ cell->GetVariantType().c_str() );
+
+ cell->SetValue( value );
+
+#if 0
+ wxListItemAttr attr;
+ tree_model->model->GetAttr( attr, cell->GetOwner()->GetModelColumn(), model_row );
+
+ if (attr.HasBackgroundColour())
+ {
+ wxColour colour = attr.GetBackgroundColour();
+ const GdkColor * const gcol = colour.GetColor();
+
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, GDK_TYPE_COLOR );
+ g_value_set_boxed( &gvalue, gcol );
+ g_object_set_property( G_OBJECT(renderer), "cell-background_gdk", &gvalue );
+ g_value_unset( &gvalue );
+ }
+ else
+ {
+ GValue gvalue = { 0, };
+ g_value_init( &gvalue, G_TYPE_BOOLEAN );
+ g_value_set_boolean( &gvalue, FALSE );
+ g_object_set_property( G_OBJECT(renderer), "cell-background-set", &gvalue );
+ g_value_unset( &gvalue );
+ }
+#endif
+
+}
+
+IMPLEMENT_CLASS(wxDataViewColumn, wxDataViewColumnBase)
+
+wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewRenderer *cell,
+ unsigned int model_column, int width,
+ wxAlignment align, int flags ) :
+ wxDataViewColumnBase( title, cell, model_column, width, align, flags )
+{
+ Init( align, flags, width );
+
+ gtk_tree_view_column_set_clickable( GTK_TREE_VIEW_COLUMN(m_column), TRUE );
+ SetTitle( title );
+}
+
+wxDataViewColumn::wxDataViewColumn( const wxBitmap &bitmap, wxDataViewRenderer *cell,
+ unsigned int model_column, int width,
+ wxAlignment align, int flags ) :
+ wxDataViewColumnBase( bitmap, cell, model_column, width, align, flags )
+{
+ Init( align, flags, width );
+
+ SetBitmap( bitmap );
+}
+
+void wxDataViewColumn::Init(wxAlignment align, int flags, int width)
+{
+ m_isConnected = false;
+
+ GtkCellRenderer *renderer = (GtkCellRenderer *) GetRenderer()->GetGtkHandle();
+ GtkTreeViewColumn *column = gtk_tree_view_column_new();
+ m_column = (GtkWidget*) column;
+
+ SetFlags( flags );
+ SetAlignment( align );
+
+ // NOTE: we prefer not to call SetMinWidth(wxDVC_DEFAULT_MINWIDTH);
+ // as GTK+ is smart and unless explicitely told, will set the minimal
+ // width to the title's lenght, which is a better default
+
+ // the GTK_TREE_VIEW_COLUMN_FIXED is required by the "fixed height" mode
+ // that we use for the wxDataViewCtrl
+ gtk_tree_view_column_set_fixed_width( column, width < 0 ? wxDVC_DEFAULT_WIDTH : width );
+ gtk_tree_view_column_set_sizing( column, GTK_TREE_VIEW_COLUMN_FIXED );
+
+ gtk_tree_view_column_pack_end( column, renderer, TRUE );
+
+ gtk_tree_view_column_set_cell_data_func( column, renderer,
+ wxGtkTreeCellDataFunc, (gpointer) GetRenderer(), NULL );
+}
+
+wxDataViewColumn::~wxDataViewColumn()
+{
+}
+
+void wxDataViewColumn::OnInternalIdle()
+{
+ if (m_isConnected)
+ return;
+
+ if (GTK_WIDGET_REALIZED(GetOwner()->m_treeview))
+ {
+ GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
+ if (column->button)
+ {
+ g_signal_connect(column->button, "button_press_event",
+ G_CALLBACK (gtk_dataview_header_button_press_callback), this);
+
+ m_isConnected = true;
+ }
+ }
+}
+
+void wxDataViewColumn::SetOwner( wxDataViewCtrl *owner )
+{
+ wxDataViewColumnBase::SetOwner( owner );
+
+ GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
+
+ gtk_tree_view_column_set_title( column, wxGTK_CONV_FONT(GetTitle(), GetOwner()->GetFont() ) );
+}
+
+void wxDataViewColumn::SetTitle( const wxString &title )
+{
+ GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
+
+ if (m_isConnected)
+ {
+ // disconnect before column->button gets recreated
+ g_signal_handlers_disconnect_by_func( column->button,
+ (GtkWidget*) gtk_dataview_header_button_press_callback, this);
+
+ m_isConnected = false;
+ }
+
+ // FIXME: can it really happen that we don't have the owner here??
+ wxDataViewCtrl *ctrl = GetOwner();
+ gtk_tree_view_column_set_title( column, ctrl ? wxGTK_CONV_FONT(title, ctrl->GetFont())
+ : wxGTK_CONV_SYS(title) );
+
+ gtk_tree_view_column_set_widget( column, NULL );
+}
+
+wxString wxDataViewColumn::GetTitle() const
+{
+ const gchar *str = gtk_tree_view_column_get_title( GTK_TREE_VIEW_COLUMN(m_column) );
+ return wxConvFileName->cMB2WX(str);
+}
+
+void wxDataViewColumn::SetBitmap( const wxBitmap &bitmap )
+{
+ wxDataViewColumnBase::SetBitmap( bitmap );
+
+ GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
+ if (bitmap.Ok())
+ {
+ GtkImage *gtk_image = GTK_IMAGE( gtk_image_new() );
+
+ GdkBitmap *mask = (GdkBitmap *) NULL;
+ if (bitmap.GetMask())
+ mask = bitmap.GetMask()->GetBitmap();
+
+ if (bitmap.HasPixbuf())
+ {
+ gtk_image_set_from_pixbuf(GTK_IMAGE(gtk_image),
+ bitmap.GetPixbuf());
+ }
+ else
+ {
+ gtk_image_set_from_pixmap(GTK_IMAGE(gtk_image),
+ bitmap.GetPixmap(), mask);
+ }
+ gtk_widget_show( GTK_WIDGET(gtk_image) );
+
+ gtk_tree_view_column_set_widget( column, GTK_WIDGET(gtk_image) );
+ }
+ else
+ {
+ gtk_tree_view_column_set_widget( column, NULL );
+ }
+}
+
+void wxDataViewColumn::SetHidden( bool hidden )
+{
+ gtk_tree_view_column_set_visible( GTK_TREE_VIEW_COLUMN(m_column), !hidden );
+}
+
+void wxDataViewColumn::SetResizeable( bool resizeable )
+{
+ gtk_tree_view_column_set_resizable( GTK_TREE_VIEW_COLUMN(m_column), resizeable );
+}
+
+void wxDataViewColumn::SetAlignment( wxAlignment align )
+{
+ GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
+
+ gfloat xalign = 0.0;
+ if (align == wxALIGN_RIGHT)
+ xalign = 1.0;
+ if (align == wxALIGN_CENTER_HORIZONTAL ||
+ align == wxALIGN_CENTER)
+ xalign = 0.5;
+
+ gtk_tree_view_column_set_alignment( column, xalign );
+}
+
+wxAlignment wxDataViewColumn::GetAlignment() const
+{
+ gfloat xalign = gtk_tree_view_column_get_alignment( GTK_TREE_VIEW_COLUMN(m_column) );
+
+ if (xalign == 1.0)
+ return wxALIGN_RIGHT;
+ if (xalign == 0.5)
+ return wxALIGN_CENTER_HORIZONTAL;
+
+ return wxALIGN_LEFT;
+}
+
+void wxDataViewColumn::SetSortable( bool sortable )
+{
+ GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
+
+ if (sortable)
+ gtk_tree_view_column_set_sort_column_id( column, GetModelColumn() );
+ else
+ gtk_tree_view_column_set_sort_column_id( column, -1 );
+}
+
+bool wxDataViewColumn::IsSortable() const
+{
+ GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
+ return (gtk_tree_view_column_get_sort_column_id( column ) != -1);
+}
+
+bool wxDataViewColumn::IsResizeable() const
+{
+ GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
+ return gtk_tree_view_column_get_resizable( column );
+}
+
+bool wxDataViewColumn::IsHidden() const
+{
+ GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
+ return !gtk_tree_view_column_get_visible( column );
+}
+
+void wxDataViewColumn::SetSortOrder( bool ascending )
+{
+ GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
+
+ if (ascending)
+ gtk_tree_view_column_set_sort_order( column, GTK_SORT_ASCENDING );
+ else
+ gtk_tree_view_column_set_sort_order( column, GTK_SORT_DESCENDING );
+}
+
+bool wxDataViewColumn::IsSortOrderAscending() const
+{
+ GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN(m_column);
+
+ return (gtk_tree_view_column_get_sort_order( column ) != GTK_SORT_DESCENDING);
+}
+
+void wxDataViewColumn::SetMinWidth( int width )
+{
+ gtk_tree_view_column_set_min_width( GTK_TREE_VIEW_COLUMN(m_column), width );
+}
+
+int wxDataViewColumn::GetMinWidth() const
+{
+ return gtk_tree_view_column_get_min_width( GTK_TREE_VIEW_COLUMN(m_column) );
+}
+
+int wxDataViewColumn::GetWidth() const
+{
+ return gtk_tree_view_column_get_width( GTK_TREE_VIEW_COLUMN(m_column) );
+}
+
+void wxDataViewColumn::SetWidth( int width )
+{
+ gtk_tree_view_column_set_fixed_width( GTK_TREE_VIEW_COLUMN(m_column), width );
+}
+
+
+//-----------------------------------------------------------------------------
+// wxGtkTreeModelNode
+//-----------------------------------------------------------------------------
+
+void wxGtkTreeModelNode::Resort()
+{
+ g_model = m_internal->GetDataViewModel();
+
+ size_t child_count = GetChildCount();
+ if (child_count == 0)
+ return;
+
+ size_t node_count = GetNodesCount();
+
+ if (child_count == 1)
+ {
+ if (node_count == 1)
+ {
+ wxGtkTreeModelNode *node = m_nodes.Item( 0 );
+ node->Resort();
+ }
+ return;
+ }
+
+ wxGtkTreeModelChildren *new_array = new wxGtkTreeModelChildren( wxGtkTreeModelNodeCmp );
+
+ size_t pos;
+ for (pos = 0; pos < child_count; pos++)
+ new_array->Add( m_children->Item( pos ) );
+
+ gint *new_order = new gint[child_count];
+
+ for (pos = 0; pos < child_count; pos++)
+ {
+ void *id = new_array->Item( pos );
+ size_t old_pos;
+ for (old_pos = 0; old_pos < child_count; old_pos++)
+ {
+ if (id == m_children->Item(old_pos))
+ {
+ new_order[pos] = old_pos;
+ break;
+ }
+ }
+ }
+
+// for (pos = 0; pos < count; pos++)
+// m_children->Clear();
+ delete m_children;
+
+ m_children = new_array;
+
+ GtkTreeModel *gtk_tree_model = GTK_TREE_MODEL( m_internal->GetGtkModel() );
+
+ GtkTreePath *path = gtk_tree_path_new ();
+ wxGtkTreeModelNode *parent = GetParent();
+ void *id = GetItem().GetID();
+
+ while (parent)
+ {
+ int pos = parent->GetChildren().Index( id );
+ gtk_tree_path_prepend_index( path, pos );
+ id = parent->GetItem().GetID();
+ parent = parent->GetParent();
+ }
+
+ GtkTreeIter iter;
+ iter.user_data = id;
+ iter.stamp = m_internal->GetGtkModel()->stamp;
+ gtk_tree_model_rows_reordered( gtk_tree_model, path, &iter, new_order );
+
+ gtk_tree_path_free (path);
+
+ delete [] new_order;
+
+ for (pos = 0; pos < node_count; pos++)
+ {
+ wxGtkTreeModelNode *node = m_nodes.Item( pos );
+ node->Resort();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// wxDataViewCtrlInternal
+//-----------------------------------------------------------------------------
+
+wxDataViewCtrlInternal::wxDataViewCtrlInternal( wxDataViewCtrl *owner,
+ wxDataViewModel *wx_model, GtkWxTreeModel *gtk_model )
+{
+ m_owner = owner;
+ m_wx_model = wx_model;
+ m_gtk_model = gtk_model;
+ m_root = NULL;
+ InitTree();
+}
+
+wxDataViewCtrlInternal::~wxDataViewCtrlInternal()
+{
+ g_object_unref( m_gtk_model );
+}
+
+void wxDataViewCtrlInternal::InitTree()
+{
+ wxDataViewItem item;
+ m_root = new wxGtkTreeModelNode( NULL, item, this );
+
+ BuildBranch( m_root );
+}
+
+void wxDataViewCtrlInternal::BuildBranch( wxGtkTreeModelNode *node )
+{
+ if (node->GetChildCount() == 0)
+ {
+ wxDataViewItem child = m_wx_model->GetFirstChild( node->GetItem() );
+ while (child.IsOk())
+ {
+ if (m_wx_model->IsContainer( child ))
+ node->AddNode( new wxGtkTreeModelNode( node, child, this ) );
+ else
+ node->AddLeave( child.GetID() );
+
+ // Don't send any events here
+
+ child = m_wx_model->GetNextSibling( child );
+ }
+ }
+}
+
+void wxDataViewCtrlInternal::Resort()
+{
+ m_root->Resort();
+}
+
+bool wxDataViewCtrlInternal::ItemAdded( const wxDataViewItem &parent, const wxDataViewItem &item )
+{
+ wxGtkTreeModelNode *parent_node = FindNode( parent );
+ if (m_wx_model->IsContainer( item ))
+ parent_node->AddNode( new wxGtkTreeModelNode( parent_node, item, this ) );
+ else
+ parent_node->AddLeave( item.GetID() );
+
+ wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_MODEL_ITEM_ADDED, m_owner->GetId() );
+ event.SetEventObject( m_owner );
+ event.SetModel( m_owner->GetModel() );
+ event.SetItem( item );
+ m_owner->GetEventHandler()->ProcessEvent( event );
+
+ return true;
+}
+
+bool wxDataViewCtrlInternal::ItemDeleted( const wxDataViewItem &parent, const wxDataViewItem &item )
+{
+ wxGtkTreeModelNode *parent_node = FindNode( parent );
+ parent_node->DeleteChild( item.GetID() );
+
+ wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_MODEL_ITEM_DELETED, m_owner->GetId() );
+ event.SetEventObject( m_owner );
+ event.SetModel( m_owner->GetModel() );
+ event.SetItem( item );
+ m_owner->GetEventHandler()->ProcessEvent( event );
+
+ return true;
+}
+
+bool wxDataViewCtrlInternal::ItemChanged( const wxDataViewItem &item )
+{
+ wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_MODEL_ITEM_CHANGED, m_owner->GetId() );
+ event.SetEventObject( m_owner );
+ event.SetModel( m_owner->GetModel() );
+ event.SetItem( item );
+ m_owner->GetEventHandler()->ProcessEvent( event );
+
+ return true;
+}
+
+bool wxDataViewCtrlInternal::ValueChanged( const wxDataViewItem &item, unsigned int col )
+{
+ wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_MODEL_VALUE_CHANGED, m_owner->GetId() );
+ event.SetEventObject( m_owner );
+ event.SetModel( m_owner->GetModel() );
+ event.SetColumn( col );
+ event.SetItem( item );
+ m_owner->GetEventHandler()->ProcessEvent( event );
+
+ return true;
+}
+
+bool wxDataViewCtrlInternal::Cleared()
+{
+ wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_MODEL_CLEARED, m_owner->GetId() );
+ event.SetEventObject( m_owner );
+ event.SetModel( m_owner->GetModel() );
+ m_owner->GetEventHandler()->ProcessEvent( event );
+
+ return true;
+}
+
+gboolean wxDataViewCtrlInternal::get_iter( GtkTreeIter *iter, GtkTreePath *path )
+{
+ g_model = m_wx_model;
+
+ int depth = gtk_tree_path_get_depth( path );
+
+ wxGtkTreeModelNode *node = m_root;
+
+ int i;
+ for (i = 0; i < depth; i++)
+ {
+ BuildBranch( node );
+
+ gint pos = gtk_tree_path_get_indices (path)[i];
+ if (pos < 0) return FALSE;
+ if ((size_t)pos >= node->GetChildCount()) return FALSE;
+
+ void* id = node->GetChildren().Item( (size_t) pos );
+
+ if (i == depth-1)
+ {
+ iter->stamp = m_gtk_model->stamp;
+ iter->user_data = id;
+ return TRUE;
+ }
+
+ size_t count = node->GetNodes().GetCount();
+ size_t pos2;
+ for (pos2 = 0; pos2 < count; pos2++)
+ {
+ wxGtkTreeModelNode *child_node = node->GetNodes().Item( pos2 );
+ if (child_node->GetItem().GetID() == id)
+ {
+ node = child_node;
+ break;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+GtkTreePath *wxDataViewCtrlInternal::get_path( GtkTreeIter *iter )
+{
+ g_model = m_wx_model;
+
+ GtkTreePath *retval = gtk_tree_path_new ();
+ void *id = iter->user_data;
+
+ wxGtkTreeModelNode *node = FindParentNode( iter );
+ while (node)
+ {
+ int pos = node->GetChildren().Index( id );
+ gtk_tree_path_prepend_index( retval, pos );
+
+ id = node->GetItem().GetID();
+ node = node->GetParent();
+ }
+
+ return retval;
+}
+
+GtkTreePath *wxDataViewCtrlInternal::get_path_safe( GtkTreeIter *iter )
+{
+ g_model = m_wx_model;
+
+ GtkTreePath *retval = gtk_tree_path_new ();
+ void *id = iter->user_data;
+
+ wxGtkTreeModelNode *node = FindParentNode( iter );
+ while (node)
+ {
+ size_t pos;
+ for (pos = 0; pos < node->GetChildren().GetCount(); pos++)
+ {
+ if (id == node->GetChildren().Item( pos ))
+ {
+ gtk_tree_path_prepend_index( retval, (int) pos );
+ continue;
+ }
+ }
+
+ id = node->GetItem().GetID();
+ node = node->GetParent();
+ }
+
+ return retval;
+}
+
+gboolean wxDataViewCtrlInternal::iter_next( GtkTreeIter *iter )
+{
+ g_model = m_wx_model;
+
+ wxGtkTreeModelNode *parent = FindParentNode( iter );
+ if( parent == NULL )
+ return FALSE;
+
+ unsigned int pos = parent->GetChildren().Index( iter->user_data );
+
+ if (pos == parent->GetChildCount()-1)
+ return FALSE;
+
+ iter->stamp = m_gtk_model->stamp;
+ iter->user_data = parent->GetChildren().Item( pos+1 );
+
+ return TRUE;
+}
+
+gboolean wxDataViewCtrlInternal::iter_children( GtkTreeIter *iter, GtkTreeIter *parent )
+{
+ g_model = m_wx_model;
+
+ wxDataViewItem item( (void*) parent->user_data );
+
+ if (!m_wx_model->IsContainer( item ))
+ return FALSE;
+
+ wxGtkTreeModelNode *parent_node = FindNode( parent );
+ BuildBranch( parent_node );
+
+ if (parent_node->GetChildCount() == 0)
+ return FALSE;
+
+ iter->stamp = m_gtk_model->stamp;
+ iter->user_data = (gpointer) parent_node->GetChildren().Item( 0 );
+
+ return TRUE;
+}
+
+gboolean wxDataViewCtrlInternal::iter_has_child( GtkTreeIter *iter )
+{
+ g_model = m_wx_model;
+
+ wxDataViewItem item( (void*) iter->user_data );
+ bool is_container = m_wx_model->IsContainer( item );
+
+ if (!is_container)
+ return FALSE;
+
+ wxGtkTreeModelNode *node = FindNode( iter );
+ BuildBranch( node );
+
+ return (node->GetChildCount() > 0);
+}
+
+gint wxDataViewCtrlInternal::iter_n_children( GtkTreeIter *iter )
+{
+ g_model = m_wx_model;
+
+ wxDataViewItem item( (void*) iter->user_data );
+
+ if (!m_wx_model->IsContainer( item ))
+ return 0;
+
+ wxGtkTreeModelNode *parent_node = FindNode( iter );
+ BuildBranch( parent_node );
+
+ // wxPrintf( "iter_n_children %d\n", parent_node->GetChildCount() );
+
+ return parent_node->GetChildCount();
+}
+
+gboolean wxDataViewCtrlInternal::iter_nth_child( GtkTreeIter *iter, GtkTreeIter *parent, gint n )
+{
+ g_model = m_wx_model;
+
+ void* id = NULL;
+ if (parent) id = (void*) parent->user_data;
+ wxDataViewItem item( id );
+
+ if (!m_wx_model->IsContainer( item ))
+ return FALSE;
+
+ wxGtkTreeModelNode *parent_node = FindNode( parent );
+ BuildBranch( parent_node );
+
+ // wxPrintf( "iter_nth_child %d\n", n );
+
+ iter->stamp = m_gtk_model->stamp;
+ iter->user_data = parent_node->GetChildren().Item( n );
+
+ return TRUE;
+}
+
+gboolean wxDataViewCtrlInternal::iter_parent( GtkTreeIter *iter, GtkTreeIter *child )
+{
+ g_model = m_wx_model;
+
+ wxGtkTreeModelNode *node = FindParentNode( child );
+ if (!node)
+ return FALSE;
+
+ iter->stamp = m_gtk_model->stamp;
+ iter->user_data = (gpointer) node->GetItem().GetID();
+
+ return TRUE;
+}
+
+static wxGtkTreeModelNode*
+wxDataViewCtrlInternal_FindNode( wxDataViewModel * model, wxGtkTreeModelNode *treeNode, const wxDataViewItem &item )
+{
+ if( model == NULL )
+ return NULL;
+
+ ItemList list;
+ list.DeleteContents( true );
+ wxDataViewItem it( item );
+ while( it.IsOk() )
+ {
+ wxDataViewItem * pItem = new wxDataViewItem( it );
+ list.Insert( pItem );
+ it = model->GetParent( it );
+ }
+
+ wxGtkTreeModelNode * node = treeNode;
+ for( ItemList::compatibility_iterator n = list.GetFirst(); n; n = n->GetNext() )
+ {
+ if( node && node->GetNodes().GetCount() != 0 )
+ {
+ int len = node->GetNodes().GetCount();
+ wxGtkTreeModelNodes nodes = node->GetNodes();
+ int j = 0;
+ for( ; j < len; j ++)
+ {
+ if( nodes[j]->GetItem() == *(n->GetData()))
+ {
+ node = nodes[j];
+ break;
+ }
+ }
+
+ if( j == len )
+ {
+ return NULL;
+ }
+ }
+ else
+ return NULL;
+ }
+ return node;
+
+}
+
+wxGtkTreeModelNode *wxDataViewCtrlInternal::FindNode( GtkTreeIter *iter )
+{
+ if (!iter)
+ return m_root;
+
+ wxDataViewItem item( (void*) iter->user_data );
+ if (!item.IsOk())
+ return m_root;
+
+ wxGtkTreeModelNode *result = wxDataViewCtrlInternal_FindNode( m_wx_model, m_root, item );
+
+ if (!result)
+ {
+ wxPrintf( "Not found %d\n", (int) iter->user_data );
+ char *crash = NULL;
+ *crash = 0;
+ }
+
+ return result;
+}
+
+wxGtkTreeModelNode *wxDataViewCtrlInternal::FindNode( const wxDataViewItem &item )
+{
+ if (!item.IsOk())
+ return m_root;
+
+ wxGtkTreeModelNode *result = wxDataViewCtrlInternal_FindNode( m_wx_model, m_root, item );
+
+ if (!result)
+ {
+ wxPrintf( "Not found %d\n", (int) item.GetID() );
+ char *crash = NULL;
+ *crash = 0;
+ }
+
+ return result;
+}
+
+static wxGtkTreeModelNode*
+wxDataViewCtrlInternal_FindParentNode( wxDataViewModel * model, wxGtkTreeModelNode *treeNode, const wxDataViewItem &item )
+{
+ if( model == NULL )
+ return NULL;
+
+ ItemList list;
+ list.DeleteContents( true );
+ if( !item.IsOk() )
+ return NULL;
+
+ wxDataViewItem it( model->GetParent( item ) );
+ while( it.IsOk() )
+ {
+ wxDataViewItem * pItem = new wxDataViewItem( it );
+ list.Insert( pItem );
+ it = model->GetParent( it );
+ }
+
+ wxGtkTreeModelNode * node = treeNode;
+ for( ItemList::compatibility_iterator n = list.GetFirst(); n; n = n->GetNext() )
+ {
+ if( node && node->GetNodes().GetCount() != 0 )
+ {
+ int len = node->GetNodes().GetCount();
+ wxGtkTreeModelNodes nodes = node->GetNodes();
+ int j = 0;
+ for( ; j < len; j ++)
+ {
+ if( nodes[j]->GetItem() == *(n->GetData()))
+ {
+ node = nodes[j];
+ break;
+ }
+ }
+
+ if( j == len )
+ {
+ return NULL;
+ }
+ }
+ else
+ return NULL;
+ }
+ //Examine whether the node is item's parent node
+ int len = node->GetChildCount();
+ for( int i = 0; i < len ; i ++ )
+ {
+ if( node->GetChildren().Item( i ) == item.GetID() )
+ return node;
+ }
+ return NULL;
+}
+
+wxGtkTreeModelNode *wxDataViewCtrlInternal::FindParentNode( GtkTreeIter *iter )
+{
+ if (!iter)
+ return NULL;
+
+ wxDataViewItem item( (void*) iter->user_data );
+ if (!item.IsOk())
+ return NULL;
+
+ return wxDataViewCtrlInternal_FindParentNode( m_wx_model, m_root, item );
+}
+
+wxGtkTreeModelNode *wxDataViewCtrlInternal::FindParentNode( const wxDataViewItem &item )
+{
+ if (!item.IsOk())
+ return NULL;
+
+ return wxDataViewCtrlInternal_FindParentNode( m_wx_model, m_root, item );
+}
+
+//-----------------------------------------------------------------------------
+// wxDataViewCtrl signal callbacks
+//-----------------------------------------------------------------------------
+
+static void
+wxdataview_selection_changed_callback( GtkTreeSelection* selection, wxDataViewCtrl *dv )
+{
+ if (!GTK_WIDGET_REALIZED(dv->m_widget))
+ return;
+
+ wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_SELECTED, dv->GetId() );
+ // TODO: item
+ event.SetModel( dv->GetModel() );
+ dv->GetEventHandler()->ProcessEvent( event );
+}
+
+static void
+wxdataview_row_activated_callback( GtkTreeView* treeview, GtkTreePath *path,
+ GtkTreeViewColumn *column, wxDataViewCtrl *dv )
+{
+ wxDataViewEvent event( wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, dv->GetId() );
+
+ GtkTreeIter iter;
+ dv->GtkGetInternal()->get_iter( &iter, path );
+ wxDataViewItem item( (void*) iter.user_data );;
+ event.SetItem( item );
+ event.SetModel( dv->GetModel() );
+ dv->GetEventHandler()->ProcessEvent( event );
+}
+
+//-----------------------------------------------------------------------------
+// wxDataViewCtrl
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// InsertChild for wxDataViewCtrl
+//-----------------------------------------------------------------------------
+
+static void wxInsertChildInDataViewCtrl( wxWindowGTK* parent, wxWindowGTK* child )
+{
+ wxDataViewCtrl * dvc = (wxDataViewCtrl*) parent;
+ GtkWidget *treeview = dvc->GtkGetTreeView();
+
+ // Insert widget in GtkTreeView
+ if (GTK_WIDGET_REALIZED(treeview))
+ gtk_widget_set_parent_window( child->m_widget,
+ gtk_tree_view_get_bin_window( GTK_TREE_VIEW(treeview) ) );
+ gtk_widget_set_parent( child->m_widget, treeview );
+}
+
+static
+void gtk_dataviewctrl_size_callback( GtkWidget *WXUNUSED(widget),
+ GtkAllocation *alloc,
+ wxDataViewCtrl *win )
+{
+ wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
+ while (node)
+ {
+ wxWindow *child = node->GetData();
+
+ GtkRequisition req;
+ gtk_widget_size_request( child->m_widget, &req );
+
+ GtkAllocation alloc;
+ alloc.x = child->m_x;
+ alloc.y = child->m_y;
+ alloc.width = child->m_width;
+ alloc.height = child->m_height;
+ gtk_widget_size_allocate( child->m_widget, &alloc );
+
+ node = node->GetNext();
+ }
+}
+
+
+
+IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
+
+wxDataViewCtrl::~wxDataViewCtrl()
+{
+ if (m_notifier)
+ GetModel()->RemoveNotifier( m_notifier );
+
+ // remove the model from the GtkTreeView before it gets destroyed by the
+ // wxDataViewCtrlBase's dtor
+ gtk_tree_view_set_model( GTK_TREE_VIEW(m_treeview), NULL );
+
+ delete m_internal;
+}
+
+void wxDataViewCtrl::Init()
+{
+ m_notifier = NULL;
+}
+
+bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
+ const wxPoint& pos, const wxSize& size,
+ long style, const wxValidator& validator )
+{
+ Init();
+
+ if (!PreCreation( parent, pos, size ) ||
+ !CreateBase( parent, id, pos, size, style, validator ))
+ {
+ wxFAIL_MSG( wxT("wxDataViewCtrl creation failed") );
+ return false;
+ }
+
+ m_insertCallback = wxInsertChildInDataViewCtrl;
+
+ m_widget = gtk_scrolled_window_new (NULL, NULL);
+
+ GtkScrolledWindowSetBorder(m_widget, style);
+
+ m_treeview = gtk_tree_view_new();
+ gtk_container_add (GTK_CONTAINER (m_widget), m_treeview);
+
+ g_signal_connect (m_treeview, "size_allocate",
+ G_CALLBACK (gtk_dataviewctrl_size_callback), this);
+
+#ifdef __WXGTK26__
+ if (!gtk_check_version(2,6,0))
+ gtk_tree_view_set_fixed_height_mode( GTK_TREE_VIEW(m_treeview), TRUE );
+#endif
+
+ if (style & wxDV_MULTIPLE)
+ {
+ GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
+ gtk_tree_selection_set_mode( selection, GTK_SELECTION_MULTIPLE );
+ }
+
+ gtk_tree_view_set_headers_visible( GTK_TREE_VIEW(m_treeview), (style & wxDV_NO_HEADER) == 0 );
+
+#ifdef __WXGTK210__
+ if (!gtk_check_version(2,10,0))
+ {
+ GtkTreeViewGridLines grid = GTK_TREE_VIEW_GRID_LINES_NONE;
+
+ if ((style & wxDV_HORIZ_RULES) != 0 &&
+ (style & wxDV_VERT_RULES) != 0)
+ grid = GTK_TREE_VIEW_GRID_LINES_BOTH;
+ else if (style & wxDV_VERT_RULES)
+ grid = GTK_TREE_VIEW_GRID_LINES_VERTICAL;
+ else if (style & wxDV_HORIZ_RULES)
+ grid = GTK_TREE_VIEW_GRID_LINES_HORIZONTAL;
+
+ gtk_tree_view_set_grid_lines( GTK_TREE_VIEW(m_treeview), grid );
+ }
+ else
+#endif
+ {
+ gtk_tree_view_set_rules_hint( GTK_TREE_VIEW(m_treeview), (style & wxDV_HORIZ_RULES) != 0 );
+ }
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (m_widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+ gtk_widget_show (m_treeview);
+
+ m_parent->DoAddChild( this );
+
+ PostCreation(size);
+
+ GtkEnableSelectionEvents();
+
+ g_signal_connect_after (m_treeview, "row_activated",
+ G_CALLBACK (wxdataview_row_activated_callback), this);
+
+ return true;
+}
+
+void wxDataViewCtrl::OnInternalIdle()
+{
+ wxWindow::OnInternalIdle();
+
+ unsigned int cols = GetColumnCount();
+ unsigned int i;
+ for (i = 0; i < cols; i++)
+ {
+ wxDataViewColumn *col = GetColumn( i );
+ col->OnInternalIdle();
+ }
+}
+
+bool wxDataViewCtrl::AssociateModel( wxDataViewModel *model )
+{
+ if (!wxDataViewCtrlBase::AssociateModel( model ))
+ return false;
+
+ GtkWxTreeModel *gtk_model = wxgtk_tree_model_new();
+ m_internal = new wxDataViewCtrlInternal( this, model, gtk_model );
+ gtk_model->internal = m_internal;
+
+ m_notifier = new wxGtkDataViewModelNotifier( gtk_model, model, this );
+
+ model->AddNotifier( m_notifier );
+
+ gtk_tree_view_set_model( GTK_TREE_VIEW(m_treeview), GTK_TREE_MODEL(gtk_model) );
+
+ // unref in wxDataViewCtrlInternal
+ // g_object_unref( gtk_model );
+
+ return true;
+}
+
+bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
+{
+ if (!wxDataViewCtrlBase::AppendColumn(col))
+ return false;
+
+ GtkTreeViewColumn *column = (GtkTreeViewColumn *)col->GetGtkHandle();
+
+ gtk_tree_view_append_column( GTK_TREE_VIEW(m_treeview), column );
+
+ return true;
+}
+
+wxDataViewItem wxDataViewCtrl::GetSelection()
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
+
+ if (m_windowStyle & wxDV_MULTIPLE)
+ {
+ }
+ else
+ {
+ GtkTreeIter iter;
+ if (gtk_tree_selection_get_selected( selection, NULL, &iter ))
+ {
+ wxDataViewItem item( (void*) iter.user_data );
+ return item;
+ }
+ }
+
+ return wxDataViewItem(0);
+}
+
+void wxDataViewCtrl::DoSetExpanderColumn()
+{
+}
+
+void wxDataViewCtrl::DoSetIndent()
+{
+}
+
+void wxDataViewCtrl::GtkDisableSelectionEvents()
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
+ g_signal_connect_after (selection, "changed",
+ G_CALLBACK (wxdataview_selection_changed_callback), this);
+}
+
+void wxDataViewCtrl::GtkEnableSelectionEvents()
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(m_treeview) );
+ g_signal_handlers_disconnect_by_func( selection,
+ (gpointer) (wxdataview_selection_changed_callback), this);
+}
+
+// static
+wxVisualAttributes
+wxDataViewCtrl::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
+{
+ return GetDefaultAttributesFromGTKWidget(gtk_tree_view_new);
+}
+
+
+#endif
+ // !wxUSE_GENERICDATAVIEWCTRL
+
+#endif
+ // wxUSE_DATAVIEWCTRL