2 # Contest UI for the MO Submitter
3 # (c) 2007 Martin Mares <mj@ucw.cz>
9 defined $ENV{"MO_ROOT"} or die "Please set MO_ROOT to the contest root directory first.\n";
11 use lib $ENV{"MO_ROOT"} . "/submit";
12 use lib $ENV{"MO_ROOT"} . "/submit/lib/perl5";
19 my $conn = new MO::Submit;
21 ### GUI INITIALIZATION ###
28 my $busy_cursor = Gtk2::Gdk::Cursor->new('watch');
31 my $window = Gtk2::Window->new('toplevel');
32 $window->signal_connect("delete-event" => sub { Gtk2->main_quit });
33 $window->set_title($conn->{"Contest"} . " Submitter");
34 $window->set_wmclass("submitter", "Submitter");
35 $window->set_default_size(640, 480);
38 my $title_lab = Gtk2::Label->new;
39 $title_lab->set_markup("<span size='x-large'>" . $conn->{"Contest"} . "</span>");
42 my $b_submit = Gtk2::Button->new('Submit');
43 $b_submit->signal_connect(clicked => \&submit);
44 $b_submit->set_sensitive(0);
46 my $b_check = Gtk2::Button->new('Check');
47 $b_check->signal_connect(clicked => sub { Gtk2->main_quit });
48 $b_check->set_sensitive(0);
50 my $b_refresh = Gtk2::Button->new('Refresh');
51 $b_refresh->signal_connect(clicked => sub { refresh(0) });
53 my $button_box = Gtk2::HBox->new;
54 $button_box->pack_start_defaults($b_submit);
55 $button_box->pack_start_defaults($b_check);
56 $button_box->pack_start_defaults($b_refresh);
57 $button_box->set_border_width(5);
60 my $task_store = Gtk2::ListStore->new('Glib::String', 'Glib::String');
62 my $task_view = Gtk2::TreeView->new($task_store);
63 my $task_renderer = Gtk2::CellRendererText->new;
64 my $task_col1 = Gtk2::TreeViewColumn->new_with_attributes("Task", $task_renderer, "text", 0);
65 $task_view->append_column($task_col1);
66 my $task_col2 = Gtk2::TreeViewColumn->new_with_attributes("Status", $task_renderer, "text", 1);
67 $task_view->append_column($task_col2);
68 $task_view->set_headers_visible(0);
70 my $task_scroll = Gtk2::ScrolledWindow->new;
71 $task_scroll->set_policy("automatic", "automatic");
72 $task_scroll->add($task_view);
73 $task_scroll->set_border_width(5);
75 my $task_frame = Gtk2::Frame->new("Tasks");
76 $task_frame->add($task_scroll);
79 my $task_sel = $task_view->get_selection;
80 $task_sel->set_mode('single');
81 $task_sel->signal_connect(changed => sub {
82 my $iter = $_[0]->get_selected;
84 $selected_task = $task_store->get($iter, 0);
85 $b_submit->set_sensitive(1);
86 $b_check->set_sensitive(1);
88 $selected_task = undef;
89 print "Deselected task\n";
90 $b_submit->set_sensitive(0);
91 $b_check->set_sensitive(0);
95 my $status_bar = Gtk2::Statusbar->new;
96 my $bar_ctx = $status_bar->get_context_id('xyzzy');
98 my $vbox = Gtk2::VBox->new;
99 $vbox->pack_start($title_lab, 0, 0, 10);
100 $vbox->pack_start($task_frame, 1, 1, 0);
101 $vbox->pack_start($button_box, 0, 0, 0);
102 $vbox->pack_start($status_bar, 0, 0, 0);
105 $window->signal_connect("expose-event" => sub { init_refresh(); return 0; });
113 print "GUI: ", $_[0], "\n" if $conn->{"Trace"};
114 defined $last_status_id and $status_bar->remove($bar_ctx, $last_status_id);
115 $last_status_id = $status_bar->push($bar_ctx, shift @_);
120 $window->window->set_cursor($busy_cursor);
121 $window->Gtk2::Gdk::flush;
126 $window->window->set_cursor(undef);
127 $window->Gtk2::Gdk::flush;
130 my $window_inited = 0;
133 if (!$window_inited) {
142 refresh(0); # FIXME: If-modified-since version?
143 return 1; # We wish to re-run the timer
146 my $task_status_object;
150 sub recalc_task_list() {
153 foreach my $t ($task_status_object->getarray("(T")) {
154 my $task = $t->get("T");
155 foreach my $p ($t->getarray("(P")) {
156 my $part = $p->get("P");
157 my $taskpart = ($task eq $part) ? $task : "$task/$part";
158 push @new_tp, $taskpart;
160 my $current_ver = $p->get("V");
161 foreach my $v ($p->getarray("(V")) {
162 if ($v->get("V") == $current_ver) {
163 my $time = strftime("%H:%M:%S", localtime $v->get("T"));
165 "$part." . $v->get("X") . ", " .
166 $v->get("L") . " bytes, " .
167 $v->get("S") . " $time)";
171 push @new_stat, $status;
175 if (join("\n", @new_tp) ne join("\n", @task_parts)) {
176 # The tasks have changed, repopulate the whole structure
179 foreach my $taskpart (@new_tp) {
180 my $iter = $task_store->append;
181 $task_store->set($iter,
186 # Update the task status
189 $task_store->foreach(sub {
190 my ($obj, $path, $iter) = @_;
191 if ($s[0] ne $ns[0]) {
192 $task_store->set($iter, 1, $ns[0]);
200 @task_parts = @new_tp;
201 @task_stat = @new_stat;
206 my $force = shift @_;
207 if (!$conn->is_connected || $force) {
208 busy("Connecting to server...");
209 if ($conn->connect) {
210 ready("Connected successfully");
212 ready($conn->{"error"});
215 if ($conn->is_connected) {
216 busy("Updating status...");
217 my $r = new Sherlock::Object("!" => "STATUS");
218 $r = $conn->request($r);
220 ready($conn->{"error"});
221 } elsif ($r->get("-")) {
224 $task_status_object = $r;
229 if (!$conn->is_connected && !$force) {
231 $conn->log("Retrying");
242 # $subwin->signal_emit("delete-event");
246 ## FIXME: Stop the status timer?
247 $subwin = Gtk2::Window->new('toplevel');
248 $subwin->set_modal(1);
249 $subwin->set_transient_for($window);
250 $subwin->set_destroy_with_parent(1);
251 $subwin->set_title("Submit task $selected_task");
252 $subwin->set_wmclass("submitter", "Submitter");
253 $subwin->signal_connect("delete-event" => sub { end_submit(0) });
255 my $bb_submit = Gtk2::Button->new('Submit');
256 $bb_submit->signal_connect(clicked => sub { end_submit(1) });
257 #$bb_submit->set_sensitive(0);
259 my $bb_cancel = Gtk2::Button->new('Cancel');
260 $bb_cancel->signal_connect(clicked => sub { end_submit(1) });
262 my $bbutton_box = Gtk2::HBox->new;
263 $bbutton_box->pack_start_defaults($bb_submit);
264 $bbutton_box->pack_start_defaults($bb_cancel);
265 $bbutton_box->set_border_width(5);
267 $subwin->add($bbutton_box);
270 #my $dialog = Gtk2::MessageDialog->new($window, [qw/modal destroy-with-parent/], 'question', 'ok-cancel', "So what?");
271 #$dialog->set_default_response("ok");
272 #$dialog->signal_connect (response => sub { $_[0]->destroy });
278 Glib::Timeout->add(5000, \&timed_refresh);