ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC/BindingEditor.pm
Revision: 1.8
Committed: Thu Oct 26 20:45:06 2006 UTC (17 years, 7 months ago) by elmex
Branch: MAIN
Changes since 1.7: +2 -1 lines
Log Message:
fixed problem with binding of modifiers when 'invsible' modifiers were activated

File Contents

# User Rev Content
1 root 1.4 package CFPlus::BindingEditor;
2 root 1.1
3 root 1.2 use strict;
4    
5 root 1.4 use CFPlus::UI;
6 root 1.1
7 root 1.5 our @ISA = CFPlus::UI::Toplevel::;
8 root 1.1
9 root 1.2 my @ALLOWED_MODIFIER_KEYS = (
10 root 1.4 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 root 1.2 );
20    
21     my %ALLOWED_MODIFIERS = (
22 root 1.4 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 root 1.2 );
32 elmex 1.8 my $ALLOWED_MODIFIER_MASK = 0x0FC3; # this is the mask of the above ALLOWED_MODIFIERS or'ed together
33 root 1.2
34     my %DIRECT_BIND_CHARS = map { $_ => 1 } qw/0 1 2 3 4 5 6 7 8 9/;
35     my @DIRECT_BIND_KEYS = (
36 root 1.4 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 root 1.2 );
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 root 1.4 return $mods . CFPlus::SDL_GetKeyName ($sym);
63 root 1.2 }
64    
65 root 1.1 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 root 1.4 $self->add (my $vb = new CFPlus::UI::VBox);
76 root 1.1
77 root 1.4 $vb->add ($self->{rec_btn} = new CFPlus::UI::Button
78 root 1.1 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 root 1.4 $vb->add (new CFPlus::UI::Label text => "Actions:");
92     $vb->add ($self->{cmdbox} = new CFPlus::UI::VBox);
93 root 1.1
94 root 1.4 $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 root 1.1 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 root 1.4 $vb->add (my $hb = new CFPlus::UI::HBox);
105     $hb->add (new CFPlus::UI::Button
106 root 1.2 text => "OK",
107 root 1.1 expand => 1,
108     tooltip => "This closes the binding editor and saves the binding",
109     on_activate => sub {
110 root 1.2 (delete $self->{binder})->destroy if $self->{binder};
111 root 1.1 $self->hide;
112     $self->commit;
113 root 1.2 0
114 root 1.1 });
115    
116 root 1.4 $hb->add (new CFPlus::UI::Button
117 root 1.2 text => "Cancel",
118 root 1.1 expand => 1,
119     tooltip => "This closes the binding editor without saving",
120     on_activate => sub {
121 root 1.2 (delete $self->{binder})->destroy if $self->{binder};
122 root 1.1 $self->hide;
123     $self->{binding_cancel}->()
124     if $self->{binding_cancel};
125 root 1.2 0
126 root 1.1 });
127    
128     $self->update_binding_widgets;
129    
130     $self
131     }
132    
133     sub cfg_bind {
134     my ($self, $mod, $sym, $cmds) = @_;
135 root 1.7 $::PROFILE->{bindings}{$mod}{$sym} = $cmds;
136 root 1.1 ::update_bindings ();
137     }
138    
139     sub cfg_unbind {
140     my ($self, $mod, $sym, $cmds) = @_;
141 root 1.7 delete $::PROFILE->{bindings}{$mod}{$sym};
142 root 1.1 ::update_bindings ();
143     }
144    
145     sub commit {
146     my ($self) = @_;
147 root 1.2
148 root 1.1 my ($mod, $sym, $cmds) = $self->get_binding;
149 root 1.2
150 root 1.1 if ($sym != 0 && @$cmds > 0) {
151 root 1.2 $::STATUSBOX->add ("Bound actions to <i>" . keycombo_to_name ($mod, $sym) . "</i>. "
152     . "Do not forget to 'Save Config'!");
153 root 1.1 $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 root 1.2 sub ask_for_bind {
184     my ($self, $commit, $end_cb) = @_;
185    
186     return if $self->{binder};
187 root 1.1
188 root 1.2 Scalar::Util::weaken $self;
189    
190 root 1.5 $self->{binder} = new CFPlus::UI::Toplevel
191 root 1.2 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 root 1.4 $self->{binder}->add (my $vb = new CFPlus::UI::VBox);
203     $vb->add (new CFPlus::UI::Label
204 root 1.2 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 root 1.4 $vb->add (my $entry = new CFPlus::UI::Entry
208 root 1.2 text => "",
209     on_key_down => sub {
210     my ($entry, $ev) = @_;
211    
212 elmex 1.8 my $mod = $ev->{mod} & $ALLOWED_MODIFIER_MASK;
213 root 1.2 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 root 1.4 if ($mod == CFPlus::KMOD_NONE
219 root 1.2 and not $DIRECT_BIND_CHARS{chr ($ev->{unicode})}
220     and not grep { $sym == $_ } @DIRECT_BIND_KEYS)
221     {
222     $::STATUSBOX->add (
223 root 1.4 "Cannot bind key " . CFPlus::SDL_GetKeyName ($sym) . " directly without modifier, "
224 root 1.2 . "as those keys are reserved for the command completer."
225     );
226     return;
227     }
228    
229     $entry->grab_focus;
230 root 1.1
231 root 1.2 $self->{binding} = [$mod, $sym];
232     $self->update_binding_widgets;
233     $self->commit if $commit;
234     $end_cb->() if $end_cb;
235    
236 root 1.6 if (my $binder = delete $self->{binder}) {
237     $binder->destroy;
238     }
239 root 1.2 1
240     },
241     on_focus_out => sub {
242     # segfaults and worse :()
243     #(delete $self->{binder})->destroy if $self->{binder};
244     1
245     },
246     );
247 root 1.1
248 root 1.2 $entry->grab_focus;
249     $self->{binder}->show;
250 root 1.1 }
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 root 1.2 $self->{keylbl}->set_text (keycombo_to_name ($mod, $sym));
285 root 1.1 $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 root 1.4 $self->{cmdbox}->add (my $hb = new CFPlus::UI::HBox);
312 root 1.1
313     my $i = $idx;
314 root 1.4 $hb->add (new CFPlus::UI::Label text => $_);
315     $hb->add (new CFPlus::UI::Button
316 root 1.1 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