ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC/BindingEditor.pm
Revision: 1.9
Committed: Sat Dec 9 02:21:25 2006 UTC (17 years, 5 months ago) by root
Branch: MAIN
CVS Tags: HEAD
Changes since 1.8: +0 -0 lines
State: FILE REMOVED
Log Message:
- slightly cleaned up and "outsourced" components
- completely rewrote keybind bindings
  - not functional yet
  - buggy a shell
  - hits perlbug

File Contents

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