2010-08-20 21:33:18 +02:00
|
|
|
#!/usr/bin/env perl
|
|
|
|
# vim:ts=4:sw=4:expandtab
|
|
|
|
# renders the layout tree using asymptote
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
|
|
|
|
use JSON::XS;
|
|
|
|
use Data::Dumper;
|
|
|
|
use AnyEvent::I3;
|
|
|
|
use v5.10;
|
|
|
|
|
|
|
|
use Gtk2 '-init';
|
|
|
|
use Gtk2::SimpleMenu;
|
2010-08-21 18:34:51 +02:00
|
|
|
use Glib qw/TRUE FALSE/;
|
2010-08-20 21:33:18 +02:00
|
|
|
|
|
|
|
my $window = Gtk2::Window->new('toplevel');
|
|
|
|
$window->signal_connect('delete_event' => sub { Gtk2->main_quit; });
|
|
|
|
|
|
|
|
my $tree_store = Gtk2::TreeStore->new(qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/);
|
|
|
|
|
2013-05-01 07:23:03 +02:00
|
|
|
my $i3 = i3();
|
2010-08-20 21:33:18 +02:00
|
|
|
|
|
|
|
my $tree_view = Gtk2::TreeView->new($tree_store);
|
|
|
|
|
|
|
|
my $layout_box = undef;
|
|
|
|
|
|
|
|
sub copy_node {
|
|
|
|
my ($n, $parent, $piter, $pbox) = @_;
|
|
|
|
|
|
|
|
my $o = ($n->{orientation} == 0 ? "u" : ($n->{orientation} == 1 ? "h" : "v"));
|
|
|
|
my $w = (defined($n->{window}) ? $n->{window} : "N");
|
|
|
|
|
|
|
|
# convert a rectangle struct to X11 notation (WxH+X+Y)
|
|
|
|
my $r = $n->{rect};
|
|
|
|
my $x = $r->{x};
|
|
|
|
my $y = $r->{y};
|
|
|
|
my $dim = $r->{width}."x".$r->{height}.($x<0?$x:"+$x").($y<0?$y:"+$y");
|
|
|
|
|
|
|
|
# add node to the tree with all known properties
|
|
|
|
my $iter = $tree_store->append($piter);
|
|
|
|
$tree_store->set($iter, 0 => $n->{name}, 1 => $w, 2 => $o, 3 => sprintf("0x%08x", $n->{id}), 4 => $n->{urgent}, 5 => $n->{focused}, 6 => $n->{layout}, 7 => $dim);
|
|
|
|
|
|
|
|
# also create a box for the node, each node has a vbox
|
|
|
|
# for combining the title (and properties) with the
|
|
|
|
# container itself, the container will be empty in case
|
|
|
|
# of no children, a vbox or hbox
|
|
|
|
my $box;
|
|
|
|
if($n->{orientation} == 1) {
|
|
|
|
$box = Gtk2::HBox->new(1, 5);
|
|
|
|
} else {
|
|
|
|
$box = Gtk2::VBox->new(1, 5);
|
|
|
|
}
|
|
|
|
|
|
|
|
# combine label and container
|
|
|
|
my $node = Gtk2::Frame->new($n->{name}.",".$o.",".$w);
|
|
|
|
$node->set_shadow_type('etched-out');
|
|
|
|
$node->add($box);
|
|
|
|
|
|
|
|
# the parent is added onto a scrolled window, so add it with a viewport
|
|
|
|
if(defined($pbox)) {
|
|
|
|
$pbox->pack_start($node, 1, 1, 0);
|
|
|
|
} else {
|
|
|
|
$layout_box = $node;
|
|
|
|
}
|
|
|
|
|
|
|
|
# recurse into children
|
|
|
|
copy_node($_, $n, $iter, $box) for @{$n->{nodes}};
|
|
|
|
|
|
|
|
# if it is a window draw a nice color
|
|
|
|
if(defined($n->{window})) {
|
|
|
|
# use a drawing area to fill a colored rectangle
|
|
|
|
my $area = Gtk2::DrawingArea->new();
|
|
|
|
|
|
|
|
# the color is stored as hex in the name
|
|
|
|
$area->{"user-data"} = $n->{name};
|
|
|
|
|
|
|
|
$area->signal_connect(expose_event => sub {
|
|
|
|
my ($widget, $event) = @_;
|
|
|
|
|
|
|
|
# fetch a cairo context and it width/height to start drawing nodes
|
|
|
|
my $cr = Gtk2::Gdk::Cairo::Context->create($widget->window());
|
|
|
|
|
|
|
|
my $w = $widget->allocation->width;
|
|
|
|
my $h = $widget->allocation->height;
|
|
|
|
|
|
|
|
my $hc = $widget->{"user-data"};
|
|
|
|
my $r = hex(substr($hc, 1, 2)) / 255.0;
|
|
|
|
my $g = hex(substr($hc, 3, 2)) / 255.0;
|
|
|
|
my $b = hex(substr($hc, 5, 2)) / 255.0;
|
|
|
|
|
|
|
|
$cr->set_source_rgb($r, $g, $b);
|
|
|
|
$cr->rectangle(0, 0, $w, $h);
|
|
|
|
$cr->fill();
|
|
|
|
|
2010-08-21 18:34:51 +02:00
|
|
|
return FALSE;
|
2010-08-20 21:33:18 +02:00
|
|
|
});
|
|
|
|
|
2010-08-21 18:34:51 +02:00
|
|
|
$box->pack_end($area, 1, 1, 0);
|
2010-08-20 21:33:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Replaced by Gtk2 Boxes:
|
|
|
|
#sub draw_node {
|
|
|
|
# my ($n, $cr, $x, $y, $w, $h) = @_;
|
|
|
|
#
|
|
|
|
# $cr->set_source_rgb(1.0, 1.0, 1.0);
|
|
|
|
# $cr->rectangle($x, $y, $w/2, $h/2);
|
|
|
|
# $cr->fill();
|
|
|
|
#}
|
|
|
|
|
|
|
|
my $json_prev = "";
|
|
|
|
|
|
|
|
my $layout_sw = Gtk2::ScrolledWindow->new(undef, undef);
|
|
|
|
my $layout_container = Gtk2::HBox->new(0, 0);
|
|
|
|
$layout_sw->add_with_viewport($layout_container);
|
|
|
|
|
|
|
|
sub copy_tree {
|
2010-11-21 21:42:28 +01:00
|
|
|
my $tree = $i3->get_tree->recv;
|
2010-08-20 21:33:18 +02:00
|
|
|
|
|
|
|
# convert the tree back to json so we only rebuild/redraw when the tree is changed
|
|
|
|
my $json = encode_json($tree);
|
2010-08-21 18:34:51 +02:00
|
|
|
if ($json ne $json_prev) {
|
2010-08-20 21:33:18 +02:00
|
|
|
$json_prev = $json;
|
|
|
|
|
2010-08-21 18:34:51 +02:00
|
|
|
# rebuild the tree and the layout
|
2010-08-20 21:33:18 +02:00
|
|
|
$tree_store->clear();
|
2010-08-21 18:34:51 +02:00
|
|
|
if(defined($layout_box)) {
|
|
|
|
$layout_container->remove($layout_box);
|
|
|
|
}
|
2010-08-20 21:33:18 +02:00
|
|
|
copy_node($tree);
|
2010-08-21 18:34:51 +02:00
|
|
|
$layout_container->add($layout_box);
|
|
|
|
$layout_container->show_all();
|
2010-08-20 21:33:18 +02:00
|
|
|
|
|
|
|
# keep things expanded, otherwise the tree collapses every reload which is more annoying then this :-)
|
|
|
|
$tree_view->expand_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub new_column {
|
|
|
|
my $tree_column = Gtk2::TreeViewColumn->new();
|
|
|
|
$tree_column->set_title(shift);
|
|
|
|
|
|
|
|
my $renderer = Gtk2::CellRendererText->new();
|
|
|
|
$tree_column->pack_start($renderer, FALSE);
|
|
|
|
$tree_column->add_attribute($renderer, text => shift);
|
|
|
|
|
|
|
|
return($tree_column);
|
|
|
|
}
|
|
|
|
|
|
|
|
my $col = 0;
|
|
|
|
$tree_view->append_column(new_column("Name", $col++));
|
|
|
|
$tree_view->append_column(new_column("Window", $col++));
|
|
|
|
$tree_view->append_column(new_column("Orientation", $col++));
|
|
|
|
$tree_view->append_column(new_column("ID", $col++));
|
|
|
|
$tree_view->append_column(new_column("Urgent", $col++));
|
|
|
|
$tree_view->append_column(new_column("Focused", $col++));
|
|
|
|
$tree_view->append_column(new_column("Layout", $col++));
|
|
|
|
$tree_view->append_column(new_column("Rect", $col++));
|
|
|
|
|
|
|
|
$tree_view->set_grid_lines("both");
|
|
|
|
|
|
|
|
my $tree_sw = Gtk2::ScrolledWindow->new(undef, undef);
|
|
|
|
$tree_sw->add($tree_view);
|
|
|
|
|
|
|
|
# Replaced by Gtk2 Boxes:
|
|
|
|
#my $area = Gtk2::DrawingArea->new();
|
|
|
|
#$area->signal_connect(expose_event => sub {
|
|
|
|
# my ($widget, $event) = @_;
|
|
|
|
#
|
|
|
|
# # fetch a cairo context and it width/height to start drawing nodes
|
|
|
|
# my $cr = Gtk2::Gdk::Cairo::Context->create($widget->window());
|
|
|
|
#
|
|
|
|
# my $w = $widget->allocation->width;
|
|
|
|
# my $h = $widget->allocation->height;
|
|
|
|
#
|
|
|
|
# draw_node($gtree, $cr, 0, 0, $w, $h);
|
|
|
|
#
|
|
|
|
# return FALSE;
|
|
|
|
#});
|
|
|
|
|
|
|
|
sub menu_export {
|
|
|
|
print("TODO: EXPORT\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
my $menu_tree = [
|
|
|
|
_File => {
|
|
|
|
item_type => '<Branch>',
|
|
|
|
children => [
|
|
|
|
_Export => {
|
|
|
|
callback => \&menu_export,
|
|
|
|
accelerator => '<ctrl>E',
|
|
|
|
},
|
|
|
|
_Quit => {
|
|
|
|
callback => sub { Gtk2->main_quit; },
|
|
|
|
accelerator => '<ctrl>Q',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
my $menu = Gtk2::SimpleMenu->new(menu_tree => $menu_tree);
|
|
|
|
|
|
|
|
my $vbox = Gtk2::VBox->new(0, 0);
|
|
|
|
$vbox->pack_start($menu->{widget}, 0, 0, 0);
|
|
|
|
$vbox->pack_end($tree_sw, 1, 1, 0);
|
|
|
|
$vbox->pack_end($layout_sw, 1, 1, 0);
|
|
|
|
|
|
|
|
$window->add($vbox);
|
|
|
|
$window->show_all();
|
|
|
|
$window->set_size_request(500,500);
|
|
|
|
|
|
|
|
Glib::Timeout->add(1000, "copy_tree", undef, Glib::G_PRIORITY_DEFAULT);
|
|
|
|
copy_tree();
|
|
|
|
|
|
|
|
Gtk2->main();
|
|
|
|
|