Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

XML Navigator

by ruoso (Curate)
on Sep 27, 2006 at 16:54 UTC ( #575194=CUFP: print w/replies, xml ) Need Help??

Well, editing XMLs in the text itself is a boring task. Mentally visualizing it is quite hard sometimes. So i wanted to write a node-centric XML Viewer/Editor which would allow me to make a better visualization of semi-structured XMLs.

I decided to craft something in Gtk2 and ended with the following code...

This is the start for what the editor will looks like, and the good news is that you can use the navigator as a widget in any application, as it's a Perl/Glib Object. It works nice for complex XMLs, but I think it would suffer with big XMLs as, for some reason I can't explain, I choosed to use XML::DOM.

Well, the first snippet is the module that implements the widget XMLNavigator. The second snippet is a program that uses it.

The way you can use it is: 1) click on a node to put it into the center. 2) for children of the center node, you can use the mouse scroll to navigate through them. Have fun.

The Module (with the patch from ForgotPasswordAgain's comment below):

package XMLNavigator; use strict; use warnings; use Gtk2; use XML::DOM; use POSIX qw(ceil); use constant DEFAULT_COLLAPSED_BOX_WIDTH => 75; use constant DEFAULT_COLLAPSED_BOX_HEIGHT => 45; use constant DEFAULT_FULL_BOX_HEIGHT => 155; use constant DEFAULT_FULL_BOX_WIDTH => 160; use constant DEFAULT_INSETS => 40; use constant RETICENCIAS_WIDTH => 15; use constant RETICENCIAS_HEIGHT => 5; use Glib::Object::Subclass 'Gtk2::DrawingArea', signals => { model_changed => { method => 'do_model_changed', flags => [qw/run-first/], return_type => undef, param_types => [] } }, properties => [ Glib::ParamSpec->scalar ('domdocument', 'domdocument', 'Object containing the DOM Document', [qw/readable writable/]), Glib::ParamSpec->int ('collapsed_box_width', 'collapsed_box_width', 'Width of the collapsed box', 0, 500, DEFAULT_COLLAPSED_BOX_WIDTH, [qw/readable writable/]), Glib::ParamSpec->int ('collapsed_box_height', 'collapsed_box_height', 'Height of the collapsed box', 0, 500, DEFAULT_COLLAPSED_BOX_HEIGHT, [qw/readable writable/]), Glib::ParamSpec->int ('full_box_height', 'full_box_height', 'Height of the full box', 0, 500, DEFAULT_FULL_BOX_HEIGHT, [qw/readable writable/]), Glib::ParamSpec->int ('full_box_width', 'full_box_width', 'Width of the full box', 0, 500, DEFAULT_FULL_BOX_WIDTH, [qw/readable writable/]), Glib::ParamSpec->int ('insets', 'insets', 'Insets between boxes', 0, 500, DEFAULT_FULL_BOX_WIDTH, [qw/readable writable/]), Glib::ParamSpec->int ('max_hori_distance', 'max_hori_distance', 'Maximum Horizontal Distance', 0, 500, 3, [qw/readable writable/]), Glib::ParamSpec->int ('max_vert_distance', 'max_vert_distance', 'Maximum Vertical Distance', 0, 500, 3, [qw/readable writable/]) ]; sub INIT_INSTANCE { my $self = shift; $self->{domdocument} = undef; $self->{matrix} = {}; $self->{collapsed_box_height} = DEFAULT_COLLAPSED_BOX_HEIGHT; $self->{collapsed_box_width} = DEFAULT_COLLAPSED_BOX_WIDTH; $self->{full_box_width} = DEFAULT_FULL_BOX_WIDTH; $self->{full_box_height} = DEFAULT_FULL_BOX_HEIGHT; $self->{max_vert_distance} = 3; $self->{max_hori_distance} = 3; $self->{insets} = DEFAULT_INSETS; $self->signal_connect(button_press_event => \&button_press_eve +nt); $self->signal_connect(expose_event => \&expose_event); $self->signal_connect(configure_event => \&configure_event); $self->signal_connect(size_request => \&do_size_request); $self->signal_connect(scroll_event => \&scroll_event); $self->set_events ([qw(exposure-mask leave-notify-mask button-press-mask button-release-mask scroll-mask)]); } sub GET_PROPERTY { my ($self, $pspec) = @_; if (exists $self->{$pspec->get_name()}) { return $self->{$pspec->get_name()}; } } sub SET_PROPERTY { my ($self, $pspec, $newval) = @_; if (exists $self->{$pspec->get_name()}) { $self->{$pspec->get_name()} = $newval; } if ($pspec->get_name eq 'domdocument') { $self->{matrix} = {}; $self->plan_matrix(); $self->queue_draw(); } elsif ($pspec->get_name eq 'matrix') { $self->plan_matrix(); $self->queue_draw(); } } sub do_model_changed { } sub plan_matrix { my $self = shift; my $max_hori_distance = $self->{max_hori_distance}; my $max_vert_distance = $self->{max_vert_distance}; my $xmlthink_center = $self->{matrix}{0}{0}; my %center_on_row = (); foreach my $key (keys %{$self->{matrix}}) { $center_on_row{$key} = $self->{matrix}{$key}{0}; } $self->{matrix} = {}; if (not defined $self->{domdocument}) { return; } my $xmlthink_root = $self->{domdocument}->getDocumentElement() +; if (not defined $xmlthink_root) { return; } if (not defined $xmlthink_center) { $xmlthink_center = $xmlthink_root; } #print "0, 0 = $xmlthink_center\n"; $self->{matrix}{0}{0} = $xmlthink_center; HORIZONTAL_BW: for my $hori_distance (1..$max_hori_distance+1) { $hori_distance *= -1; my $elem = $self->{matrix}{$hori_distance + 1}{0}; my $other = $elem->getParentNode(); if ($other && $other != $self->{domdocument}) { #print "$hori_distance, 0 = $other\n"; $self->{matrix}{$hori_distance}{0} = $other; } else { last HORIZONTAL_BW; } for my $sign_v (-1, 1) { VERTICAL_BW: for my $vert_distance (1..$max_vert_distance) +{ $vert_distance *= $sign_v; my $other = $self->{matrix}{$hori_dist +ance + 1}{$vert_distance - $sign_v}; my $brot = $sign_v<0?$other->getPrevio +usSibling():$other->getNextSibling(); while (defined $brot && $brot->getNode +Type() != XML::DOM::ELEMENT_NODE) { $brot = $sign_v<0?$brot->getPr +eviousSibling():$brot->getNextSibling(); } if ($brot) { #print "".($hori_distance+1)." +, $vert_distance = $brot\n"; $self->{matrix}{$hori_distance + + 1}{$vert_distance} = $brot; my @list = grep { $_->getNodeT +ype() == XML::DOM::ELEMENT_NODE } $brot->getChildNodes(); if ($#list >= 0) { my $la = $hori_distanc +e+1.0001; $la =~ s/,/./g; #print "$la, $vert_dis +tance = undef\n"; $self->{matrix}{$la}{$ +vert_distance} = undef } } else { last VERTICAL_BW; } if ($hori_distance == -1) { if (abs($vert_distance) == $ma +x_vert_distance -1) { my $obrot = $sign_v<0? +$other->getPreviousSibling():$other->getNextSibling(); if ($obrot) { #print "".($ho +ri_distance+1).", ".($vert_distance + $sign_v)." = undef\n"; $self->{matrix +}{$hori_distance + 1}{$vert_distance + $sign_v} = undef; } last VERTICAL_BW; } } else { if (abs($vert_distance) == $ma +x_vert_distance) { my $obrot = $sign_v<0? +$other->getPreviousSibling():$other->getNextSibling(); if ($obrot) { #print "".($ho +ri_distance+1).", ".($vert_distance + $sign_v)." = undef\n"; $self->{matrix +}{$hori_distance + 1}{$vert_distance + $sign_v} = undef; } } } } } if (abs($hori_distance) == $max_hori_distance + 1) { my $obrot = $other->getParentNode(); #print "$hori_distance, 0 = undef\n"; $self->{matrix}{$hori_distance}{0} = undef; } } HORIZONTAL_FW: for my $hori_distance (1..$max_hori_distance) { my $elem = $self->{matrix}{$hori_distance - 1}{0}; last HORIZONTAL_FW unless defined $elem; my @list = grep { $_->getNodeType() == XML::DOM::ELEME +NT_NODE } $elem->getChildNodes(); last HORIZONTAL_FW unless $#list >= 0; if (not $center_on_row{$hori_distance}) { $center_on_row{$hori_distance} = $list[ceil($# +list/2)]; } my $obj = $center_on_row{$hori_distance}; #print "$hori_distance, 0 = $obj\n"; $self->{matrix}{$hori_distance}{0} = $obj; for my $sign_v (-1, 1) { VERTICAL_FW: for my $vert_distance (1..$max_vert_distance) +{ $vert_distance *= $sign_v; my $other = $self->{matrix}{$hori_dist +ance}{$vert_distance - $sign_v}; my $brot = $sign_v<0?$other->getPrevio +usSibling():$other->getNextSibling(); while (defined $brot && $brot->getNode +Type() != XML::DOM::ELEMENT_NODE) { $brot = $sign_v<0?$brot->getPr +eviousSibling():$brot->getNextSibling(); } if ($brot) { #print "".($hori_distance).", +$vert_distance = $brot\n"; $self->{matrix}{$hori_distance +}{$vert_distance} = $brot; if (grep { $_->getNodeType() = += XML::DOM::ELEMENT_NODE } $brot->getChildNodes()) { my $la = $hori_distanc +e+0.0001; $la =~ s/,/./g; #print "".($la).", $ve +rt_distance = undef\n"; $self->{matrix}{$la}{$ +vert_distance} = undef } } else { last VERTICAL_FW; } if (abs($vert_distance) == $max_vert_d +istance) { my $obrot = $sign_v<0?$other-> +getPreviousSibling():$other->getNextSibling(); if ($obrot) { #print "".($hori_dista +nce).", ".($vert_distance+$sign_v)." = $brot\n"; $self->{matrix}{$hori_ +distance}{$vert_distance + $sign_v} = undef; } } } } if (abs($hori_distance) == $max_hori_distance) { my @list = grep { $_->getNodeType() == XML::DO +M::ELEMENT_NODE } $elem->getChildNodes(); if ($#list >= 0) { #print "".($hori_distance+1).", 0 = un +def\n"; $self->{matrix}{$hori_distance+1}{0} = + undef; } } } $self->drawit_all(); } sub do_size_request { my ($self, $requisition) = @_; $requisition->width ($self->{full_box_width}); $requisition->height ($self->{full_box_height}); } sub configure_event { my ($self, $event) = @_; $self->{pixmap} = Gtk2::Gdk::Pixmap->new ($self->window, $self->allocation->width, $self->allocation->height, -1); # same depth as window $self->{max_vert_distance} = int((($self->allocation->height/2 +)-($self->{full_box_height}/2)-($self->{insets}/2))/($self->{collapse +d_box_height}+$self->{insets})) || 1; $self->{max_hori_distance} = int((($self->allocation->width/2) +-($self->{full_box_width}/2)-($self->{insets}/2))/($self->{collapsed_ +box_width}+$self->{insets})) || 1; $self->plan_matrix(); } sub expose_event { my ($self, $event) = @_; $self->window->draw_drawable ($self->style->fg_gc($self->state), $self->{pixmap}, $event->area->x, $event->area->y, $event->area->x, $event->area->y, $event->area->width, $event->area->height); } sub drawit_all { my $self = shift; return unless $self->{pixmap}; $self->drawit_in(0, 0, $self->allocation->width(), $self->allo +cation->height()); } sub drawit_in { my $self = shift; my ($x, $y, $w, $h) = @_; my $gc = $self->style->fg_gc($self->state); $gc->set_clip_rectangle(Gtk2::Gdk::Rectangle->new($x, $y, $w, +$h)); $gc->set_rgb_fg_color(Gtk2::Gdk::Color->new(255*257, 255*257, +255*257)); $self->{pixmap}->draw_rectangle($gc, 1, 0, 0, $w, $h); $gc->set_rgb_fg_color(Gtk2::Gdk::Color->new(0, 0, 0)); $self->draw_data($self->{pixmap}, $gc); $gc->set_clip_rectangle(undef); $self->queue_draw(); } sub draw_data { my $self = shift; my ($area, $gc) = @_; my $max_hori_distance = $self->{max_hori_distance}; my $max_vert_distance = $self->{max_vert_distance}; my $xmlthink_doc = $self->{domdocument}; return unless $xmlthink_doc; my $xmlthink_root = $xmlthink_doc->getDocumentElement(); my $xmlthink_center = $self->{matrix}{0}{0}; if (not defined $xmlthink_center) { $self->plan_matrix(); $xmlthink_center = $self->{matrix}{0}{0}; return unless $xmlthink_center; } my $canvas_width = $self->allocation->width; my $canvas_height = $self->allocation->height; my $center_x = int($canvas_width/2); my $center_y = int($canvas_height/2); my $fullb_w = $self->{full_box_width}; my $fullb_h = $self->{full_box_height}; my $collb_w = $self->{collapsed_box_width}; my $collb_h = $self->{collapsed_box_height}; my $insets = $self->{insets}; $self->draw_center_element($area, $gc); if ($xmlthink_root != $xmlthink_center) { $self->draw_line_to_parent($area, $gc, $center_x - int +($insets/2) - int($fullb_w/2) + 1, $center_y - int($insets/2)); } for my $hori (sort {$a <=> $b} keys %{$self->{matrix}}) { for my $vert (sort {$a <=> $b} keys %{$self->{matrix}{ +$hori}}) { next if ($hori == 0 && $vert == 0); my $obj = $self->{matrix}{$hori}{$vert}; my $abs_distance_x = (abs($hori) * ($collb_w + + $insets)); my $abs_distance_y = (abs($vert) * ($collb_h + + $insets)); $abs_distance_x += int(($fullb_w - $collb_w)/2 +); if (abs($hori)<0.1) { # Center Column !! $abs_distance_y += int(($fullb_h - $co +llb_h)/2); } my $distance_x = $abs_distance_x * ($hori>=1?1 +:-1); my $distance_y = $abs_distance_y * ($vert==0?0 +:$vert>0?1:-1); my $box_center_x = $center_x + $distance_x; my $box_center_y = $center_y + $distance_y; my $x = $box_center_x - (int($collb_w/2)+int($ +insets/2)); my $y = $box_center_y - (int($collb_h/2)+int($ +insets/2)); if (exists $self->{matrix}{$hori - 1} && int($ +hori)==$hori) { $self->draw_line_to_parent($area, $gc, + $x, int($y+$collb_h/2)); } if ($obj) { $self->draw_collapsed_element($area, $ +gc, $obj, $x, $y); } else { if (int($hori)!=$hori) { # side reticencia $x = $box_center_x + (int($col +lb_w/2)-int($insets/2)); $y = $box_center_y - (int($col +lb_h/2)+int($insets/2)); $self->draw_reticencia($area, +$gc, $x+2, $y); } else { # element reticencia $x = $box_center_x - (int($col +lb_w / 2)+int($insets/2)); $y = $box_center_y - (int(RETI +CENCIAS_HEIGHT / 2)+int($insets/2)); $self->draw_reticencia($area, +$gc, $x, $y); } } } } $self->queue_draw(); } sub draw_center_element { my ($self, $area, $gc) = @_; my $canvas_width = $self->allocation->width; my $canvas_height = $self->allocation->height; my $pangoc = $self->get_pango_context(); my $fontdesc = Gtk2::Pango::FontDescription->from_string("Sans + 10"); my $xmlthink_center = $self->{matrix}{0}{0}; my $center_x = int($canvas_width/2); my $center_y = int($canvas_height/2); my $fullb_w = $self->{full_box_width}; my $fullb_h = $self->{full_box_height}; my $insets = $self->{insets}; my $x = $center_x - int($fullb_w/2) - int($insets/2); my $y = $center_y - int($fullb_h/2) - int($insets/2); # BIG RECTANGLE $area->draw_rectangle($gc, 0, $x, $y, $fullb_w, $fullb_h); # TAG my $tag_rect = Gtk2::Gdk::Rectangle->new($x+2, $y+2, $fullb_w- +4, 20); $area->draw_rectangle($gc, 0, $tag_rect->x, $tag_rect->y, $tag +_rect->width, $tag_rect->height); $self->draw_text($gc, $xmlthink_center->getTagName(), $tag_rec +t->x+2, $tag_rect->y+2, $tag_rect->width-4, $tag_rect->height-4); # ATTRIBUTES my $attr_rect = Gtk2::Gdk::Rectangle->new($x+2, $y+24, $fullb_ +w-4, int(($fullb_h-32)/2)); $area->draw_rectangle($gc, 0, $attr_rect->x, $attr_rect->y, $a +ttr_rect->width, $attr_rect->height); $self->draw_text($gc, $self->make_text_from_attributes($xmlthi +nk_center, "\n"), $attr_rect->x+2, $attr_rect->y+2, $attr_rect->width +-4, $attr_rect->height-4); # CDATA my $cdata_rect = Gtk2::Gdk::Rectangle->new($x+2, $y+24+int(($f +ullb_h-32)/2)+2, $fullb_w-4, int(($fullb_h-23)/2)); $area->draw_rectangle($gc, 0, $cdata_rect->x, $cdata_rect->y, +$cdata_rect->width, $cdata_rect->height); $self->draw_text($gc, $self->make_text_from_cdata($xmlthink_ce +nter), $cdata_rect->x+2, $cdata_rect->y+2, $cdata_rect->width-4, $cda +ta_rect->height-4); } sub make_text_from_attributes { my ($draw, $element, $sep) = @_; my $str = ''; my $nmap = $element->getAttributes(); for my $i (0..($nmap->getLength() - 1)) { my $node = $nmap->item($i); $str .= $node->getNodeName()."=".$node->getNodeValue() +.$sep; } chop($str); return $str; } sub make_text_from_cdata { my ($draw, $element) = @_; my $str = ''; for my $child ($element->getChildNodes()) { if ($child->getNodeType() != XML::DOM::ELEMENT_NODE && $child->getNodeType() != XML::DOM::ATTRIBUTE_NODE) + { my $o = $child->getNodeValue(); $o =~ s/^\s*//; $o =~ s/\s*$//; $str .= $o."\n" if $o; } } return $str; } sub draw_reticencia { my ($draw, $area, $gc, $x, $y) = @_; $area->draw_arc($gc, 0, $x, $y, (RETICENCIAS_WIDTH / 3), RETIC +ENCIAS_HEIGHT, 90*64, 180*64); $area->draw_arc($gc, 0, $x +(2*RETICENCIAS_WIDTH / 3) - 1, $y, + (RETICENCIAS_WIDTH / 3), RETICENCIAS_HEIGHT, 270*64, 180*64); $area->draw_line($gc, $x+(2*RETICENCIAS_WIDTH/6), $y+(RETICENC +IAS_HEIGHT/2), $x+(4*RETICENCIAS_WIDTH/6), $y+(RETICENCIAS_HEIGHT/2)) +; $area->draw_line($gc, $x+(RETICENCIAS_WIDTH/2), $y, $x+(RETICE +NCIAS_WIDTH/2), $y+(RETICENCIAS_HEIGHT)); } sub draw_collapsed_element { my ($draw, $area, $gc, $element, $x, $y) = @_; my $collb_w = $draw->{collapsed_box_width}; my $collb_h = $draw->{collapsed_box_height}; my $tag = $element->getTagName(); my $attr = $draw->make_text_from_attributes($element, ", "); my $cdata = $draw->make_text_from_cdata($element); $draw->{pixmap}->draw_rectangle($gc, 0, $x, $y, $collb_w, $col +lb_h); $draw->draw_text($gc, $tag, $x+2, $y, $collb_w-4, int($collb_h +/3)); $draw->draw_text($gc, $attr, $x+2, $y+int($collb_h/3), $collb_ +w-4, int($collb_h/3)); $draw->draw_text($gc, $cdata, $x+2, $y+2*int($collb_h/3), $col +lb_w-4, int($collb_h/3)); } sub draw_text { my ($draw, $gc, $text, $x, $y, $w, $h) = @_; my $pangoc = $draw->get_pango_context(); my $fontdesc = Gtk2::Pango::FontDescription->from_string("Sans + 10"); my $rect = Gtk2::Gdk::Rectangle->new($x, $y, $w, $h); my $layout = Gtk2::Pango::Layout->new($pangoc); my $clipped = Gtk2::Gdk::GC->new($draw->{pixmap}); $clipped->set_clip_rectangle($rect); $layout->set_font_description($fontdesc); $layout->set_text($text); $draw->{pixmap}->draw_layout($clipped, $x, $y, $layout); } sub draw_line_to_parent { my ($self, $area, $gc, $x, $y) = @_; my $canvas_width = $self->allocation->width; my $canvas_height = $self->allocation->height; $area->draw_line($gc, $x, $y, $x-int($self->{insets}/2), $y); $area->draw_line($gc, $x-int($self->{insets}/2), $y, $x-int($s +elf->{insets}/2), int($canvas_height/2)-int($self->{insets}/2)); $area->draw_line($gc, $x-int($self->{insets}/2), int($canvas_h +eight/2)-int($self->{insets}/2), $x-$self->{insets}, int($canvas_heig +ht/2)-int($self->{insets}/2)); } sub scroll_event { my ($self, $event) = @_; my $x = $event->x; my $y = $event->y; my $dir = $event->direction; my ($col, $row) = $self->x_y_to_col_row($x, $y); if ($col > 0) { my $other = $dir eq 'up'?-1:1; if (exists $self->{matrix}{$col}{$other} && $self->{ma +trix}{$col}{$other}) { $self->{matrix}{$col}{0} = $self->{matrix}{$co +l}{$other}; while (exists $self->{matrix}{$col + 1}) { delete $self->{matrix}{$col + 1}; $col++; } $self->plan_matrix(); } } } sub element_on_col_row { my ($self, $col, $row) = @_; return $self->{matrix}{$col}{$row}; } sub x_y_to_col_row { my ($self, $x, $y) = @_; my ($col, $row); my $canvas_width = $self->allocation->width; my $canvas_height = $self->allocation->height; my $center_x = int($canvas_width/2); my $center_y = int($canvas_height/2); my $fullb_w = $self->{full_box_width}; my $fullb_h = $self->{full_box_height}; my $collb_w = $self->{collapsed_box_width}; my $collb_h = $self->{collapsed_box_height}; my $insets = $self->{insets}; if ($x > ($center_x + int($fullb_w/2))) { my $rel_x = $x - ($center_x + int($fullb_w/2)); $col = int($rel_x/($collb_w+$insets)) + 1; my $rel_y = $y - $center_y; $row = ceil($rel_y/($collb_h+$insets))+0; } elsif ($x < ($center_x - int($fullb_w/2))) { my $rel_x = $x - ($center_x - int($fullb_w/2)); $col = int($rel_x/($collb_w+$insets)) - 1; my $rel_y = $y - $center_y; $row = ceil($rel_y/($collb_h+$insets)); } else { $col = 0; if ($y > ($center_y + int($fullb_h/2))) { my $rel_y = $y - ($center_y + int($fullb_h/2)) +; $row = int($rel_y/($collb_h+$insets)) + 1; } elsif ($y < ($center_y - int($fullb_h/2))) { my $rel_y = $y - ($center_y - int($fullb_h/2) +- int($insets/2)); $row = int($rel_y/($collb_h+$insets)) - 1; } } return ($col, $row); } sub button_press_event { my ($draw, $event) = @_; my $x = $event->x; my $y = $event->y; my $button = $event->button; my ($col, $row) = $draw->x_y_to_col_row($x, $y); $draw->click_col_row($button, $col, $row); } sub click_col_row { my ($self, $button, $col, $row) = @_; $col ||= 0; $row ||= 0; if (exists $self->{matrix}{$col}{$row} and $self->{matrix}{$co +l}{$row}) { my $obj = $self->{matrix}{$col}{$row}; if ($button == 1) { $self->{matrix} = {}; $self->{matrix}{0}{0} = $obj; $self->plan_matrix(); } } } 1;

Trial Program (with the patch from zentara's comment below):

#!/usr/bin/perl use strict; use warnings; use XML::DOM; use Gtk2 qw(-init); use XMLNavigator; my $mainwindow = Gtk2::Window->new("toplevel"); my $hbox = Gtk2::HBox->new(); my $parser = XML::DOM::Parser->new(); my $nav = XMLNavigator->new(domdocument => $parser->parsefile($ARGV[0] +)); $hbox->add($nav); $mainwindow->add($hbox); $mainwindow->signal_connect( destroy => sub { Gtk2->main_quit; } ); $mainwindow->set_size_request(500, 500); $mainwindow->show_all(); Gtk2->main();
daniel

Replies are listed 'Best First'.
Re: XML Navigator
by zentara (Archbishop) on Sep 27, 2006 at 18:12 UTC
    Cool package.

    Just a minor nit, your test window opens too small, and it dosn't close with WindowManager controls. Add

    $mainwindow->signal_connect( destroy => sub { Gtk2->main_quit; } ); $mainwindow->set_size_request(500,500);

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum

      Thanks, the patch is applied :)

      daniel
Re: XML Navigator
by ForgotPasswordAgain (Priest) on Sep 28, 2006 at 16:06 UTC

    Neat.

    I think you could replace:

    if ($pspec->get_name eq 'collapsed_box_height') { return $self->{collapsed_box_height}; } elsif ($pspec->get_name eq 'collapsed_box_width') { return $self->{collapsed_box_width}; } elsif ($pspec->get_name eq 'full_box_height') { return $self->{full_box_height}; } elsif ($pspec->get_name eq 'full_box_width') { return $self->{full_box_width}; } elsif ($pspec->get_name eq 'insets') { return $self->{insets}; } elsif ($pspec->get_name eq 'domdocument') { return $self->{domdocument}; } elsif ($pspec->get_name eq 'matrix') { return $self->{matrix}; } elsif ($pspec->get_name eq 'max_hori_distance') { return $self->{max_hori_distance}; } elsif ($pspec->get_name eq 'max_vert_distance') { return $self->{max_vert_distance}; }

    with something like:

    my %props = map { $_->{name} => 1 } $self->list_properties; return $self->{$pspec->get_name} if exists $props{$pspec->get_name};

    or even simply:

    return $self->{$pspec->get_name} if exists $self->{$pspec->get_name};

    Most of SET_PROPERTY is like that too. And

    my $draw = shift; my $gc = shift; my $text = shift; my $x = shift; my $y = shift; my $w = shift; my $h = shift;

    Yuck.

    my ($draw, $gc, $text, $x, $y, $w, $h) = @_;

    or

    my %args; @args{qw(draw gc text x y w h)} = @_;
      Also, check out Perl6::Attributes. Saves a LOT of typing, and makes your code easier to read and maintain, IMHO.

      $self->{collapsed_box_height} becomes $.collapsed_box_height

      which, when faced with as many $self references as you have, will prevent eye bleeding and carpal tunnel. And give Perl haters less to moan about.

Re: XML Navigator
by wazoox (Prior) on Sep 27, 2006 at 20:08 UTC
    Wow, that rocks! Thank you for this, both cool toy and useful tool :)

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: CUFP [id://575194]
Approved by Hue-Bond
Front-paged by grinder
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (5)
As of 2020-11-29 20:09 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?