ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC/BindingEditor.pm
Revision: 1.4
Committed: Sun Jul 30 13:16:44 2006 UTC (17 years, 9 months ago) by root
Branch: MAIN
CVS Tags: rel-0_2
Changes since 1.3: +57 -57 lines
Log Message:
the big rename

File Contents

# Content
1 package CFPlus::BindingEditor;
2
3 use strict;
4
5 use CFPlus::UI;
6
7 our @ISA = CFPlus::UI::FancyFrame::;
8
9 my @ALLOWED_MODIFIER_KEYS = (
10 CFPlus::SDLK_LSHIFT,
11 CFPlus::SDLK_LCTRL ,
12 CFPlus::SDLK_LALT ,
13 CFPlus::SDLK_LMETA ,
14
15 CFPlus::SDLK_RSHIFT,
16 CFPlus::SDLK_RCTRL ,
17 CFPlus::SDLK_RALT ,
18 CFPlus::SDLK_RMETA ,
19 );
20
21 my %ALLOWED_MODIFIERS = (
22 CFPlus::KMOD_LSHIFT => "LSHIFT",
23 CFPlus::KMOD_LCTRL => "LCTRL",
24 CFPlus::KMOD_LALT => "LALT",
25 CFPlus::KMOD_LMETA => "LMETA",
26
27 CFPlus::KMOD_RSHIFT => "RSHIFT",
28 CFPlus::KMOD_RCTRL => "RCTRL",
29 CFPlus::KMOD_RALT => "RALT",
30 CFPlus::KMOD_RMETA => "RMETA",
31 );
32
33 my %DIRECT_BIND_CHARS = map { $_ => 1 } qw/0 1 2 3 4 5 6 7 8 9/;
34 my @DIRECT_BIND_KEYS = (
35 CFPlus::SDLK_F1,
36 CFPlus::SDLK_F2,
37 CFPlus::SDLK_F3,
38 CFPlus::SDLK_F4,
39 CFPlus::SDLK_F5,
40 CFPlus::SDLK_F6,
41 CFPlus::SDLK_F7,
42 CFPlus::SDLK_F8,
43 CFPlus::SDLK_F9,
44 CFPlus::SDLK_F10,
45 CFPlus::SDLK_F11,
46 CFPlus::SDLK_F12,
47 CFPlus::SDLK_F13,
48 CFPlus::SDLK_F14,
49 CFPlus::SDLK_F15,
50 );
51
52 sub keycombo_to_name {
53 my ($mod, $sym) = @_;
54
55 my $mods = join '+',
56 map { $ALLOWED_MODIFIERS{$_} }
57 grep { ($_ + 0) & ($mod + 0) }
58 keys %ALLOWED_MODIFIERS;
59 $mods .= "+" if $mods ne '';
60
61 return $mods . CFPlus::SDL_GetKeyName ($sym);
62 }
63
64 sub new {
65 my $class = shift;
66
67 my $self = $class->SUPER::new (
68 binding => [],
69 commands => [],
70 title => "Macro/Keybinding Recorder",
71 @_
72 );
73
74 $self->add (my $vb = new CFPlus::UI::VBox);
75
76 $vb->add ($self->{rec_btn} = new CFPlus::UI::Button
77 text => "start recording",
78 tooltip => "Start/Stops recording of actions."
79 ."All subsequent actions after the recording started will be captured."
80 ."The actions are displayed after the record was stopped."
81 ."To bind the action you have to click on the 'Bind' button",
82 on_activate => sub {
83 unless ($self->{recording}) {
84 $self->start;
85 } else {
86 $self->stop;
87 }
88 });
89
90 $vb->add (new CFPlus::UI::Label text => "Actions:");
91 $vb->add ($self->{cmdbox} = new CFPlus::UI::VBox);
92
93 $vb->add (new CFPlus::UI::Label text => "Bound to: ");
94 $vb->add (my $hb = new CFPlus::UI::HBox);
95 $hb->add ($self->{keylbl} = new CFPlus::UI::Label expand => 1);
96 $hb->add (new CFPlus::UI::Button
97 text => "bind",
98 tooltip => "This opens a query where you have to press the key combination to bind the recorded actions",
99 on_activate => sub {
100 $self->ask_for_bind;
101 });
102
103 $vb->add (my $hb = new CFPlus::UI::HBox);
104 $hb->add (new CFPlus::UI::Button
105 text => "OK",
106 expand => 1,
107 tooltip => "This closes the binding editor and saves the binding",
108 on_activate => sub {
109 (delete $self->{binder})->destroy if $self->{binder};
110 $self->hide;
111 $self->commit;
112 0
113 });
114
115 $hb->add (new CFPlus::UI::Button
116 text => "Cancel",
117 expand => 1,
118 tooltip => "This closes the binding editor without saving",
119 on_activate => sub {
120 (delete $self->{binder})->destroy if $self->{binder};
121 $self->hide;
122 $self->{binding_cancel}->()
123 if $self->{binding_cancel};
124 0
125 });
126
127 $self->update_binding_widgets;
128
129 $self
130 }
131
132 sub cfg_bind {
133 my ($self, $mod, $sym, $cmds) = @_;
134 $::CFG->{profile}{default}{bindings}{$mod}{$sym} = $cmds;
135 ::update_bindings ();
136 }
137
138 sub cfg_unbind {
139 my ($self, $mod, $sym, $cmds) = @_;
140 delete $::CFG->{profile}{default}{bindings}{$mod}{$sym};
141 ::update_bindings ();
142 }
143
144 sub commit {
145 my ($self) = @_;
146
147 my ($mod, $sym, $cmds) = $self->get_binding;
148
149 if ($sym != 0 && @$cmds > 0) {
150 $::STATUSBOX->add ("Bound actions to <i>" . keycombo_to_name ($mod, $sym) . "</i>. "
151 . "Do not forget to 'Save Config'!");
152 $self->{binding_change}->($mod, $sym, $cmds)
153 if $self->{binding_change};
154 } else {
155 $::STATUSBOX->add ("No action bound, no key or action specified!");
156 $self->{binding_cancel}->()
157 if $self->{binding_cancel};
158 }
159 }
160
161 sub start {
162 my ($self) = @_;
163
164 $self->{rec_btn}->set_text ("stop recording");
165 $self->{recording} = 1;
166 $self->clear_command_list;
167 $::CONN->start_record if $::CONN;
168 }
169
170 sub stop {
171 my ($self) = @_;
172
173 $self->{rec_btn}->set_text ("start recording");
174 $self->{recording} = 0;
175
176 my $rec;
177 $rec = $::CONN->stop_record if $::CONN;
178 return unless ref $rec eq 'ARRAY';
179 $self->set_command_list ($rec);
180 }
181
182 sub ask_for_bind {
183 my ($self, $commit, $end_cb) = @_;
184
185 return if $self->{binder};
186
187 Scalar::Util::weaken $self;
188
189 $self->{binder} = new CFPlus::UI::FancyFrame
190 title => "Bind Action",
191 x => "center",
192 y => "center",
193 z => 1000,
194 has_close_button => 1,
195 on_delete => sub {
196 (delete $self->{binder})->destroy;
197 1
198 },
199 ;
200
201 $self->{binder}->add (my $vb = new CFPlus::UI::VBox);
202 $vb->add (new CFPlus::UI::Label
203 text => "Press a modifier (CTRL, ALT and/or SHIFT) and a key."
204 . "You can only bind 0-9 and F1-F15 without modifiers."
205 );
206 $vb->add (my $entry = new CFPlus::UI::Entry
207 text => "",
208 on_key_down => sub {
209 my ($entry, $ev) = @_;
210
211 my $mod = $ev->{mod};
212 my $sym = $ev->{sym};
213
214 # XXX: This seems a little bit hackisch to me, but I have to ignore them
215 return if grep { $_ == $sym } @ALLOWED_MODIFIER_KEYS;
216
217 if ($mod == CFPlus::KMOD_NONE
218 and not $DIRECT_BIND_CHARS{chr ($ev->{unicode})}
219 and not grep { $sym == $_ } @DIRECT_BIND_KEYS)
220 {
221 $::STATUSBOX->add (
222 "Cannot bind key " . CFPlus::SDL_GetKeyName ($sym) . " directly without modifier, "
223 . "as those keys are reserved for the command completer."
224 );
225 return;
226 }
227
228 $entry->grab_focus;
229
230 $self->{binding} = [$mod, $sym];
231 $self->update_binding_widgets;
232 $self->commit if $commit;
233 $end_cb->() if $end_cb;
234
235 (delete $self->{binder})->destroy;
236 1
237 },
238 on_focus_out => sub {
239 # segfaults and worse :()
240 #(delete $self->{binder})->destroy if $self->{binder};
241 1
242 },
243 );
244
245 $entry->grab_focus;
246 $self->{binder}->show;
247 }
248
249 # $mod and $sym are the modifiers and key symbol
250 # $cmds is a array ref of strings (the commands)
251 # $cb is the callback that is executed on OK
252 # $ccb is the callback that is executed on CANCEL and
253 # when the binding was unsuccessful on OK
254 sub set_binding {
255 my ($self, $mod, $sym, $cmds, $cb, $ccb) = @_;
256
257 $self->clear_command_list;
258 $self->{recording} = 0;
259 $self->{rec_btn}->set_text ("start recording");
260
261 $self->{binding} = [$mod, $sym];
262 $self->{commands} = $cmds;
263
264 $self->{binding_change} = $cb;
265 $self->{binding_cancel} = $ccb;
266
267 $self->update_binding_widgets;
268 }
269
270 # this is a shortcut method that asks for a binding
271 # and then just binds it.
272 sub do_quick_binding {
273 my ($self, $cmds, $end_cb) = @_;
274 $self->set_binding (undef, undef, $cmds, sub { $self->cfg_bind (@_) });
275 $self->ask_for_bind (1, $end_cb);
276 }
277
278 sub update_binding_widgets {
279 my ($self) = @_;
280 my ($mod, $sym, $cmds) = $self->get_binding;
281 $self->{keylbl}->set_text (keycombo_to_name ($mod, $sym));
282 $self->set_command_list ($cmds);
283 }
284
285 sub get_binding {
286 my ($self) = @_;
287 return (
288 $self->{binding}->[0],
289 $self->{binding}->[1],
290 [ grep { defined $_ } @{$self->{commands}} ]
291 );
292 }
293
294 sub clear_command_list {
295 my ($self) = @_;
296 $self->{cmdbox}->clear ();
297 }
298
299 sub set_command_list {
300 my ($self, $cmds) = @_;
301
302 $self->{cmdbox}->clear ();
303 $self->{commands} = $cmds;
304
305 my $idx = 0;
306
307 for (@$cmds) {
308 $self->{cmdbox}->add (my $hb = new CFPlus::UI::HBox);
309
310 my $i = $idx;
311 $hb->add (new CFPlus::UI::Label text => $_);
312 $hb->add (new CFPlus::UI::Button
313 text => "delete",
314 tooltip => "Deletes the action from the record",
315 on_activate => sub {
316 $self->{cmdbox}->remove ($hb);
317 $cmds->[$i] = undef;
318 });
319
320
321 $idx++
322 }
323 }
324
325 1