Commit | Line | Data |
---|---|---|
9afca65d MG |
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 |