Initial commit
[gruntmaster-gui.git] / lib / Gruntmaster / GUI.pm
1 package Gruntmaster::GUI;
2
3 use 5.014000;
4 use strict;
5 use warnings;
6 use parent qw/Wx::App/;
7 our $VERSION = '5999.000_001';
8 $VERSION = eval $VERSION;
9
10 use constant FORMATS => [qw/C CPP GO HASKELL JAVA PASCAL PERL PYTHON/];
11
12 use Gruntmaster::Data;
13 use Sub::Name qw/subname/;
14 use Try::Tiny;
15 use Wx qw/wxVERTICAL wxHORIZONTAL wxEXPAND wxALL wxTOP wxBOTTOM wxTE_MULTILINE wxTE_PROCESS_ENTER wxDefaultPosition wxDefaultSize wxLC_ICON/;
16 use Wx::Event qw/EVT_BUTTON EVT_COMBOBOX EVT_LISTBOX EVT_TEXT EVT_TEXT_ENTER/;
17
18 my ($db, $pb, $ct);
19 my ($problem_list, $contest_list, $job_list);
20 my (%problem, %contest);
21
22 sub problem_enable_relevant{
23 $problem{genformat}->Enable($problem{generator}->GetValue eq 'Run');
24 $problem{gensource}->Enable($problem{generator}->GetValue eq 'Run');
25 $problem{verformat}->Enable($problem{runner}->GetValue ne 'File');
26 $problem{versource}->Enable($problem{runner}->GetValue ne 'File');
27 $problem{tests}->Enable($problem{runner}->GetValue eq 'File');
28 }
29
30 sub select_problem {
31 my $id = $problem_list->GetClientData($problem_list->GetSelection);
32 $pb = $db->problem($id);
33 $problem{id}->SetLabel("ID: $id");
34 $problem{$_}->SetValue($pb->get_column($_) // '') for qw/name author writer owner tests timeout olimit level generator runner judge testcnt value statement genformat gensource verformat versource/;
35 $problem{private}->SetValue($pb->private);
36 problem_enable_relevant;
37 }
38
39 sub select_contest {
40 my $id = $contest_list->GetClientData($contest_list->GetSelection);
41 $ct = $db->contest($id);
42 $contest{id}->SetLabel("ID: $id");
43 $contest{$_}->SetValue($ct->get_column($_)) for qw/name start stop owner/;
44 }
45
46 sub connect_or_disconnect {
47 my ($dsn, $button, $nb) = @_;
48 subname 'connect_or_disconnect', sub {
49 if (defined $db) {
50 undef $db;
51 $dsn->Enable;
52 $nb->Disable;
53 $button->SetLabel('Connect');
54 $problem_list->Clear;
55 } else {
56 try {
57 $db = Gruntmaster::Data->connect($dsn->GetValue);
58 $db->problems->count({}); # Trigger database connection
59 } catch {
60 Wx::MessageBox("Cannot connect to database: $_");
61 undef $db;
62 };
63 return unless defined $db;
64 $dsn->Disable;
65 $nb->Enable;
66 $button->SetLabel('Disconnect');
67 $problem_list->Append($_->name, $_->id) for $db->problems->search({}, {order_by => 'name'});
68 $problem_list->GetParent->GetSizer->Layout;
69 $contest_list->Append($_->name, $_->id) for $db->contests->search({}, {order_by => 'name'});
70 $contest_list->GetParent->GetSizer->Layout;
71 $problem_list->SetSelection(0);
72 $contest_list->SetSelection(0);
73 select_problem;
74 select_contest;
75 }
76 $button->GetParent->GetSizer->Layout;
77 }
78 }
79
80 sub OnInit {
81 my $frame = Wx::Frame->new(undef, -1, 'Gruntmaster 6000', [-1, -1], [500, 700]);
82 my ($nb, $problems, $contests, $jobs);
83
84 {
85 my $panel = Wx::Panel->new($frame);
86 my $subpanel = Wx::Panel->new($panel);
87 my $panel_sizer = Wx::BoxSizer->new(wxVERTICAL);
88 my $subpanel_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
89
90 my $dsn = Wx::TextCtrl->new($subpanel, -1, '', wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
91 my $db_button = Wx::Button->new($subpanel, -1, 'Connect');
92 $nb = Wx::Notebook->new($panel);
93 $nb->Disable;
94 EVT_BUTTON($db_button, $db_button, connect_or_disconnect $dsn, $db_button, $nb);
95 EVT_TEXT_ENTER($dsn, $dsn, connect_or_disconnect $dsn, $db_button, $nb);
96
97 $panel_sizer->Add($subpanel, 0, wxEXPAND | wxALL, 10);
98 $panel_sizer->Add($nb, 1, wxEXPAND | wxALL, 10);
99 $subpanel_sizer->Add($dsn, 1, wxEXPAND | wxALL, 10);
100 $subpanel_sizer->Add($db_button, 0, wxEXPAND | wxALL, 10);
101 $panel->SetSizer($panel_sizer);
102 $subpanel->SetSizer($subpanel_sizer);
103 }
104
105 sub label {
106 my ($parent, $sizer, $content) = @_;
107 my $temp = Wx::StaticText->new($parent, -1, $content);
108 $sizer->Add($temp, wxEXPAND);
109 $temp
110 }
111 sub formsize {
112 my ($sizer, $item) = @_;
113 $sizer->Add($item, 0, wxEXPAND | wxTOP | wxBOTTOM, 5);
114 }
115
116 {
117 $problems = Wx::Panel->new($nb);
118 my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
119 $problem_list = Wx::ListBox->new($problems);
120 EVT_LISTBOX($problem_list, $problem_list, \&select_problem);
121 my $form1 = Wx::ScrolledWindow->new($problems);
122 my $form2 = Wx::ScrolledWindow->new($problems);
123 my $f1sizer = Wx::BoxSizer->new(wxVERTICAL);
124 my $f2sizer = Wx::BoxSizer->new(wxVERTICAL);
125 $problem{id} = label $form1, $f1sizer, 'ID';
126 my %id_to_label = (timeout => 'Time limit', olimit => 'Output limit');
127 for (qw/name author writer owner tests timeout olimit/) {
128 label $form1, $f1sizer, $id_to_label{$_} // ucfirst;
129 formsize $f1sizer, $problem{$_} = Wx::TextCtrl->new($form1, -1, '');
130 }
131 $problem{private} = Wx::CheckBox->new($form2, -1, 'Private');
132 $f2sizer->Add($problem{private});
133 label $form2, $f2sizer, 'Level';
134 formsize $f2sizer, $problem{level} = Wx::ComboBox->new($form2);
135 $problem{level}->Append([qw/beginner easy medium hard/]);
136 label $form2, $f2sizer, 'Generator';
137 formsize $f2sizer, $problem{generator} = Wx::ComboBox->new($form2);
138 $problem{generator}->Append([qw/File Run Undef/]);
139 label $form2, $f2sizer, 'Runner';
140 formsize $f2sizer, $problem{runner} = Wx::ComboBox->new($form2);
141 $problem{runner}->Append([qw/File Verifier Interactive/]);
142 label $form2, $f2sizer, 'Judge';
143 formsize $f2sizer, $problem{judge} = Wx::ComboBox->new($form2);
144 $problem{judge}->Append([qw/Absolute Points/]);
145 label $form2, $f2sizer, 'Test count';
146 formsize $f2sizer, $problem{testcnt} = Wx::SpinCtrl->new($form2);
147 label $form2, $f2sizer, 'Value';
148 formsize $f2sizer, $problem{value} = Wx::SpinCtrl->new($form2);
149 label $form1, $f1sizer, 'Statement';
150 formsize $f1sizer, $problem{statement} = Wx::TextCtrl->new($form1, -1, '', wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
151 #$f1sizer->Add($problem{statement}, 2, wxEXPAND | wxTOP | wxBOTTOM, 5);
152 label $form2, $f2sizer, 'Generator program';
153 formsize $f2sizer, $problem{genformat} = Wx::ComboBox->new($form2);
154 $problem{genformat}->Append(FORMATS);
155 formsize $f2sizer, $problem{gensource} = Wx::TextCtrl->new($form2, -1, '', wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
156 label $form2, $f2sizer, 'Verifier program';
157 formsize $f2sizer, $problem{verformat} = Wx::ComboBox->new($form2);
158 $problem{verformat}->Append(FORMATS);
159 formsize $f2sizer, $problem{versource} = Wx::TextCtrl->new($form2, -1, '', wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
160 EVT_COMBOBOX($problem{$_}, $problem{$_}, \&problem_enable_relevant) for qw/generator runner/;
161 EVT_TEXT($problem{$_}, $problem{$_}, \&problem_enable_relevant) for qw/generator runner/;
162
163 $sizer->Add($problem_list, 0, wxEXPAND | wxALL, 10);
164 $sizer->Add($form1, 1, wxALL, 10);
165 $sizer->Add($form2, 1, wxALL, 10);
166 $form1->SetSizer($f1sizer);
167 $form2->SetSizer($f2sizer);
168 $problems->SetSizer($sizer);
169 }
170
171 {
172 $contests = Wx::Panel->new($nb);
173 my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
174 $contest_list = Wx::ListBox->new($contests);
175 EVT_LISTBOX($contest_list, $contest_list, \&select_contest);
176 my $form = Wx::ScrolledWindow->new($contests);
177 my $fsizer = Wx::BoxSizer->new(wxVERTICAL);
178 $contest{id} = label $form, $fsizer, 'ID';
179 label $form, $fsizer, 'Name';
180 formsize $fsizer, $contest{name} = Wx::TextCtrl->new($form, -1, '');
181 label $form, $fsizer, 'Start';
182 formsize $fsizer, $contest{start} = Wx::TextCtrl->new($form, -1, '');
183 label $form, $fsizer, 'Stop';
184 formsize $fsizer, $contest{stop} = Wx::TextCtrl->new($form, -1, '');
185 label $form, $fsizer, 'owner';
186 formsize $fsizer, $contest{owner} = Wx::TextCtrl->new($form, -1, '');
187
188 $sizer->Add($contest_list, 0, wxEXPAND | wxALL, 10);
189 $sizer->Add($form, 1, wxALL, 10);
190 $form->SetSizer($fsizer);
191 $contests->SetSizer($sizer);
192 }
193
194 $nb->AddPage($problems, 'Problems');
195 $nb->AddPage($contests, 'Contests');
196 $frame->Show(1)
197 }
198
199 1;
200 __END__
201
202 =head1 NAME
203
204 Gruntmaster::GUI - Gruntmaster 6000 Online Judge -- GUI database editor
205
206 =head1 SYNOPSIS
207
208 use Gruntmaster::GUI;
209 Gruntmaster::GUI->new->MainLoop;
210
211 =head1 DESCRIPTION
212
213 Gruntmaster::GUI is a GUI viewer and editor for the Gruntmaster 6000 database.
214
215 CLI tools with the same purpose are distributed with Gruntmaster::Data. See L<gruntmaster-contest(1)>, L<gruntmaster-job(1)>, L<gruntmaster-problem(1)>.
216
217 =head1 AUTHOR
218
219 Marius Gavrilescu E<lt>marius@ieval.roE<gt>
220
221 =head1 COPYRIGHT AND LICENSE
222
223 Copyright (C) 2014 by Marius Gavrilescu
224
225 This library is free software; you can redistribute it and/or modify
226 it under the same terms as Perl itself, either Perl version 5.18.2 or,
227 at your option, any later version of Perl 5 you may have available.
228
229
230 =cut
This page took 0.03845 seconds and 4 git commands to generate.