Re: cool stuff in cvs head



muppet wrote:

- CellRenderer subclassing
i've merged the Gtk2::CellRenderer subclassing code from a couple of weeks ago into Gtk2, with torsten's examples.


Thanks, appreciated as always.

I've been playing around with the cell-renderer lately, and I'm running into problems. I had hoped that this latest "release" would fix some of these problems, but no such luck. BTW, it compiles cleanly on my system, but not all tests run successfully in Gtk2.

The problems in the renderers show up in two cases:
1) When row drap'n'drog is enabled
2) When a full tree is used instead of a list.

The first case is easily shown by just a few changes to the cellrenderer_popup.pl example. Please see attached diff. There are two main problems: - The current renderer code gets some unexpected parameters when a dnd operation is started. It looks to me as if the renderer in this case is used to render the selected row to a pixmap instead of a window, the pixmap is then dragged around following the cursor. I haven't looked into fixing this problem, it was too easy to work around it by just ignoring the rendering request... - After using the popup to select a new entry, the treeview will consider a dnd operation to have started as soon as the cursor enters the window. Looks really weird... The "workaround" is for the user to do the drop outside the window... I've tried various stuff to try to track this one down, but no luck so far. In the process I also ran into a problem when trying to do a "$menu->get_flags". The xs-code for Gtk2::Widget said "inhandled case in flag_get - shouldn't happen", but it certainly did.

The second case is a bit more difficult to show. None of the existing examples is using the TreeStore, and that's where it shows up. I've attached my own testcase (warning: work-in-progress) which of course contains lots of stuff that has nothing to with this problem. Anyway, the problem is that the window doesn't update correctly when expanding the list. This causes the renderers to put data the data at the position they used before, not to the new position.... He*l, look for yourselves, it's too difficult to explain in words for a poor tung-stuck Norwegian. Please note that this problem is not limited to the custom renderers, it also happens when only stock Gtk renderers are used. A Gtk bug? I haven't tried to go back to letting the treeview pick up the data directly from the model, so it could have something to do with the cell_data_func, I guess.

Does anybody have some working code of how to listen in to all the signal activity going on in a Gtk2 app? There seems to be a way to listen in to the signals using g_signal_add_emission_hook(), and this certainly could be useful for debugging purposes. But the xs-code is commented out. What's the full story?

Bjarne
*** /usr/src/Gtk2-perl/cvs_20031029_gtk2-perl-xs/Gtk2/examples/cellrenderer_popup.pl    Mon Oct 27 20:37:27 
2003
--- cellrenderer_popup.pl       Wed Oct 29 14:43:50 2003
***************
*** 97,102 ****
--- 97,103 ----
  
  sub on_render {
        my ($cell, $window, $widget, $background_area, $cell_area, $expose_area, $flags) = @_;
+       return if ref $window eq 'Gtk2::Gdk::Pixmap'; # XXX FIXME
        my $state = 'normal';
  
        if ($cell->{show_box}) {
***************
*** 227,232 ****
--- 228,236 ----
  
  # now a view
  $treeview = Gtk2::TreeView->new ($model);
+ #$treeview->set_rules_hint (1);
+ $treeview->set_reorderable (1);
+ 
  
  #
  # regular editable text column for column 0, the string
#! /usr/bin/perl -w

use Gtk2 -init;
 
use Data::Dumper;
# An application will be using TreeView to present data from a database.
# - The data from the database is stored in lists of objects.  Access to the
#   data is through these objects only.
# - All cells in one column have the same format, i.e. there is only list of
#   valid values for all cells in a column using the EnumRenderer.  The
#   data-objects from the database know the format.  (For testing purposes,
#   they provide the formats in a compatible way, the class to do
#   type-conversion between database types and Glib types is left out).

# This set of packages tries to emulate this application.

use strict;

package DateRenderer;

use Glib::Object::Subclass "Gtk2::CellRenderer",
    signals => {
        edited => {
            flags => [qw(run-last)],
            param_types => [qw(Glib::String Glib::Scalar)],
        },
    },
    properties => [
        Glib::ParamSpec->boolean (
            "editable", "Editable", "Can I change that?",
            0, [qw(readable writable)]
        ),
        Glib::ParamSpec->string (
            "date", "Date", "What's the date again?",
            "", [qw(readable writable)]
        ),
        Glib::ParamSpec->int (
            'xpad', 'Xpad', 'Internal horizontal padding', 0, 65535,
            3, [qw/readable writable/]
        ),
        Glib::ParamSpec->int (
            'ypad', 'Ypad', 'Internal vertical padding', 0, 65535,
            2, [qw/readable writable/]
        ),
        Glib::ParamSpec->int (
            'arrow_width', 'Arrow_width', 'Width of arrow', 0, 65535,
            15, [qw/readable writable/]
        ),
    ],
;

__PACKAGE__->_install_overrides;

sub INIT_INSTANCE {
    my $self = shift;
    $self->{xpad} = 3;
    $self->{ypad} = 2;
    $self->{arrow_width} = 15;
}

our $popup_window;
our $arrow = Gtk2::Arrow->new ("down", "none");

sub get_date_string {
    my ($cell) = @_;
    return if $cell->{date} eq '';
    my ($year, $month, $day) = split(/\//, $cell->{date});
    return sprintf ("%04d/%02d/%02d", $year, $month, $day);
}

sub calc_size {
    my ($cell, $layout) = @_;
    my ($width, $height) = $layout->get_pixel_size;

    return (
        0, 0,
        $width + $cell->{xpad}*2 + $cell->{arrow_width},
        $height + $cell->{ypad}*2
    );
}

sub on_get_size {
    my ($cell, $widget, $cell_area) = @_;
    my $layout = $cell->get_layout ($widget);
    $layout->set_text ($cell->get_date_string || '');
    return $cell->calc_size ($layout);
}

sub get_layout {
    my ($cell, $widget) = @_;
    return $cell->{layout} if defined $cell->{layout};
    return $cell->{layout} = $widget->create_pango_layout ("");
}

sub on_render {
    my (
        $cell, $window, $widget, $background_area,
        $cell_area, $expose_area, $flags
    ) = @_;
    return if ref $window eq 'Gtk2::Gdk::Pixmap'; # XXX FIXME
    my $state = 'normal';
    if (grep {/selected/} @$flags) {
        $state = $widget->has_focus
            ? 'selected'
            : 'active';
    } else {
        $state = $widget->state eq 'insensitive'
            ? 'insensitive'
            : 'normal';
    }
    my $layout = $cell->get_layout ($widget);
    $layout->set_text ($cell->get_date_string || '');
    my ($xoff, $yoff, $width, $height) = $cell->calc_size ($layout);
    $widget->get_style->paint_layout (
        $window, $state,
        1, $cell_area,
        $widget, "cellrenderertext",
        $cell_area->x + $xoff + $cell->{xpad},
        $cell_area->y + $yoff + $cell->{ypad},
        $layout
    );
    $widget->get_style->paint_arrow ($window, $widget->state,
        'none', $cell_area,
        $arrow, '',
        'down', 1,
        $cell_area->x + $cell_area->width - $cell->{arrow_width},
        $cell_area->y + $cell_area->height - ($cell_area->height - 2),
        $cell->{arrow_width} - 3, $cell_area->height - 2
    );
}

sub on_start_editing {
    my (
        $cell, $event, $view, $path, $background_area,
        $cell_area, $flags
    ) = @_;

    if (defined($popup_window)) {
        $popup_window->destroy;
        $popup_window = undef;
    }
    my ($xorig, $yorig) =  $view->get_bin_window->get_origin;
    my ($xcell, $ycell) = $view->tree_to_widget_coords (
        $cell_area->x, $cell_area->y
    );
    $popup_window = Gtk2::Window->new ('popup');
    my $calendar = Gtk2::Calendar->new;
    my ($year, $month, $day);
    if (defined $cell->get_date_string) {
        ($year, $month, $day) = split(/\//, $cell->get_date_string);
    } else {
        ($year, $month, $day) = (localtime (time))[5,4,3];
        $year += 1900; ++$month;
    }
    $calendar->select_month ($month - 1, $year);
    $calendar->select_day ($day);
    $calendar->display_options (
        [qw(show_heading show_day_names week_start_monday)]
    );
    $calendar->signal_connect (day_selected_double_click => sub {
        my ($calendar) = @_;
        $cell->signal_emit (edited => $path, [$calendar->get_date]);
        $popup_window->destroy;
        $popup_window = undef;
    });
    $popup_window->move ($xorig + $xcell, $yorig + $ycell + $cell_area->height);
    $popup_window->add ($calendar);
    $popup_window->show_all;

    return;
}

1;

package EnumRenderer;

use Glib::Object::Subclass Gtk2::CellRenderer::,
    signals => {
        edited => {
            flags => [qw/run-last/],
            #                  path         index
            param_types => [qw/Glib::String Glib::Int/],
        },
    },
    properties => [
        Glib::ParamSpec->boolean (
            'show_box', 'Show Box',
            'If true, draw an option menu-looking background on the cell',
            1, ['readable', 'writable']
        ),
        Glib::ParamSpec->boolean (
            'editable', 'Editable', 'Can i change that?',
            0, ['readable', 'writable']
        ),
        Glib::ParamSpec->int (
            'xpad', 'Xpad', 'Internal horizontal padding', 0, 65535,
            3, [qw/readable writable/]
        ),
        Glib::ParamSpec->int (
            'ypad', 'Ypad', 'Internal vertical padding', 0, 65535,
            2, [qw/readable writable/]
        ),
        Glib::ParamSpec->int (
            'arrow_width', 'Arrow_width', 'Width of arrow', 0, 65535,
            15, [qw/readable writable/]
        ),
        Glib::ParamSpec->int (
            'index', 'Index', 'Index of selected list item', 0, 65535,
            0, [qw/readable writable/]
        ),
        Glib::ParamSpec->boxed (
            'list', 'List', 'List of possible values',
            'Glib::Scalar', [qw/readable writable/]
        ),
    ],
;

__PACKAGE__->_install_overrides;

sub INIT_INSTANCE {
    my $self = shift;
    $self->{show_box} = 1;
    $self->{xpad} = 3;
    $self->{ypad} = 2;
    $self->{arrow_width} = 15;
#    $self->set (mode => 'editable') if $self->{editable};
}

sub calc_size {
    my ($cell, $layout) = @_;
    my ($w, $h) = $layout->get_pixel_size;
    return (
        0, 0,
        $w + $cell->{xpad} * 2 + $cell->{arrow_width},
        $h + $cell->{ypad} * 2
    );
}

sub on_get_size {
    my ($cell, $widget, $area) = @_;
    return (
        3, 3,
        $area->width - $cell->{arrow_width} - 2*$cell->{xpad} - 4,
        $area->height - 6
    ) if $area;
    my $layout = $cell->get_layout ($widget);
    $layout->set_text ($cell->{list}[$cell->{index}] || "");
    return $cell->calc_size ($layout);
}

sub get_layout {
    my ($cell, $widget) = @_;
    return $cell->{layout} if defined $cell->{layout};
    return $cell->{layout} = $widget->create_pango_layout ("");
}

sub on_render {
    my (
        $cell, $window, $widget, $background_area,
        $cell_area, $expose_area, $flags
    ) = @_;
    return if ref $window eq 'Gtk2::Gdk::Pixmap';
    warn Data::Dumper::Dumper (\ _)
        unless ref $window eq 'Gtk2::Gdk::Window';
    my $state = 'normal';
    if ($cell->{'show_box'}) {
        $widget->get_style->paint_box (
            $window, $widget->state,
            'out', $cell_area,
            undef, "optionmenu",
            $cell_area->x,
            $cell_area->y,
            $cell_area->width, $cell_area->height
        );
    } else {
        if (grep {/selected/} @$flags) {
            $state = $widget->has_focus
                ? 'selected'
                : 'active';
        } else {
            $state = $widget->state eq 'insensitive'
                ? 'insensitive'
                : 'normal';
        }
    }
    my $layout = $cell->get_layout ($widget);
    $layout->set_text ($cell->{list}[$cell->{index}] || "");
    my ($xoff, $yoff, $width, $height) = $cell->calc_size ($layout);
    $widget->get_style->paint_layout (
        $window, $state,
        1, $cell_area,
        $widget, "cellrenderertext",
        $cell_area->x + $xoff + $cell->{xpad},
        $cell_area->y + $yoff + $cell->{ypad},
        $layout
    );
    $widget->get_style->paint_arrow (
        $window, $state,
        'none', $cell_area,
        $widget, "",
        'down', 0,
        $cell_area->x+$cell_area->width - $cell->{arrow_width},
        $cell_area->y+$cell_area->height - ($cell_area->height - 2),
        $cell->{arrow_width} - 3, $cell_area->height - 2
    );
}

sub menu_pos_func {
    my ($menu, $x, $y, $data) = @_;
    my ($treeview, $cell, $cell_area) = @$data;
    my ($wx, $wy) = $treeview->get_bin_window->get_origin;
    my ($tx, $ty) = $treeview->tree_to_widget_coords (
        $cell_area->x, $cell_area->y
    );
    $x = $wx + $tx + $cell->{xpad};
    $y = $wy + $ty + $cell_area->height / 2 - 2;
    # center the menu vertically around the selected item.
    # this is inspired heavily by GtkOptionMenu.
    my $active = $menu->get_active;
    $y -= $active->get_child_requisition->height / 2 if $active;
    foreach my $i ($menu->get_children) {
        last if $i == $active;
        $y -= $i->get_child_requisition->height if $i->visible;
    }
    # play nicely with rtl languages
    if ($treeview->get_direction eq 'rtl') {
        $x =$wx + $tx + $cell_area->width - $menu->get_child_requisition->width;
    }
    return ($x, $y, 1);
}

sub editing_done {
    my ($item, $udata) = @_;
    my ($cell, $path, $idx, $menu) = @$udata;
$menu->popdown;
    $cell->signal_emit ('edited', $path, $idx);
$menu->destroy;
}

sub on_start_editing {
    my (
        $cell, $event, $widget, $path,
        $background_area, $cell_area, $flags
    ) = @_;
    my $menu = Gtk2::Menu->new;
    my @data = @{ $cell->{list} };
    for (my $i = 0 ; $i < @data ; $i++) {
        my $item = Gtk2::MenuItem->new ($data[$i]);
        $item->show;
        $menu->append ($item);
#       $item->{path} = $path;
#       $item->{index} = $i;
#       $item->{text} = $data[$i];
#$item->{menu} = $menu;
        $item->signal_connect (
            activate => \&editing_done, [$cell, $path, $i, $menu]
        );
    }
    $menu->set_active ($cell->{index});
# XXX: Shouldn't happen, but it does...
#    my $widget_flags = $menu->get_flags;
#    warn Data::Dumper::Dumper ($widget_flags);
    $menu->popup (
        undef, undef,
        \&menu_pos_func, [$widget, $cell, $cell_area],
        $event ? $event->button : 0, 0
    );
#    my $item = $menu->get_active;
#    $menu->select_item ($item) if $item;
    return;
}

1;

package Database;

# Emulate the data objects.  The "constructor" just returns a pre-set list
# of objects, where it normally should return a list based on a database
# query.  Undef is a NULL.
# The data is hierarchic.

# Common sub for data types using 'Gtk2::CellRendererText'
sub _get_text_renderer_data {
    my ($col, $cell, $model, $iter, $info) = @_;
    my $row = $model->get ($iter, 0);
    my $get_meth = $info->{get_meth};
    my $a = $row->$get_meth;
    my $t = defined $a ? sprintf ($info->{fmt}, $a) : '';
    $t = substr ($t, 0, $info->{maxlen})
        if defined $info->{maxlen};
    $cell->set (text => $t);
}

sub _get_toggle_renderer_data {
    my ($col, $cell, $model, $iter, $info) = @_;
    my $row = $model->get ($iter, 0);
    my $get_meth = $info->{get_meth};
    my $a = $row->$get_meth;
    $a = 0 unless defined $a;
    $cell->set_active ($a);
}

sub _get_enum_renderer_data {
    my ($col, $cell, $model, $iter, $info) = @_;
    my $row = $model->get ($iter, 0);
    my $get_meth = $info->{get_meth};
    my $a = $row->$get_meth;
    $a = 'NULL' unless defined $a;
    my $i = $info->{lookup}->{$a} || 0;
    $cell->set ('index' => $i);
}

sub _get_date_renderer_data {
    my ($col, $cell, $model, $iter, $info) = @_;
    my $row = $model->get ($iter, 0);
    my $get_meth = $info->{get_meth};
    my $d = $row->$get_meth;
    my $t = $d || '';
    $t = substr ($t, 0, $info->{maxlen})
        if defined $info->{maxlen};
    $cell->set (date => $t);
}

sub _editing_text_finished {
    my ($cell, $path, $new_text, $udata) = @_;
    my ($model, $info) = @$udata;
    $path = Gtk2::TreePath->new_from_string ($path);
    my $iter = $model->get_iter ($path);
    my $row = $model->get ($iter, 0);
    my $set_meth = $info->{set_meth};
    $row->$set_meth ($new_text);
}

sub _editing_toggle_finished {
    my ($cell, $path, $udata) = @_;
    my ($model, $info) = @$udata;
    $path = Gtk2::TreePath->new_from_string ($path);
    my $iter = $model->get_iter ($path);
    my $row = $model->get ($iter, 0);
    my $set_meth = $info->{set_meth};
    my $get_meth = $info->{get_meth};
    $row->$set_meth (!$row->$get_meth);
}

sub _editing_enum_finished {
    my ($cell, $path, $new_index, $udata) = @_;
    my ($model, $info) = @$udata;
    $path = Gtk2::TreePath->new_from_string ($path);
    my $iter = $model->get_iter ($path);
    my $row = $model->get ($iter, 0);
    my $set_meth = $info->{set_meth};
    my $val = $info->{renderer_props}->{list}->[$new_index];
    $val = undef if $val eq 'NULL';
    $row->$set_meth ($val);
}

sub _editing_date_finished {
    my ($cell, $path, $new_date, $udata) = @_;
    my ($year, $month, $day) = @$new_date;
    ++$month;
    $new_date = sprintf ("%04d/%02d/%02d", $year, $month, $day);
    my ($model, $info) = @$udata;
    $path = Gtk2::TreePath->new_from_string ($path);
    my $iter = $model->get_iter ($path);
    my $row = $model->get ($iter, 0);
    my $set_meth = $info->{set_meth};
    $new_date = undef if $new_date eq '';
    $row->$set_meth ($new_date);
}

use constant METADATA => [
    id => {
        is_parent => 1,
        renderer => 'Gtk2::CellRendererText',
        renderer_props => {xalign => 1.0},
        fmt => '%d', maxlen => 8, alignment => 1.0,
        cell_cb => \&_get_text_renderer_data,
    },
    str => {
        renderer => 'Gtk2::CellRendererText',
        fmt => '%s', maxlen => 40,
        cell_cb => \&_get_text_renderer_data,
        renderer_props => {'editable' => 1},
        edited_cb => \&_editing_text_finished,
        edited_signal => 'edited',
    },
    float => {
        renderer => 'Gtk2::CellRendererText',
        fmt => '%.2f', maxlen => 8, alignment => 1.0,
        cell_cb => \&_get_text_renderer_data,
        renderer_props => {'editable' => 1, xalign => 1.0},
        edited_cb => \&_editing_text_finished,
        edited_signal => 'edited',
    },
    bool => {
        renderer => 'Gtk2::CellRendererToggle',
        renderer_props => {'activatable' => 1},
        edited_cb => \&_editing_toggle_finished,
        edited_signal => 'toggled',
        cell_cb => \&_get_toggle_renderer_data,
    },
    date => {
        renderer => 'DateRenderer',
        renderer_props => {'mode' => 'editable'},
        edited_cb => \&_editing_date_finished,
        edited_signal => 'edited',
        cell_cb => \&_get_date_renderer_data,
    },
    enum => {
        renderer => 'EnumRenderer',
        renderer_props => {
            list => [qw(NULL KB SL DF GR)],
            mode => 'editable',
        },
        lookup => {
            NULL => 0,
            KB => 1,
            SL => 2,
            DF => 3,
            GR => 4,
        },
        cell_cb => \&_get_enum_renderer_data,
        fmt => '%s', maxlen => 8,
        edited_cb => \&_editing_enum_finished,
        edited_signal => 'edited',
    },
    set => {
        renderer => 'SetRenderer',
        fmt => sub {
            return '[' . join (",", @_) . ']';
        },
        maxlen => 20,
        legal => ['PROC', 'RAW', 'SHIFT'],
        skip => 1,
    },
];

# Add get/set method names
for (my $i = 0; $i < scalar @{METADATA()}; $i += 2) {
    METADATA->[$i + 1]->{get_meth} = 'get_' . METADATA->[$i];
    METADATA->[$i + 1]->{set_meth} = 'set_' . METADATA->[$i];
}

# Simulate a database query
sub query {
    my ($class, $db_connection, $query) = @_; # Not using these much...
    my @table = (
        #  0: %8d
        #  1: %-40s
        #  2: %8.2f
        #  3: Yes/No (bool), 
        #  4: Date in yyyy/mm/dd format
        #  5: Enum ('KB', 'SL', 'DF', 'GR')
        #  6: Set ('PROC', 'RAW', 'SHIFT')
        #  7: Ref to parent row
        bless ([ 1000, 'A string', 12.3567,   undef, '1970/01/01',  'KB', ['RAW']                 ], $class),
        bless ([ 1001, 'B string', 12.3,          1, '1970/01/01',  'RT', ['PROC', 'RAW']         ], $class),
        bless ([ 1002, 'C string', 12.45,     undef, '1970/01/01',  'SL', ['PROC', 'SHIFT', 'RAW']], $class),
        bless ([ 1003, 'D string', 123567.87,     1, '1970/01/01', undef, ['RAW', 'PROC']         ], $class),
        bless ([ 1004, 'E string', 12345.678, undef, '1970/01/01',  'GR', [undef]                 ], $class),
        bless ([ 1005, 'F string', 1231.3567,     1, '1970/01/01', undef, ['RAW']                 ], $class),
        bless ([ 1006, 'G string', 122.3567,      1,        undef,  'KB', ['RAW']                 ], $class),
        bless ([ 1007,      undef, 12.3567,   undef, '1970/01/01',  'KB', ['RAW']                 ], $class),
    );
    # Make hierarchy
    $table[1]->[7] = $table[0]; $table[2]->[7] = $table[0];
    $table[3]->[7] = $table[2]; $table[4]->[7] = $table[3];
    $table[5]->[7] = $table[4]; $table[7]->[7] = $table[6];
    return \ table;
}

# Accessors
sub get_id {$_[0]->[0]};
sub get_str {$_[0]->[1]};
sub get_float {$_[0]->[2]};
sub get_bool {$_[0]->[3]};
sub get_date {$_[0]->[4]};
sub get_enum {$_[0]->[5]};
sub get_set {$_[0]->[6]};
sub set_id {$_[0]->[0] = $_[1]};
sub set_str {$_[0]->[1] = $_[1]};
sub set_float {$_[0]->[2] = $_[1]};
sub set_bool {$_[0]->[3] = $_[1]};
sub set_date {$_[0]->[4] = $_[1]};
sub set_enum {$_[0]->[5] = $_[1]};
sub set_set {$_[0]->[6] = $_[1]};

sub parent {$_[0]->[7]};
sub reparent {$_[0]->[7] = $_[1]};

1;

package main;

my $window = Gtk2::Window->new;
$window->set_title ("Bjarne's cell renderer test");
$window->signal_connect (delete_event => sub { Gtk2->main_quit; 0; });
 
my $vbox = Gtk2::VBox->new;
$window->add ($vbox);
 
my $label = Gtk2::Label->new;
$label->set_markup ('<big>Test-table</big>');
$vbox->pack_start ($label, 0, 0, 0);
 
# Create and load the model.  The model only contains one single column,
# which is the reference to the object.
my $model = Gtk2::TreeStore->new ('Glib::Scalar');
my $flags = $model->get_flags;
for my $fl (@$flags) {
    print STDERR "Model has flag $fl set\n";
}
my $data = Database->query;
my %iter_by_row = ();
for my $row (@$data) {
    my $iter;
    my $parent = $row->parent;
    # Assume tsorted data
    if (defined $parent) {
        $iter = $model->append ($iter_by_row{$row->parent});
    } else {
        $iter = $model->append (0);
    }
    $model->set ($iter, 0 => $row);
    $iter_by_row{$row} = $iter;
}
undef %iter_by_row;

$model->signal_connect ('row-inserted' => sub {
    my ($model, $path, $iter, $udata) = @_;
    return; # For now
    warn Data::Dumper::Dumper (\ _);
    unless ($model->iter_is_valid ($iter)) {
        printf STDERR "row-inserted: Invalid iterator!\n";
        return;
    }
    my $row = $model->get_value ($iter, 0);
    # XXX: model returns undef for row.  Why???
    my $piter = $model->iter_parent ($iter);
    my $parent_row;
    $parent_row = $model->get ($piter, 0) if defined $piter;
    if (defined $parent_row) {
        printf STDERR "Row %d reparented to %d from %d\n",
            $row->get_id, $parent_row->get_id, $row->parent->get_id;
    } else {
        printf STDERR "Row %d reparented to toplevel\n", $row->get_id;
    }
    $row->reparent ($parent_row);
}, 'drop');

my $mdata = Database->METADATA;

# The view is more complicated, or rather the columns/renderers are
my $view = Gtk2::TreeView->new ($model);
$view->set_rules_hint (1);
$view->set_reorderable (1);
for (my $i = 0; $i < @$mdata; $i += 2) {
    my $title = $mdata->[$i];
    my $cinfo = $mdata->[$i + 1];
    next if $cinfo->{skip};
    my $renderer = $cinfo->{renderer}->new;
    $renderer->set (%{$cinfo->{renderer_props}})
        if exists $cinfo->{renderer_props};
    $renderer->signal_connect (
        $cinfo->{edited_signal} => $cinfo->{edited_cb}, [$model, $cinfo]
    ) if exists $cinfo->{edited_cb};
    my $col = Gtk2::TreeViewColumn->new_with_attributes ($title, $renderer);
    $col->set (
        reorderable => 1,
        resizable => 1,
        alignment => 0.5,
    );
    $renderer->{column} = $col;
    $col->{legal_vals} = $cinfo->{legal} if exists $cinfo->{legal};
    $col->set_cell_data_func ($renderer, $cinfo->{cell_cb}, $cinfo);
    $view->append_column ($col);
    $view->set_expander_column ($col) if exists $cinfo->{is_parent};
}
$vbox->pack_start ($view, 0, 0, 0);
my @cols = $view->get_columns;
printf "Nr columns = %d\n", scalar @cols;

$window->show_all;
 
Gtk2->main;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]