#!/usr/bin/perl # Contest UI for the MO Submitter # (c) 2007 Martin Mares use strict; use warnings; BEGIN { defined $ENV{"MO_ROOT"} or die "Please set MO_ROOT to the contest root directory first.\n"; } use lib $ENV{"MO_ROOT"} . "/submit"; use lib $ENV{"MO_ROOT"} . "/submit/lib/perl5"; use MO::Submit; use Sherlock::Object; use Gtk2 -init; my $conn = new MO::Submit; ### GUI INITIALIZATION ### sub init_refresh(); sub timed_refresh(); sub refresh($); my $busy_cursor = Gtk2::Gdk::Cursor->new('watch'); # The main window my $window = Gtk2::Window->new('toplevel'); $window->signal_connect("delete-event" => sub { Gtk2->main_quit }); $window->set_title($conn->{"Contest"} . " Submitter"); $window->set_wmclass("submitter", "Submitter"); $window->set_default_size(320, 400); # The title label my $title_lab = Gtk2::Label->new; $title_lab->set_markup("" . $conn->{"Contest"} . ""); # The row of buttons my $b_submit = Gtk2::Button->new('Submit'); $b_submit->signal_connect(clicked => sub { Gtk2->main_quit }); $b_submit->set_sensitive(0); my $b_check = Gtk2::Button->new('Check'); $b_check->signal_connect(clicked => sub { Gtk2->main_quit }); $b_check->set_sensitive(0); my $b_refresh = Gtk2::Button->new('Refresh'); $b_refresh->signal_connect(clicked => sub { refresh(0) }); my $button_box = Gtk2::HBox->new; $button_box->pack_start_defaults($b_submit); $button_box->pack_start_defaults($b_check); $button_box->pack_start_defaults($b_refresh); $button_box->set_border_width(5); # The list of tasks my $task_store = Gtk2::ListStore->new('Glib::String', 'Glib::String', 'Glib::String'); my $task_view = Gtk2::TreeView->new($task_store); my $task_renderer = Gtk2::CellRendererText->new; my $task_col1 = Gtk2::TreeViewColumn->new_with_attributes("Task", $task_renderer, "text", 0); $task_view->append_column($task_col1); my $task_col2 = Gtk2::TreeViewColumn->new_with_attributes("Part", $task_renderer, "text", 1); $task_view->append_column($task_col2); my $task_col3 = Gtk2::TreeViewColumn->new_with_attributes("Status", $task_renderer, "text", 2); $task_view->append_column($task_col3); $task_view->set_headers_visible(0); my $task_scroll = Gtk2::ScrolledWindow->new; $task_scroll->set_policy("automatic", "automatic"); $task_scroll->add($task_view); $task_scroll->set_border_width(5); my $task_frame = Gtk2::Frame->new("Tasks"); $task_frame->add($task_scroll); my $selected_task; my $task_sel = $task_view->get_selection; $task_sel->set_mode('single'); $task_sel->signal_connect(changed => sub { my $iter = $_[0]->get_selected; if ($iter) { $selected_task = $task_store->get($iter, 0); print "Selected $selected_task\n"; $b_submit->set_sensitive(1); $b_check->set_sensitive(1); } else { $selected_task = undef; print "Deselected task\n"; $b_submit->set_sensitive(0); $b_check->set_sensitive(0); } }); my $status_bar = Gtk2::Statusbar->new; my $bar_ctx = $status_bar->get_context_id('xyzzy'); my $vbox = Gtk2::VBox->new; $vbox->pack_start($title_lab, 0, 0, 10); $vbox->pack_start($task_frame, 1, 1, 0); $vbox->pack_start($button_box, 0, 0, 0); $vbox->pack_start($status_bar, 0, 0, 0); $window->add($vbox); $window->signal_connect("expose-event" => sub { init_refresh(); return 0; }); $window->show_all; Glib::Timeout->add(5000, \&timed_refresh); Gtk2->main; exit 0; ### ACTIONS ### my $last_status_id; sub status($) { print "GUI: ", $_[0], "\n" if $conn->{"Trace"}; defined $last_status_id and $status_bar->remove($bar_ctx, $last_status_id); $last_status_id = $status_bar->push($bar_ctx, shift @_); } sub busy($) { status($_[0]); $window->window->set_cursor($busy_cursor); $window->Gtk2::Gdk::flush; } sub ready($) { status($_[0]); $window->window->set_cursor(undef); $window->Gtk2::Gdk::flush; } my $window_inited = 0; sub init_refresh() { if (!$window_inited) { refresh(1); $window_inited = 1; } return 1; } sub timed_refresh() { refresh(0); # FIXME: If-modified-since version? return 1; # We wish to re-run the timer } my $task_status_object; my $task_parts = {}; sub recalc_task_list() { $task_parts = {}; $task_store->clear; foreach my $t ($task_status_object->getarray("(T")) { my $task = $t->get("T"); foreach my $p ($t->getarray("(P")) { my $part = $p->get("P"); $task_parts->{$task}->{$part} = $p; my $status = "---"; my $iter = $task_store->append; $task_store->set($iter, 0, $task, 1, ($task eq $part ? "" : $part), 2, $status); } } } sub refresh($) { my $force = shift @_; if (!$conn->is_connected || $force) { busy("Connecting to server..."); if ($conn->connect) { ready("Connected successfully"); } else { ready($conn->{"error"}); } } if ($conn->is_connected) { busy("Updating status..."); my $r = new Sherlock::Object("!" => "STATUS"); $r = $conn->request($r); if (!defined $r) { ready($conn->{"error"}); } elsif ($r->get("-")) { ready($r->get("-")); } else { $task_status_object = $r; recalc_task_list; ready("Ready"); } } if (!$conn->is_connected && !$force) { # Retry $conn->log("Retrying"); refresh(1); } }