1
package Padre::Wx::Dialog::KeyBindings;
6
use Padre::Constant ();
8
use Padre::Util ('_T');
10
use Padre::Wx::Role::Main ();
11
use Padre::Wx::Role::Dialog ();
13
our $VERSION = '0.90';
16
Padre::Wx::Role::Dialog
20
# Creates the key bindings dialog and returns the instance
25
# Create the Wx dialog
26
my $self = $class->SUPER::new(
29
Wx::gettext('Key Bindings'),
30
Wx::wxDefaultPosition,
32
Wx::wxDEFAULT_FRAME_STYLE,
35
# Set some internal parameters
36
$self->{sortcolumn} = 0;
37
$self->{sortreverse} = 0;
40
$self->SetMinSize( [ 770, 550 ] );
42
# Create sizer that will host all controls
43
my $sizer = Wx::BoxSizer->new(Wx::wxHORIZONTAL);
46
$self->_create_controls($sizer);
48
# Bind the control events
51
# Wrap everything in a vbox to add some padding
52
$self->SetSizer($sizer);
54
$self->CentreOnParent;
59
# Create dialog controls
60
sub _create_controls {
61
my ( $self, $sizer ) = @_;
64
my $filter_label = Wx::StaticText->new( $self, -1, Wx::gettext('&Filter:') );
67
$self->{filter} = Wx::TextCtrl->new( $self, -1, '' );
69
# Filtered key bindings list
70
$self->{list} = Wx::ListView->new(
73
Wx::wxDefaultPosition,
75
Wx::wxLC_REPORT | Wx::wxLC_SINGLE_SEL,
77
my @titles = qw(Action Description Shortcut);
78
foreach my $i ( 0 .. 2 ) {
79
$self->{list}->InsertColumn( $i, Wx::gettext( $titles[$i] ) );
80
$self->{list}->SetColumnWidth( $i, Wx::wxLIST_AUTOSIZE );
83
# TODO add tooltip with the comments
86
my $shortcut_label = Wx::StaticText->new( $self, -1, Wx::gettext('Sh&ortcut:') );
88
# modifier radio button fields
89
$self->{ctrl} = Wx::CheckBox->new( $self, -1, Wx::gettext('Ctrl') );
90
$self->{alt} = Wx::CheckBox->new( $self, -1, Wx::gettext('Alt') );
91
$self->{shift} = Wx::CheckBox->new( $self, -1, Wx::gettext('Shift') );
94
my $plus_label_1 = Wx::StaticText->new( $self, -1, '+' );
95
my $plus_label_2 = Wx::StaticText->new( $self, -1, '+' );
99
_T('None'), _T('Backspace'), _T('Tab'), _T('Space'), _T('Up'), _T('Down'),
100
_T('Left'), _T('Right'), _T('Insert'), _T('Delete'), _T('Home'), _T('End'),
101
_T('PageUp'), _T('PageDown'), _T('Enter'), _T('Escape'),
102
'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12',
103
'A' .. 'Z', '0' .. '9', '~', '-', '=', '[', ']', ';', '\'', ',', '.', '/'
105
$self->{keys} = \@keys;
107
my @translated_keys = map { Wx::gettext($_) } @keys;
108
$self->{key} = Wx::Choice->new(
110
Wx::wxDefaultPosition,
114
$self->{key}->SetSelection(0);
116
# TODO tooltips for all buttons
118
# Set key binding button
119
$self->{button_set} = Wx::Button->new(
120
$self, -1, Wx::gettext('&Set'),
122
$self->{button_set}->Enable(1);
125
$self->{button_delete} = Wx::Button->new(
126
$self, -1, Wx::gettext('&Delete'),
128
$self->{button_delete}->Enable(1);
131
$self->{button_reset} = Wx::Button->new(
132
$self, -1, Wx::gettext('&Reset'),
134
$self->{button_reset}->SetToolTip( Wx::gettext('Reset to default shortcut') );
135
$self->{button_reset}->Enable(1);
138
$self->{button_close} = Wx::Button->new(
139
$self, Wx::wxID_CANCEL, Wx::gettext('&Close'),
143
#----- Dialog Layout -------
147
my $filter_sizer = Wx::BoxSizer->new(Wx::wxHORIZONTAL);
148
$filter_sizer->Add( $filter_label, 0, Wx::wxALIGN_CENTER_VERTICAL, 5 );
149
$filter_sizer->Add( $self->{filter}, 1, Wx::wxALIGN_CENTER_VERTICAL, 5 );
151
# Ctrl/Alt Modifier sizer
152
my $modifier_sizer = Wx::BoxSizer->new(Wx::wxVERTICAL);
153
$modifier_sizer->Add( $self->{ctrl}, 1, Wx::wxALIGN_CENTER_VERTICAL, 5 );
154
$modifier_sizer->AddSpacer(3);
155
$modifier_sizer->Add( $self->{alt}, 1, Wx::wxALIGN_CENTER_VERTICAL, 5 );
158
my $value_sizer = Wx::BoxSizer->new(Wx::wxHORIZONTAL);
159
$value_sizer->Add( $shortcut_label, 0, Wx::wxALIGN_CENTER_VERTICAL, 5 );
160
$value_sizer->AddStretchSpacer;
161
$value_sizer->Add( $modifier_sizer, 0, Wx::wxALIGN_CENTER_VERTICAL, 5 );
162
$value_sizer->AddSpacer(5);
163
$value_sizer->Add( $plus_label_1, 0, Wx::wxALIGN_CENTER_VERTICAL, 5 );
164
$value_sizer->AddSpacer(5);
165
$value_sizer->Add( $self->{shift}, 0, Wx::wxALIGN_CENTER_VERTICAL, 5 );
166
$value_sizer->AddSpacer(5);
167
$value_sizer->Add( $plus_label_2, 0, Wx::wxALIGN_CENTER_VERTICAL, 5 );
168
$value_sizer->AddSpacer(5);
169
$value_sizer->Add( $self->{key}, 0, Wx::wxALIGN_CENTER_VERTICAL, 5 );
170
$value_sizer->AddStretchSpacer;
171
$value_sizer->Add( $self->{button_set}, 0, Wx::wxALIGN_CENTER_VERTICAL, 5 );
172
$value_sizer->Add( $self->{button_delete}, 0, Wx::wxALIGN_CENTER_VERTICAL, 5 );
173
$value_sizer->Add( $self->{button_reset}, 0, Wx::wxALIGN_CENTER_VERTICAL, 5 );
176
my $button_sizer = Wx::BoxSizer->new(Wx::wxHORIZONTAL);
177
$button_sizer->Add( $self->{button_close}, 1, Wx::wxLEFT, 5 );
178
$button_sizer->AddSpacer(5);
180
# Main vertical sizer
181
my $vsizer = Wx::BoxSizer->new(Wx::wxVERTICAL);
182
$vsizer->Add( $filter_sizer, 0, Wx::wxALL | Wx::wxEXPAND, 3 );
183
$vsizer->Add( $self->{list}, 1, Wx::wxALL | Wx::wxEXPAND, 3 );
184
$vsizer->Add( $value_sizer, 0, Wx::wxALL | Wx::wxEXPAND, 3 );
185
$vsizer->AddSpacer(5);
186
$vsizer->Add( $button_sizer, 0, Wx::wxALIGN_RIGHT, 5 );
187
$vsizer->AddSpacer(5);
189
# Hide value and info sizer at startup
190
$vsizer->Show( 2, 0 );
191
$vsizer->Show( 3, 0 );
193
# Store vertical sizer reference for later usage
194
$self->{vsizer} = $vsizer;
196
# Wrap with a horizontal sizer to get left/right padding
197
$sizer->Add( $vsizer, 1, Wx::wxALL | Wx::wxEXPAND, 5 );
202
# A Private method to binds events to controls
206
# Set focus when Keypad Down or page down keys are pressed
210
$self->_on_char( $_[1] );
214
# Update filter search results on each text change
223
# When an item is selected, its values must be populated below
224
Wx::Event::EVT_LIST_ITEM_SELECTED(
228
shift->_on_list_item_selected(@_);
232
# When the title is clicked, sort the items
233
Wx::Event::EVT_LIST_COL_CLICK(
237
shift->list_col_click(@_);
242
Wx::Event::EVT_BUTTON(
246
shift->_on_set_button;
251
Wx::Event::EVT_BUTTON(
253
$self->{button_delete},
255
shift->_on_delete_button;
260
Wx::Event::EVT_BUTTON(
262
$self->{button_reset},
264
shift->_on_reset_button;
269
Wx::Event::EVT_BUTTON(
271
$self->{button_close},
273
shift->_on_close_button;
280
# Private method to handle on character pressed event
284
my $code = $event->GetKeyCode;
286
$self->{list}->SetFocus
287
if ( $code == Wx::WXK_DOWN )
288
or ( $code == Wx::WXK_NUMPAD_PAGEDOWN )
289
or ( $code == Wx::WXK_PAGEDOWN );
296
# Translates the shortcut to its native language
297
sub _translate_shortcut {
300
my @parts = split /-/, $shortcut;
301
my $regular_key = @parts ? $parts[-1] : '';
303
return join '-', map { Wx::gettext($_) } @parts;
306
# Private method to handle the selection of a key binding item
307
sub _on_list_item_selected {
311
my $list = $self->{list};
312
my $index = $list->GetFirstSelected;
313
my $action_name = $list->GetItemText($index);
314
my $action = $self->ide->actions->{$action_name};
316
my $shortcut = $self->ide->actions->{$action_name}->shortcut;
317
$shortcut = '' if not defined $shortcut;
319
$self->{button_reset}->Enable( $shortcut ne $self->config->default( $action->shortcut_setting ) );
321
$self->{button_delete}->Enable( $shortcut ne '' );
323
$self->_update_shortcut_ui($shortcut);
328
# Updates the shortcut UI
329
sub _update_shortcut_ui {
330
my ( $self, $shortcut ) = @_;
332
my @parts = split /-/, $shortcut;
333
my $regular_key = @parts ? $parts[-1] : '';
335
# Find the regular key index in the choice box
336
my $regular_index = 0;
337
my @keys = @{ $self->{keys} };
338
for ( my $i = 0; $i < scalar @keys; $i++ ) {
339
if ( $regular_key eq $keys[$i] ) {
346
$self->{key}->SetSelection($regular_index);
347
$self->{ctrl}->SetValue( $shortcut =~ /Ctrl/ ? 1 : 0 );
348
$self->{alt}->SetValue( $shortcut =~ /Alt/ ? 1 : 0 );
349
$self->{shift}->SetValue( $shortcut =~ /Shift/ ? 1 : 0 );
351
# Make sure the value and info sizer are not hidden
352
$self->{vsizer}->Show( 2, 1 );
353
$self->{vsizer}->Show( 3, 1 );
354
$self->{vsizer}->Layout;
359
# Private method to handle the pressing of the set value button
363
my $index = $self->{list}->GetFirstSelected;
364
my $action_name = $self->{list}->GetItemText($index);
367
for my $regular_key ( 'Shift', 'Ctrl', 'Alt' ) {
368
push @key_list, $regular_key if $self->{ lc $regular_key }->GetValue;
370
my $key_index = $self->{key}->GetSelection;
371
my $regular_key = $self->{keys}->[$key_index];
372
push @key_list, $regular_key if not $regular_key eq 'None';
373
my $shortcut = join '-', @key_list;
375
$self->_try_to_set_binding( $action_name, $shortcut );
380
# Tries to set the binding and asks the user if he want to set the shortcut if has already be used elsewhere
381
sub _try_to_set_binding {
382
my ( $self, $action_name, $shortcut ) = @_;
384
my $other_action = $self->ide->shortcuts->{$shortcut};
385
if ( defined $other_action && $other_action->name ne $action_name ) {
386
my $answer = $self->yes_no(
388
Wx::gettext("The shortcut '%s' is already used by the action '%s'.\n"),
389
$shortcut, $other_action->label_text
391
. Wx::gettext('Do you want to override it with the selected action?'),
392
Wx::gettext('Override Shortcut')
395
$self->_set_binding( $other_action->name, '' );
401
$self->_set_binding( $action_name, $shortcut );
406
# Sets the key binding in Padre's configuration
408
my ( $self, $action_name, $shortcut ) = @_;
410
my $shortcuts = $self->ide->shortcuts;
411
my $action = $self->ide->actions->{$action_name};
413
# modify shortcut registry
414
my $old_shortcut = $action->shortcut;
415
delete $shortcuts->{$old_shortcut} if defined $old_shortcut;
416
$shortcuts->{$shortcut} = $action;
418
# set the action's shortcut
419
$action->shortcut( $shortcut eq '' ? undef : $shortcut );
421
# modify the configuration database
422
$self->config->set( $action->shortcut_setting, $shortcut );
423
$self->config->write;
425
# Update the action's UI
426
my $non_default = $self->config->default( $action->shortcut_setting ) ne $shortcut;
427
$self->_update_action_ui( $action_name, $shortcut, $non_default );
432
# Private method to update the UI from the provided preference
433
sub _update_action_ui {
435
my ( $self, $action_name, $shortcut, $non_default ) = @_;
437
my $list = $self->{list};
438
my $index = $list->FindItem( -1, $action_name );
440
$self->{button_reset}->Enable($non_default);
441
$list->SetItem( $index, 2, _translate_shortcut($shortcut) );
442
$self->_set_item_bold_font( $index, $non_default );
444
$self->_update_shortcut_ui($shortcut);
449
# Private method to handle the pressing of the delete button
450
sub _on_delete_button {
453
# Prepare the key binding
454
my $index = $self->{list}->GetFirstSelected;
455
my $action_name = $self->{list}->GetItemText($index);
457
$self->_set_binding( $action_name, '' );
462
# Private method to handle the pressing of the reset button
463
sub _on_reset_button {
466
my $index = $self->{list}->GetFirstSelected;
467
my $action_name = $self->{list}->GetItemText($index);
468
my $action = $self->ide->actions->{$action_name};
470
$self->_try_to_set_binding(
472
$self->config->default( $action->shortcut_setting )
478
# Private method to handle the close action
479
sub _on_close_button {
481
my $main = $self->GetParent;
483
# re-create menu to activate shortcuts
484
delete $main->{menu};
485
$main->{menu} = Padre::Wx::Menubar->new($main);
486
$main->SetMenuBar( $main->menu->wx );
489
$self->EndModal(Wx::wxID_CLOSE);
493
# Private method to update the key bindings list view
496
my $filter = quotemeta $self->{filter}->GetValue;
499
my $list = $self->{list};
500
$list->DeleteAllItems;
502
my $actions = $self->ide->actions;
503
my $real_color = Wx::SystemSettings::GetColour(Wx::wxSYS_COLOUR_WINDOW);
504
my $alternate_color = Wx::Colour->new(
505
int( $real_color->Red * 0.9 ),
506
int( $real_color->Green * 0.9 ),
511
my @action_names = sort { $a cmp $b } keys %$actions;
512
if ( $self->{sortcolumn} == 1 ) {
514
# Sort by Descreption
515
@action_names = sort { $actions->{$a}->label_text cmp $actions->{$b}->label_text } keys %$actions;
517
if ( $self->{sortcolumn} == 2 ) {
520
@action_names = sort {
521
_translate_shortcut( $actions->{$a}->shortcut || '' )
522
cmp _translate_shortcut( $actions->{$b}->shortcut || '' )
525
if ( $self->{sortreverse} ) {
526
@action_names = reverse @action_names;
529
foreach my $action_name (@action_names) {
530
my $action = $actions->{$action_name};
531
my $shortcut = defined $action->shortcut ? $action->shortcut : '';
533
# Ignore key binding if it does not match the filter
535
if $action->label_text !~ /$filter/i
536
and $action_name !~ /$filter/i
537
and $shortcut !~ /$filter/i;
539
# Add the key binding to the list control
540
$list->InsertStringItem( $index, $action_name );
541
$list->SetItem( $index, 1, $action->label_text );
542
$list->SetItem( $index, 2, _translate_shortcut($shortcut) );
544
# Non-default (i.e. overriden) shortcuts should have a bold font
545
my $non_default = $self->config->default( $action->shortcut_setting ) ne $shortcut;
546
$self->_set_item_bold_font( $index, $non_default );
548
# Alternating table colors
549
$list->SetItemBackgroundColour( $index, $alternate_color ) unless $index % 2;
556
# Private method to set item to bold
557
# Somehow SetItemFont is not there... hence i had to write this long workaround
558
sub _set_item_bold_font {
559
my ( $self, $index, $bold ) = @_;
561
my $list = $self->{list};
562
my $item = $list->GetItem($index);
563
my $font = $item->GetFont;
564
$font->SetWeight( $bold ? Wx::wxFONTWEIGHT_BOLD : Wx::wxFONTWEIGHT_NORMAL );
565
$item->SetFont($font);
566
$list->SetItem($item);
571
# Private method to resize list columns
572
sub _resize_columns {
575
# Resize all columns but the last to their biggest item width
576
my $list = $self->{list};
577
for ( 0 .. $list->GetColumnCount - 1 ) {
578
$list->SetColumnWidth( $_, Wx::wxLIST_AUTOSIZE );
587
my $column = $event->GetColumn;
588
my $prevcol = $self->{sortcolumn};
589
my $reversed = $self->{sortreverse};
590
$reversed = $column == $prevcol ? !$reversed : 0;
591
$self->{sortcolumn} = $column;
592
$self->{sortreverse} = $reversed;
597
# Shows the key binding dialog
601
# Set focus on the filter text field
602
$self->{filter}->SetFocus;
604
# Update the preferences list
608
$self->_resize_columns;
610
# If it is not shown, show the dialog
625
Padre::Wx::Dialog::KeyBindings - a dialog to show and configure key bindings
629
This dialog lets the user search for an action and then configure a new
636
my $key_bindings = Padre::Wx::Dialog::KeyBindings->new($main);
638
Returns a new C<Padre::Wx::Dialog::KeyBindings> instance
642
$key_bindings->show($main);
644
Shows the dialog. Returns C<undef>.
646
=head1 COPYRIGHT & LICENSE
648
Copyright 2008-2011 The Padre development team as listed in Padre.pm.
650
This program is free software; you can redistribute
651
it and/or modify it under the same terms as Perl itself.
653
The full text of the license can be found in the
654
LICENSE file included with this module.
661
# Copyright 2008-2011 The Padre development team as listed in Padre.pm.
663
# This program is free software; you can redistribute it and/or
664
# modify it under the same terms as Perl 5 itself.