ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC/BindingEditor.pm
Revision: 1.7
Committed: Sun Oct 1 14:48:51 2006 UTC (17 years, 8 months ago) by root
Branch: MAIN
CVS Tags: rel-0_53
Changes since 1.6: +2 -2 lines
Log Message:
support for grabbing win32 proxy

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
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 $::PROFILE->{bindings}{$mod}{$sym} = $cmds;
135 ::update_bindings ();
136 }
137
138 sub cfg_unbind {
139 my ($self, $mod, $sym, $cmds) = @_;
140 delete $::PROFILE->{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::Toplevel
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 if (my $binder = delete $self->{binder}) {
236 $binder->destroy;
237 }
238 1
239 },
240 on_focus_out => sub {
241 # segfaults and worse :()
242 #(delete $self->{binder})->destroy if $self->{binder};
243 1
244 },
245 );
246
247 $entry->grab_focus;
248 $self->{binder}->show;
249 }
250
251 # $mod and $sym are the modifiers and key symbol
252 # $cmds is a array ref of strings (the commands)
253 # $cb is the callback that is executed on OK
254 # $ccb is the callback that is executed on CANCEL and
255 # when the binding was unsuccessful on OK
256 sub set_binding {
257 my ($self, $mod, $sym, $cmds, $cb, $ccb) = @_;
258
259 $self->clear_command_list;
260 $self->{recording} = 0;
261 $self->{rec_btn}->set_text ("start recording");
262
263 $self->{binding} = [$mod, $sym];
264 $self->{commands} = $cmds;
265
266 $self->{binding_change} = $cb;
267 $self->{binding_cancel} = $ccb;
268
269 $self->update_binding_widgets;
270 }
271
272 # this is a shortcut method that asks for a binding
273 # and then just binds it.
274 sub do_quick_binding {
275 my ($self, $cmds, $end_cb) = @_;
276 $self->set_binding (undef, undef, $cmds, sub { $self->cfg_bind (@_) });
277 $self->ask_for_bind (1, $end_cb);
278 }
279
280 sub update_binding_widgets {
281 my ($self) = @_;
282 my ($mod, $sym, $cmds) = $self->get_binding;
283 $self->{keylbl}->set_text (keycombo_to_name ($mod, $sym));
284 $self->set_command_list ($cmds);
285 }
286
287 sub get_binding {
288 my ($self) = @_;
289 return (
290 $self->{binding}->[0],
291 $self->{binding}->[1],
292 [ grep { defined $_ } @{$self->{commands}} ]
293 );
294 }
295
296 sub clear_command_list {
297 my ($self) = @_;
298 $self->{cmdbox}->clear ();
299 }
300
301 sub set_command_list {
302 my ($self, $cmds) = @_;
303
304 $self->{cmdbox}->clear ();
305 $self->{commands} = $cmds;
306
307 my $idx = 0;
308
309 for (@$cmds) {
310 $self->{cmdbox}->add (my $hb = new CFPlus::UI::HBox);
311
312 my $i = $idx;
313 $hb->add (new CFPlus::UI::Label text => $_);
314 $hb->add (new CFPlus::UI::Button
315 text => "delete",
316 tooltip => "Deletes the action from the record",
317 on_activate => sub {
318 $self->{cmdbox}->remove ($hb);
319 $cmds->[$i] = undef;
320 });
321
322
323 $idx++
324 }
325 }
326
327 1