--- deliantra/Deliantra-Client/DC/Macro.pm 2006/12/09 21:44:43 1.2 +++ deliantra/Deliantra-Client/DC/Macro.pm 2007/12/26 21:03:21 1.16 @@ -1,21 +1,58 @@ -package CFPlus::Macro; +package DC::Macro; use strict; -use CFPlus::UI; +use List::Util (); +use DC::UI; our $REFRESH_MACRO_LIST; +our %DEFAULT_KEYMAP = ( + (map +("($_)" => "!completer $_"), "a" .. "z"), + "(!)" => "!completer shout ", + "(\")" => "!completer say ", + "(')" => "!completer", + + "LShift-tab" => "!toggle-messagewindow", + "RShift-tab" => "!toggle-messagewindow", + "tab" => "!toggle-playerbook", + "f1" => "!toggle-help", + "f2" => "!toggle-stats", + "f3" => "!toggle-skills", + "f4" => "!toggle-spells", + "f5" => "!toggle-inventory", + "f9" => "!toggle-setup", + (map +("LAlt-$_" => "!switch-tab $_"), 0..9), + (map +("RAlt-$_" => "!switch-tab $_"), 0..9), + "LAlt-x" => "!close-current-tab", + "return" => "!activate-chat", + "." => "!repeat-command", + + "," => "take", + "space" => "apply", + "enter" => "examine", + "[+]" => "rotateshoottype +", + "[-]" => "rotateshoottype -", + "LAlt-e" => "examine", + "LAlt-s" => "ready_skill find traps", + "LAlt-d" => "ready_skill disarm traps", + "LAlt-p" => "ready_skill praying", +); + # allowed modifiers our %MODIFIER = ( - "LShift" => CFPlus::KMOD_LSHIFT, - "RShift" => CFPlus::KMOD_RSHIFT, - "LCtrl" => CFPlus::KMOD_LCTRL, - "RCtrl" => CFPlus::KMOD_RCTRL, - "LAlt" => CFPlus::KMOD_LALT, - "RAlt" => CFPlus::KMOD_RALT, - "LMeta" => CFPlus::KMOD_LMETA, - "RMeta" => CFPlus::KMOD_RMETA, + "LShift" => DC::KMOD_LSHIFT, + "RShift" => DC::KMOD_RSHIFT, +# "Shift" => DC::KMOD_LSHIFT | DC::KMOD_RSHIFT, + "LCtrl" => DC::KMOD_LCTRL, + "RCtrl" => DC::KMOD_RCTRL, +# "Ctrl" => DC::KMOD_LCTRL | DC::KMOD_RCTRL, + "LAlt" => DC::KMOD_LALT, + "RAlt" => DC::KMOD_RALT, +# "Alt" => DC::KMOD_LALT | DC::KMOD_RALT, + "LMeta" => DC::KMOD_LMETA, + "RMeta" => DC::KMOD_RMETA, +# "Meta" => DC::KMOD_LMETA | DC::KMOD_RMETA, ); # allowed modifiers @@ -25,28 +62,79 @@ our @DIRECT_CHARS = qw(0 1 2 3 4 5 6 7 8 9); our @DIRECT_KEYS = ( - CFPlus::SDLK_F1, - CFPlus::SDLK_F2, - CFPlus::SDLK_F3, - CFPlus::SDLK_F4, - CFPlus::SDLK_F5, - CFPlus::SDLK_F6, - CFPlus::SDLK_F7, - CFPlus::SDLK_F8, - CFPlus::SDLK_F9, - CFPlus::SDLK_F10, - CFPlus::SDLK_F11, - CFPlus::SDLK_F12, - CFPlus::SDLK_F13, - CFPlus::SDLK_F14, - CFPlus::SDLK_F15, + DC::SDLK_F1, + DC::SDLK_F2, + DC::SDLK_F3, + DC::SDLK_F4, + DC::SDLK_F5, + DC::SDLK_F6, + DC::SDLK_F7, + DC::SDLK_F8, + DC::SDLK_F9, + DC::SDLK_F10, + DC::SDLK_F11, + DC::SDLK_F12, + DC::SDLK_F13, + DC::SDLK_F14, + DC::SDLK_F15, +); + +our %MACRO_FUNCTION = ( + "toggle-messagewindow" => sub { $::MESSAGE_WINDOW->toggle_visibility }, + "toggle-playerbook" => sub { $::PL_WINDOW->toggle_visibility }, + "toggle-help" => sub { $::HELP_WINDOW->toggle_visibility }, + "toggle-stats" => sub { ::toggle_player_page ($::STATS_PAGE) }, + "toggle-skills" => sub { ::toggle_player_page ($::SKILL_PAGE) }, + "toggle-spells" => sub { ::toggle_player_page ($::SPELL_PAGE) }, + "toggle-inventory" => sub { ::toggle_player_page ($::INVENTORY_PAGE) }, + "toggle-pickup" => sub { ::toggle_player_page ($::PICKUP_PAGE) }, + "toggle-setup" => sub { $::SETUP_DIALOG->toggle_visibility }, + "toggle-setup" => sub { $::SETUP_DIALOG->toggle_visibility }, + "switch-tab" => sub { $::MESSAGE_WINDOW->user_switch_to_page (0 + shift) }, + "close-current-tab" => sub { $::MESSAGE_WINDOW->close_current_tab }, + "activate-chat" => sub { $::MESSAGE_WINDOW->activate_current }, + "repeat-command" => sub { + $::CONN->user_send ($::COMPLETER->{last_command}) + if $::CONN && exists $::COMPLETER->{last_command}; + }, + "completer" => sub { + if ($::CONN) { + $::COMPLETER->set_prefix (shift); + $::COMPLETER->show; + } + }, ); +our $DEFAULT_KEYMAP; + +sub init { + $DEFAULT_KEYMAP ||= do { + my %sym = map +(DC::SDL_GetKeyName $_, $_), DC::SDLK_FIRST .. DC::SDLK_LAST; + my $map; + + while (my ($k, $v) = each %DEFAULT_KEYMAP) { + if ($k =~ /^\((.)\)$/) { + $map->{U}{ord $1} = $v; + } else { + my @mod = split /-/, $k; + my $sym = $sym{pop @mod} + or warn "unknown keysym $k\n"; + + my $mod = 0; $mod |= $MODIFIER{$_} for @mod; + + $map->{K}[DC::popcount $mod]{$mod}{$sym} = $v; + } + } + + %DEFAULT_KEYMAP = (); + $map + }; +} + sub accelkey_to_string($) { join "-", - (grep $_[0][0] & $MODIFIER{$_}, - keys %MODIFIER), - CFPlus::SDL_GetKeyName $_[0][1] + (grep $_[0][0] & $MODIFIER{$_}, keys %MODIFIER), + DC::SDL_GetKeyName $_[0][1] } sub trigger_to_string($) { @@ -83,7 +171,7 @@ &$end_cb; }; - $window = new CFPlus::UI::Toplevel + $window = new DC::UI::Toplevel title => "Edit Macro Trigger", x => "center", y => "center", @@ -101,9 +189,9 @@ }, ; - $window->add (my $vb = new CFPlus::UI::VBox); + $window->add (my $vb = new DC::UI::VBox); - $vb->add (new CFPlus::UI::Label + $vb->add (new DC::UI::Label text => "To bind the macro to a key,\n" . "press a modifier (Ctrl, Alt\n" . "and/or Shift) and a key, or\n" @@ -113,7 +201,7 @@ ellipsise => 0, ); - $vb->add (my $entry = new CFPlus::UI::Label + $vb->add (my $entry = new DC::UI::Label fg => [0, 0, 0, 1], bg => [1, 1, 0, 1], ); @@ -136,8 +224,8 @@ keys %MODIFIER ); - return if $sym >= CFPlus::SDLK_MODIFIER_MIN - && $sym <= CFPlus::SDLK_MODIFIER_MAX; + return if $sym >= DC::SDLK_MODIFIER_MIN + && $sym <= DC::SDLK_MODIFIER_MAX; if ($mod || ((grep $_ eq chr $ev->{unicode}, @DIRECT_CHARS) @@ -146,7 +234,7 @@ $macro->{accelkey} = [$mod, $sym]; $done->(1); } else { - $entry->set_text ("cannot bind " . (CFPlus::SDL_GetKeyName $sym) . " without modifier."); + $entry->set_text ("cannot bind " . (DC::SDL_GetKeyName $sym) . " without modifier."); } 1 }; @@ -158,34 +246,79 @@ $window->show; } +sub find_default($) { + my ($ev) = @_; + + for my $m (reverse grep $_, @{ $DEFAULT_KEYMAP->{K} }) { + for (keys %$m) { + if ($_ == ($ev->{mod} & $_)) { + if (defined (my $cmd = $m->{$_}{$ev->{sym}})) { + return $cmd; + } + } + } + } + + if (my $cmd = $DEFAULT_KEYMAP->{U}{$ev->{unicode}}) { + return $cmd; + } + + () +} + # find macro by event -# maybe return multiple results? -sub match_event($) { +sub find($) { my ($ev) = @_; - for my $macro (@{ $::PROFILE->{macro} || [] }) { - my $key = $macro->{accelkey} - or next; - - $key->[1] == $ev->{sym} - && $key->[0] == ($ev->{mod} & $MODIFIER_MASK) - && return $macro; + # try user-defined macros + if (my @user = + grep { + if (my $key = $_->{accelkey}) { + $key->[1] == $ev->{sym} + && $key->[0] == ($ev->{mod} & $MODIFIER_MASK) + } else { + 0 + } + } @{ $::PROFILE->{macro} || [] } + ) { + return @user; + } + + # now try default keymap + if (defined (my $def = find_default $ev)) { + return { + action => [$def], + }; } () } +sub execute { + my ($macro) = @_; + + for (@{ $macro->{action} }) { + if (/^\!(\S+)\s?(.*)$/) { + $MACRO_FUNCTION{$1}->($2) + if exists $MACRO_FUNCTION{$1}; + } else { + $::CONN->send_command ($_) + if $::CONN; + } + } +} + sub keyboard_setup { - my $kbd_setup = new CFPlus::UI::VBox; + my $kbd_setup = new DC::UI::VBox; - $kbd_setup->add (my $list = new CFPlus::UI::VBox); + $kbd_setup->add (my $list = new DC::UI::VBox); - $list->add (new CFPlus::UI::FancyFrame + $list->add (new DC::UI::FancyFrame label => "Options", - child => (my $hb = new CFPlus::UI::HBox), + child => (my $hb = new DC::UI::HBox), ); - $hb->add (new CFPlus::UI::Label text => "only shift-up stops fire"); - $hb->add (new CFPlus::UI::CheckBox + $hb->add (new DC::UI::Label text => "only shift-up stops fire"); + $hb->add (new DC::UI::CheckBox expand => 1, state => $::CFG->{shift_fire_stop}, tooltip => "If this checkbox is enabled you will stop fire only if you stop pressing shift.", @@ -196,9 +329,9 @@ }, ); - $list->add (new CFPlus::UI::FancyFrame - label => "Bindings", - child => (my $macros = new CFPlus::UI::Table), + $list->add (new DC::UI::FancyFrame + label => "Macros", + child => (my $macros = new DC::UI::VBox), ); my $refresh; @@ -211,7 +344,7 @@ my ($macro) = @_; $kbd_setup->clear; - $kbd_setup->add (new CFPlus::UI::Button + $kbd_setup->add (new DC::UI::Button text => "Return", tooltip => "Return to the macro list.", on_activate => sub { @@ -221,25 +354,25 @@ 1 }, ); - $kbd_setup->add (new CFPlus::UI::FancyFrame + $kbd_setup->add (new DC::UI::FancyFrame label => "Edit Macro", - child => (my $editor = new CFPlus::UI::Table col_expand => [0, 1]), + child => (my $editor = new DC::UI::Table col_expand => [0, 1]), ); - $editor->add (0, 1, new CFPlus::UI::Label + $editor->add_at (0, 1, new DC::UI::Label text => "Trigger", tooltip => $tooltip_trigger, can_hover => 1, can_events => 1, ); - $editor->add (0, 2, new CFPlus::UI::Label + $editor->add_at (0, 2, new DC::UI::Label text => "Actions", tooltip => $tooltip_commands, can_hover => 1, can_events => 1, ); - $editor->add (1, 2, my $textedit = new CFPlus::UI::TextEdit + $editor->add_at (1, 2, my $textedit = new DC::UI::TextEdit text => macro_to_text $macro, tooltip => $tooltip_commands, on_changed => sub { @@ -247,7 +380,7 @@ }, ); - $editor->add (1, 1, my $accel = new CFPlus::UI::Button + $editor->add_at (1, 1, my $accel = new DC::UI::Button text => trigger_to_string $macro, tooltip => "To change the trigger for a macro, activate this button.", on_activate => sub { @@ -260,7 +393,7 @@ ); my $recording; - $editor->add (1, 3, new CFPlus::UI::Button + $editor->add_at (1, 3, new DC::UI::Button text => "Start Recording", tooltip => "Start/Stop command recording: when recording, " . "actions and commands you invoke are appended to this macro. " @@ -274,9 +407,8 @@ $recording = $::CONN && !$recording; if ($recording) { $widget->set_text ("Stop Recording"); - my $action = $macro->{action} ||= []; $::CONN->record (sub { - push @$action, $_[0]; + push @{ $macro->{action} }, $_[0]; $textedit->set_text (macro_to_text $macro); }) if $::CONN; } else { @@ -287,22 +419,34 @@ ); }; + $macros->add (new DC::UI::Button + text => "New Macro", + tooltip => "Creates a new, empty, macro you can edit.", + on_activate => sub { + my $macro = { }; + push @{ $::PROFILE->{macro} }, $macro; + $edit_macro->($macro); + }, + ); + + $macros->add (my $macrolist = new DC::UI::Table col_expand => [0, 1]); + $REFRESH_MACRO_LIST = $refresh = sub { - $macros->clear; + $macrolist->clear; - $macros->add (0, 0, new CFPlus::UI::Label + $macrolist->add_at (0, 1, new DC::UI::Label text => "Trigger", align => 0, tooltip => $tooltip_trigger . $tooltip_common, ); - $macros->add (1, 0, new CFPlus::UI::Label + $macrolist->add_at (1, 1, new DC::UI::Label text => "Commands", tooltip => $tooltip_commands . $tooltip_common, ); for my $idx (0 .. $#{$::PROFILE->{macro} || []}) { my $macro = $::PROFILE->{macro}[$idx]; - my $y = $idx + 1; + my $y = $idx + 2; my $macro_cb = sub { my ($widget, $ev) = @_; @@ -312,7 +456,7 @@ } elsif ($ev->{button} == 2) { $::CONN->macro_send ($macro) if $::CONN; } elsif ($ev->{button} == 3) { - (new CFPlus::UI::Menu + (new DC::UI::Menu items => [ ["Edit" => sub { $edit_macro->($macro) }], ["Invoke" => sub { $::CONN->macro_send ($macro) if $::CONN }], @@ -330,7 +474,7 @@ 1 }; - $macros->add (0, $y, new CFPlus::UI::Label + $macrolist->add_at (0, $y, new DC::UI::Label text => trigger_to_string $macro, tooltip => $tooltip_trigger . $tooltip_common, align => 0, @@ -339,8 +483,8 @@ on_button_down => $macro_cb, ); - $macros->add (1, $y, new CFPlus::UI::Label - text => (join "; ", @{ $macro->{action} }), + $macrolist->add_at (1, $y, new DC::UI::Label + text => (join "; ", @{ $macro->{action} || [] }), tooltip => $tooltip_commands . $tooltip_common, expand => 1, ellipsise => 3, @@ -359,14 +503,13 @@ # this is a shortcut method that asks for a binding # and then just binds it. sub quick_macro { - my ($self, $cmds, $end_cb) = @_; + my ($cmds, $end_cb) = @_; my $macro = { action => $cmds, }; trigger_edit $macro, sub { - if ($_[0]) { push @{ $::PROFILE->{macro} }, $macro; $REFRESH_MACRO_LIST->(); @@ -376,3 +519,4 @@ }; } +1