File Coverage

lib/Kephra/Edit/Marker.pm
Criterion Covered Total %
statement 6 177 3.3
branch 0 80 0.0
condition 0 36 0.0
subroutine 2 35 5.7
pod 0 23 0.0
total 8 351 2.2


line stmt bran cond sub pod time code
1             package Kephra::Edit::Marker;
2             our $VERSION = '0.24';
3            
4 1     1   1221 use strict;
  1         3  
  1         31  
5 1     1   6 use warnings;
  1         3  
  1         2736  
6            
7             # internal data handling subs
8 0     0     sub _ep { Kephra::App::EditPanel::_ref() }
9 0     0     sub _config { Kephra::API::settings()->{search}{marker} }
10             my @bookmark;
11             my @bookmark_nr = 0..9;
12             my $marker_nr = 10; # pos remembered by edit control
13 0     0     sub _attribute {'marked_lines'}
14 0     0     sub _bookmarks { @bookmark }
15             sub bookmark_is_set {
16 0     0 0   my $nr = shift;
17 0 0 0       return if $nr < 0 or $nr > 9;
18 0           $bookmark[$nr]{set};
19             }
20             sub _marker_search_byte {
21 0     0     my $search_byte = 1 << $marker_nr;
22 0 0         $search_byte |= (1 << scalar(@bookmark_nr) )-1 if _config()->{any};
23 0           $search_byte;
24             }
25             sub _refresh_bookmark_data { # checks if this bookmark is still valid
26             # refresh or deletes data data if necessary
27 0     0     my $nr = shift;
28 0 0         return unless bookmark_is_set($nr);
29 0           my $bm_data = $bookmark[$nr];
30 0           my $doc_nr = Kephra::Document::Data::validate_doc_nr( $bm_data->{doc_nr} );
31 0 0         $doc_nr = Kephra::Document::Data::nr_from_file_path($bm_data->{file})
32             if Kephra::Document::Data::get_file_path($doc_nr) ne $bm_data->{file};
33 0 0         _delete_bookmark_data($nr), return 0 if $doc_nr == -1;
34 0           $bm_data->{doc_nr} = $doc_nr;
35 0           my $ep = Kephra::Document::Data::_ep($doc_nr);
36 0           my $line = $ep->MarkerNext(0, (1 << $nr) );
37 0 0         _delete_bookmark_data($nr), return 0 if $line == -1;
38 0           my $ll = $ep->LineLength( $line );
39 0 0         if ($bm_data->{col} > $ll) {
40 0           $bm_data->{col} = $ll;
41 0           $bm_data->{pos} = $ep->PositionFromLine( $line ) + $bm_data->{col};
42             }
43 0           return $bm_data->{line} = $line;
44             }
45            
46 0     0     sub _refresh_all_bookmarks { _refresh_bookmark_data($_) for @bookmark_nr }
47             sub _delete_bookmark_data {
48 0     0     my $nr = shift;
49 0 0 0       return if $nr < 0 or $nr > 9;
50 0           $bookmark[$nr] = {};
51             }
52            
53             sub _get_pos {# switch: was command triggered from context menu or key/main menu
54 0     0     my $ep = _ep();
55 0           my $line = Kephra::App::EditPanel::Margin::clicked_on_line();
56 0 0         return $line > -1 ? $ep->PositionFromLine($line) : $ep->GetCurrentPos();
57             }
58             sub _get_line {# switch: was command triggered from context menu or key/main menu
59 0     0     my $line = Kephra::App::EditPanel::Margin::clicked_on_line();
60 0 0         return $line > -1 ? $line : _ep()->GetCurrentLine();
61             }
62             #
63             # external API
64             #
65             sub define_marker {
66 0     0 0   my $ep = shift;
67 0           my $conf = Kephra::App::EditPanel::Margin::_marker_config();
68            
69 0           my $color = \&Kephra::Config::color;
70 0           my $fore = &$color( $conf->{fore_color} );
71 0           my $back = &$color( $conf->{back_color} );
72             $ep->MarkerDefineBitmap
73             ( $_, Kephra::CommandList::get_cmd_property
74 0           ( 'bookmark-goto-'.$_, 'icon' ) ) for @bookmark_nr;
75 0           $ep->MarkerDefineBitmap( $marker_nr,
76             Kephra::CommandList::get_cmd_property('marker-toggle-here', 'icon'));
77             }
78            
79             sub delete_doc {
80 0     0 0   my $doc_nr = shift;
81 0           delete_all_bookmarks_in_doc($doc_nr);
82 0           delete_all_marker_in_doc($doc_nr);
83             }
84             # bookmarks
85             sub restore_bookmarks {
86 0     0 0   my $bookmark_data = shift;
87 0           for my $nr (@bookmark_nr) {
88 0 0         if ( defined $bookmark_data->{$nr}){
89 0           my $this_bm = $bookmark_data->{$nr};
90 0 0 0       next unless ref $this_bm eq 'HASH' and $this_bm->{file} and $this_bm->{pos};
      0        
91 0           $bookmark[$nr]{file} = $this_bm->{file};
92 0           my $bookmark = $bookmark[$nr];
93 0           my $doc_nr = $bookmark->{doc_nr} =
94             Kephra::Document::Data::nr_from_file_path( $this_bm->{file} );
95 0 0         next if $doc_nr < 0;
96 0           my $ep = Kephra::Document::Data::_ep($doc_nr);
97 0           my $pos = $bookmark->{pos} = $this_bm->{pos};
98 0           my $line = $bookmark->{line} = $ep->LineFromPosition( $pos );
99 0           $bookmark->{col} = $pos - $ep->PositionFromLine( $line );
100 0 0         $bookmark->{set} = 1 if $ep->MarkerAdd( $line, $nr ) > -1;
101             }
102             }
103             }
104            
105             sub get_bookmark_data {
106 0     0 0   _refresh_bookmark_data($_) for @bookmark_nr;
107 0           my %bm_data;
108 0           for my $nr (@bookmark_nr) {
109 0 0         next unless bookmark_is_set($nr);
110 0           $bm_data{$nr}{file} = $bookmark[$nr]{file};
111 0           $bm_data{$nr}{pos} = $bookmark[$nr]{pos};
112             }
113 0           \%bm_data;
114             }
115            
116             sub toggle_bookmark_in_pos {
117 0     0 0   my $nr = shift;
118 0           my $pos = shift;
119 0           my $ep = _ep();
120 0           my $line = $ep->LineFromPosition($pos);
121             # if bookmark is not in current line it will be set
122 0           my $marker_in_line = (1 << $nr) & $ep->MarkerGet($line);
123 0           delete_bookmark($nr);
124 0 0         unless ($marker_in_line) {
125 0           my $bookmark = $bookmark[$nr];
126 0           $bookmark->{file} = Kephra::Document::Data::file_path();
127 0           $bookmark->{pos} = $pos;
128 0           $bookmark->{doc_nr} = Kephra::Document::Data::current_nr();
129 0           $bookmark->{col} = $pos - $ep->PositionFromLine($line);
130 0           $bookmark->{line} = $line;
131 0 0         $bookmark->{set} = 1 if $ep->MarkerAdd( $line, $nr) > -1;
132             }
133             }
134             sub toggle_bookmark_here { # toggle triggered by margin middle click
135 0     0 0   my ($ep, $event ) = @_;
136 0 0         return unless ref $event eq 'Wx::MouseEvent';
137              
138            
139 0           my $pos = $ep->PositionFromPoint( $event->GetPosition() );
140 0           my $marker = $ep->MarkerGet($ep->LineFromPosition($pos) );
141 0 0         if ( $marker & ((1 << 10)-1) ){
142 0           for my $nr (@bookmark_nr) { # delete bookmarks in this line
143 0 0         delete_bookmark($nr) if $marker & (1 << $nr)
144             }
145             } else {
146 0           for my $nr (@bookmark_nr) { # set a free bookmark with lowest number
147 0 0         return toggle_bookmark_in_pos($nr, $pos) unless bookmark_is_set($nr);
148             }
149             }
150             }
151             sub toggle_bookmark { # toggle command, triggered from macro, key, [context] menu
152 0     0 0   toggle_bookmark_in_pos(shift, _get_pos() );
153             }
154             sub goto_bookmark {
155 0     0 0   my $nr = shift;
156 0 0         if ( _refresh_bookmark_data($nr) ) {
157 0           Kephra::Document::Change::to_nr( $bookmark[$nr]{doc_nr} );
158 0           Kephra::Edit::Goto::pos( $bookmark[$nr]{pos} );
159             }
160             }
161            
162             sub delete_bookmark {
163 0     0 0   my $nr = shift;
164 0 0         if ( _refresh_bookmark_data( $nr ) ){
165 0           my $ep = Kephra::Document::Data::_ep( $bookmark[$nr]->{doc_nr} );
166 0           $ep->MarkerDeleteAll($nr);
167 0           _delete_bookmark_data($nr);
168             }
169             }
170             sub delete_all_bookmarks_in_doc {
171 0     0 0   my $cnr = Kephra::Document::Data::current_nr();
172 0           for my $nr (@bookmark_nr) {
173 0           _refresh_bookmark_data( $nr );
174 0 0         next unless bookmark_is_set($nr);
175 0 0         delete_bookmark($nr) if $bookmark[$nr]->{doc_nr} eq $cnr;
176             }
177             }
178 0     0 0   sub delete_all_bookmarks { delete_bookmark($_) for @bookmark_nr }
179            
180             # marker
181             sub restore {
182 0     0 0   my $doc_nr = shift;
183 0           my $marker_pos = Kephra::Document::Data::get_attribute( _attribute(), $doc_nr);
184 0 0         return unless ref $marker_pos eq 'ARRAY';
185 0           my $ep = Kephra::Document::Data::_ep($doc_nr);
186 0           $ep->MarkerAdd( $_, $marker_nr) for @$marker_pos;
187             }
188            
189             sub store { # update marker pos in the file data, saved later in File:Session
190             # bookmarks are saved by Kephra::Edit::Search::save_search_data()
191 0     0 0   my $search_byte = 1 << $marker_nr;
192 0           for my $doc_nr (@{Kephra::Document::Data::all_nr()}) {
  0            
193 0           my $ep = Kephra::Document::Data::_ep($doc_nr);
194 0           my $line = 0;
195 0           my @marker_pos;
196 0           push @marker_pos, $line++
197             while -1 != ( $line = $ep->MarkerNext( $line, $search_byte ) );
198 0           Kephra::Document::Data::set_attribute( _attribute(), \@marker_pos, $doc_nr);
199             }
200             }
201            
202             sub toggle_marker_in_line { # generic set / delete marker in line
203 0     0 0   my $line = shift;
204 0           my $ep = _ep();
205 0 0         ($ep->MarkerGet($line) & (1 << $marker_nr))
206             ? $ep->MarkerDelete( $line, $marker_nr)
207             : $ep->MarkerAdd( $line, $marker_nr);
208             }
209            
210             sub toggle_marker_here { # toggle triggered by margin left click
211 0     0 0   my ($ep, $event ) = @_;
212 0 0         return unless ref $event eq 'Wx::MouseEvent';
213             #$ep->LineFromPosition( $event->GetPosition() if ref $event eq 'Wx::StyledTextEvent'
214            
215 0           toggle_marker_in_line(
216             $ep->LineFromPosition( $ep->PositionFromPoint( $event->GetPosition() ) )
217             );
218             }
219            
220             sub toggle_marker { # toggle triggered by keyboard / icon / contextmenu
221 0     0 0   toggle_marker_in_line( _get_line() );
222             }
223            
224             sub goto_prev_marker_in_doc {
225 0     0 0   my $ep = _ep();
226 0           my $do_wrap = _config()->{wrap};
227 0           my $search_byte = _marker_search_byte();
228 0           my $line = $ep->MarkerPrevious( $ep->GetCurrentLine - 1, $search_byte );
229 0 0 0       $line = $ep->MarkerPrevious( $ep->GetLineCount(), $search_byte )
230             if $line == -1 and $do_wrap;
231 0 0         Kephra::Edit::Goto::line_nr( $line ) if $line > -1;
232             }
233            
234             sub goto_next_marker_in_doc {
235 0     0 0   my $ep = _ep();
236 0           my $do_wrap = _config()->{wrap};
237 0           my $search_byte = _marker_search_byte();
238 0           my $line = $ep->MarkerNext( $ep->GetCurrentLine + 1, $search_byte );
239 0 0 0       $line = $ep->MarkerNext( 0, $search_byte )
240             if $line == -1 and $do_wrap;
241 0 0         Kephra::Edit::Goto::line_nr( $line ) if $line > -1;
242             }
243            
244             sub goto_prev_marker {
245 0     0 0   my $search_byte = _marker_search_byte();
246 0           my $ep = _ep();
247 0           my $line = my $cur_line =
248             $ep->MarkerPrevious( $ep->GetCurrentLine() - 1, $search_byte );
249 0 0         if ($line > -1) { Kephra::Edit::Goto::line_nr( $line ) }
  0            
250             else {
251 0           my $do_wrap = _config()->{wrap};
252 0           my $doc_nr = my $cur_doc = Kephra::Document::Data::current_nr();
253 0           while ( ($doc_nr = Kephra::Document::Data::next_nr(-1, $doc_nr)) != -1 ){
254 0 0 0       return if $cur_doc < $doc_nr and not $do_wrap;
255 0           $ep = Kephra::Document::Data::_ep($doc_nr);
256 0           $line = $ep->MarkerPrevious( $ep->GetLineCount(), $search_byte );
257 0 0 0       return if ($doc_nr == $cur_doc)
      0        
258             and ($line == $cur_line or $line == -1);
259 0 0         if ($line > -1) {
260 0           Kephra::Document::Change::to_number( $doc_nr );
261 0           return Kephra::Edit::Goto::line_nr( $line );
262             }
263             }
264             }
265             }
266            
267             sub goto_next_marker {
268 0     0 0   my $search_byte = _marker_search_byte();
269 0           my $ep = _ep();
270 0           my $line = my $cur_line =
271             $ep->MarkerNext( $ep->GetCurrentLine() + 1, $search_byte );
272 0 0         if ($line > -1) { Kephra::Edit::Goto::line_nr( $line ) }
  0            
273             else {
274 0           my $do_wrap = _config()->{wrap};
275 0           my $doc_nr = my $cur_doc = Kephra::Document::Data::current_nr();
276 0           while ( ($doc_nr = Kephra::Document::Data::next_nr(-1, $doc_nr)) != -1 ){
277 0 0 0       return if $cur_doc > $doc_nr and not $do_wrap;
278 0           $ep = Kephra::Document::Data::_ep($doc_nr);
279 0           $line = $ep->MarkerNext( 0, $search_byte );
280 0 0 0       return if ($doc_nr == $cur_doc)
      0        
281             and ($line == $cur_line or $line == -1);
282 0 0         if ($line > -1) {
283 0           Kephra::Document::Change::to_number( $doc_nr );
284 0           return Kephra::Edit::Goto::line_nr( $line );
285             }
286             }
287             }
288             }
289            
290             sub delete_all_marker_in_doc {
291 0     0 0   my $doc_nr = Kephra::Document::Data::valid_or_current_doc_nr(shift);
292 0           my $ep = Kephra::Document::Data::_ep($doc_nr);
293 0           $ep->MarkerDeleteAll($marker_nr);
294             }
295             sub delete_all_marker {
296 0     0 0   $_->MarkerDeleteAll($marker_nr) for @{Kephra::Document::Data::get_all_ep()};
  0            
297             }
298            
299             1;
300            
301             =head1 NAME
302            
303             Kephra::Edit::Marker - bookmark and marker functions
304            
305             =head1 DESCRIPTION
306            
307             Marker are position in the document, that are marked by symbols in the margin
308             on the left side. Every document can have many Marker. They can be navigated
309             via [Alt+][Shift+]F2 or search bar.
310            
311             But there are only 10 bookmarks numbered from 0 to 9.
312            
313             =cut