File Coverage

lib/Kephra/CommandList.pm
Criterion Covered Total %
statement 18 124 14.5
branch 0 54 0.0
condition 0 9 0.0
subroutine 6 28 21.4
pod 0 19 0.0
total 24 234 10.2


line stmt bran cond sub pod time code
1             package Kephra::CommandList;
2             our $VERSION = '0.15';
3            
4 1     1   1590 use strict;
  1         2  
  1         37  
5 1     1   7 use warnings;
  1         1  
  1         113  
6 1     1   6 use YAML::Tiny();
  1         1  
  1         254  
7            
8            
9             my %list; # the real commandlist
10             my @keymap; # maps numerical key code to cmd call ref
11 0 0   0 0   sub data { if (ref $_[0] eq 'HASH') { %list = %{$_[0]} } else { \%list } }
  0            
  0            
  0            
12 0     0 0   sub clear { %list = () }
13 0     0 0   sub file { Kephra::Config::filepath( _config()->{file}) }
14 0     0     sub _config{ Kephra::API::settings()->{app}{commandlist} }
15            
16             #sub load_cache { %list = %{ YAML::Tiny::LoadFile( $_[0] ) }}
17             #sub store_cache { YAML::Tiny::DumpFile( \%list ) }
18             # @hash1{keys %hash2} = values %hash2;
19            
20            
21             # refactor commandlist definition & localisation data into a format that can be
22             # evaled and used by gui parts
23             sub load {
24 0     0 0   my $cmd_list_def = Kephra::Config::File::load_from_node_data( _config() );
25 0 0         $cmd_list_def = Kephra::Config::Default::commandlist() unless $cmd_list_def;
26 0           assemble_data($cmd_list_def);
27             }
28            
29            
30             sub assemble_data {
31 0     0 0   my $cmd_list_def = shift;
32 1     1   5 no strict;
  1         3  
  1         263  
33 0           local ($leaf_type, $cmd_id, $target_leafe);
34             # copy data of a hash structures into specified commandlist leafes
35 0           for my $key ( qw{call enable enable_event state state_event key icon} ) {
36 0           _copy_values_of_nested_list($cmd_list_def->{$key}, $key);
37             }
38 0           my $l18n = Kephra::Config::Localisation::strings();
39 0           _copy_values_of_nested_list($l18n->{commandlist}{label},'label');
40 0           _copy_values_of_nested_list($l18n->{commandlist}{help}, 'help');
41 0           numify_key_code( keys %list );
42 0           undef $leaf_type;
43 0           undef $cmd_id;
44 0           undef $target_leafe;
45             }
46            
47 0     0 0   sub eval_data { eval_cmd_data( keys %list ) }
48            
49             sub _copy_values_of_nested_list {
50 0     0     my $root_node = shift; # source
51 1     1   6 no strict;
  1         1  
  1         93  
52 0           $target_leafe = shift;
53 0 0         _parse_and_copy_node($root_node, '') if ref $root_node eq 'HASH';
54             }
55             sub _parse_and_copy_node {
56 0     0     my ($parent_node, $parent_id) = @_;
57 1     1   5 no strict;
  1         1  
  1         1538  
58 0           for ( keys %$parent_node ){
59 0           $cmd_id = $parent_id . $_;
60 0           $leaf_type = ref $parent_node->{$_};
61 0 0         if (not $leaf_type) {
    0          
62 0 0         $list{$cmd_id}{$target_leafe} = $parent_node->{$_}
63             if $parent_node->{$_};
64             } elsif ($leaf_type eq 'HASH'){
65 0           _parse_and_copy_node($parent_node->{$_}, $cmd_id . '-')
66             }
67             }
68             }
69            
70            
71             sub numify_key_code {
72 0     0 0   my @cmd = @_;
73 0           my ($item_data, $rest, $kcode, $kname, $i, $char); #rawdata, keycode
74 0           my $k18n = Kephra::Config::Localisation::strings()->{key};
75 0           my $shift = $k18n->{meta}{shift}. '+';
76 0           my $alt = $k18n->{meta}{alt} . '+';
77 0           my $ctrl = $k18n->{meta}{ctrl} . '+';
78 0           my %keycode_map = (
79             back => &Wx::WXK_BACK, tab => &Wx::WXK_TAB, enter => &Wx::WXK_RETURN,
80             esc => &Wx::WXK_ESCAPE, space => &Wx::WXK_SPACE,
81             plus => 43, minus => 45, sharp => 47, tilde => 92,
82             del=> &Wx::WXK_DELETE, ins => &Wx::WXK_INSERT,
83             pgup => &Wx::WXK_PAGEUP, pgdn => &Wx::WXK_PAGEDOWN,
84             home => &Wx::WXK_HOME, end => &Wx::WXK_END,
85             left => &Wx::WXK_LEFT, up => &Wx::WXK_UP,
86             right => &Wx::WXK_RIGHT, down => &Wx::WXK_DOWN,
87             f1 => &Wx::WXK_F1, f2 => &Wx::WXK_F2, f3 => &Wx::WXK_F3, f4 => &Wx::WXK_F4,
88             f5 => &Wx::WXK_F5, f6 => &Wx::WXK_F6, f7 => &Wx::WXK_F7, f8 => &Wx::WXK_F8,
89             f9 => &Wx::WXK_F9,f10 => &Wx::WXK_F10,f11 => &Wx::WXK_F11,f12 => &Wx::WXK_F12,
90             numpad_enter => &Wx::WXK_NUMPAD_ENTER
91             );
92 0           for (@cmd){
93 0           $item_data = $list{$_};
94 0 0         next unless exists $item_data->{key};
95 0           $rest = $item_data->{key};
96 0           $kname = '';
97 0           $kcode = 0;
98 0           while (){
99 0           $i = index $rest, '+';
100 0 0         last unless $i > 0;
101 0           $char = lc substr $rest, 0, 1;
102 0 0         if ($char eq 's') {$kname .= $shift; $kcode += 1000}
  0 0          
  0 0          
  0            
103 0           elsif ($char eq 'c') {$kname .= $ctrl; $kcode += 2000}
  0            
104 0           elsif ($char eq 'a') {$kname .= $alt; $kcode += 4000}
105 0           $rest = substr $rest, $i + 1;
106             }
107 0 0         $kname .= exists $k18n->{$rest}
108             ? $k18n->{$rest}
109             : ucfirst $rest;
110 0           $item_data->{key} = $kname;
111 0 0         $kcode += length($rest) == 1
112             ? ord uc $rest
113             : $keycode_map{$rest};
114 0           $item_data->{keycode} = $kcode;
115             }
116             }
117            
118             sub eval_cmd_data {
119 0     0 0   my @cmd = @_;
120 0           my ($item_data, $ico_path);
121 0           for (@cmd){
122 0           my $item_data = $list{$_};
123 0           $item_data->{sub} = $item_data->{call};
124 0 0         $item_data->{sub} =~ tr/()&;/ /d if $item_data->{sub};
125 0           for my $node_type (qw(call state enable)) {
126 0 0         $item_data->{$node_type} = eval 'sub {'.$item_data->{$node_type}.'}'
127             if $item_data->{$node_type};
128             }
129 0 0 0       if ($item_data->{call} and $item_data->{key}){
130 0           $keymap[$item_data->{keycode}] = $item_data->{call};
131             }
132 0 0         next unless $item_data->{icon};
133 0           $item_data->{icon} = Kephra::Config::icon_bitmap($item_data->{icon});
134             }
135             }
136            
137            
138             #
139             # external API - getting cmd date, manipulating content
140             #
141 0 0   0 0   sub new_cmd { replace_cmd(@_) unless exists $list{ $_[0] } }
142             sub new_cmd_list {
143 0     0 0   for (@_) {
144             #new_cmd();
145             }
146             }
147             sub replace_cmd {
148 0     0 0   my ($cmd_id, $properties) = @_;
149 0 0         return unless ref $properties eq 'HASH';
150             # if node exist, copy juste assigned values
151 0 0         if ( exists $list{$cmd_id}) {
152 0           $list{$cmd_id}{$_} = $properties->{$_} for keys %$properties;
153             }
154 0           else { $list{$cmd_id} = $properties }
155 0           numify_key_code($cmd_id);
156 0           eval_cmd_data($cmd_id);
157             }
158            
159 0     0 0   sub del_cmd { delete @list{$_[0]} }
160             sub rename_cmd {
161 0     0 0   my ($old_ID, $new_ID) = @_;
162 0 0 0       return unless $new_ID and ref $list{$old_ID} eq 'HASH';
163 0           $list{$new_ID} = $list{$old_ID};
164 0           del_cmd($old_ID);
165             }
166             sub get_cmd_property { # explicit value of one command
167 0     0 0   my $cmd_id = shift;
168 0           my $leafe = shift;
169 0 0 0       $list{$cmd_id}{$leafe}
170             if ref $list{$cmd_id} eq 'HASH'
171             and exists $list{$cmd_id}{$leafe};
172             }
173             sub get_cmd_properties { # all values of one command
174 0     0 0   my $cmd_id = shift;
175 0 0         $list{$cmd_id} if ref $list{$cmd_id} eq 'HASH';
176             }
177             sub get_property_list { # values of same type from different commands
178 0     0 0   my $property = shift;
179 0           my @result;
180 0           for (@_) {
181 0 0         push @result, $list{$_}{$property} if exists $list{$_}{$property}
182             }
183 0           return @result;
184             }
185            
186             sub run_cmd_by_id {
187 0     0 0   my $cmd_id = shift;
188 0 0         $list{$cmd_id}{call}() if ref $list{$cmd_id}{call} eq 'CODE';
189             }
190            
191             sub run_cmd_by_keycode {
192 0     0 0   my $keycode = shift;
193 0 0         if (ref $keymap[$keycode] eq 'CODE'){
194 0           $keymap[$keycode]();
195 0           return 1;
196             }
197 0           return 0;
198             }
199            
200             sub del_temp_data{
201 0     0 0   my $l18n = Kephra::Config::Localisation::strings();
202 0 0         delete $l18n->{commandlist} if exists $l18n->{commandlist};
203             #delete $Kephra::localisation{key}
204             # if exists $l18n->{key};
205             }
206            
207             =head1 NAME
208            
209             Kephra::API::CommandList - external API for user callable functions
210            
211             =head1 DESCRIPTION
212            
213             The CommandList is a dynamically changeable list, that contains all the
214             function calls for every menu item, toolbar button and most other widget items.
215             It holds also label, help text, key binding, icon and more for each command.
216             All these properties have to be changed globally here in a clean way.
217             These commands where used by different gui elements, that allows menu and
218             toolbar definitions to be very compact, readable and and easy changeable.
219            
220             Names of commands contain dashes as separator of namespaces.
221            
222             =head1 SPECIFICATION
223            
224             CommandlistItem
225            
226             =over 4
227            
228             =item * ID - unique identifier, hashkey, following hash is its value
229            
230             =item * call - CODEREF : actual action, performed when this command is called
231            
232             =item * sub - string : name of the called routine
233            
234             =item * enable - CODEREF : returns enable status (0 for disable)
235            
236             =item * enable_event - string : API::EventTable ID when to check to en/disable
237            
238             =item * state - CODEREF : that returns state value (for switches)
239            
240             =item * state_event - string : API::EventTable ID when to check is state changed
241            
242             =item * label - string : descriptive name
243            
244             =item * help - string : short help sentence
245            
246             =item * key - string : label of key binding
247            
248             =item * keycode - numeric keycode
249            
250             =item * icon - Wx::Bitmap
251            
252             =back
253            
254             =cut
255            
256             1;