File Coverage

lib/Kephra/Edit.pm
Criterion Covered Total %
statement 6 274 2.1
branch 0 104 0.0
condition 0 75 0.0
subroutine 2 42 4.7
pod 0 31 0.0
total 8 526 1.5


line stmt bran cond sub pod time code
1             package Kephra::Edit;
2             our $VERSION = '0.42';
3            
4 1     1   1127 use strict;
  1         2  
  1         38  
5 1     1   7 use warnings;
  1         2  
  1         3825  
6             #
7             # internal helper function
8             #
9 0     0     sub _ep_ref { Kephra::App::EditPanel::_ref() }
10 0     0     sub _keep_focus{ Wx::Window::SetFocus( _ep_ref() ) }
11             sub _let_caret_visible {
12 0     0     my $ep = _ep_ref();
13 0           my ($selstart, $selend) = $ep->GetSelection;
14 0           my $los = $ep->LinesOnScreen;
15 0 0         if ( $selstart == $selend ) {
16 0 0         $ep->ScrollToLine($ep->GetCurrentLine - ( $los / 2 ))
17             unless $ep->GetLineVisible( $ep->GetCurrentLine() );
18             } else {
19 0           my $startline = $ep->LineFromPosition($selstart);
20 0           my $endline = $ep->LineFromPosition($selend);
21 0 0 0       $ep->ScrollToLine( $startline - (($los - $endline - $startline) / 2) )
22             unless $ep->GetLineVisible($startline)
23             and $ep->GetLineVisible($endline);
24             }
25 0           $ep->EnsureCaretVisible;
26             }
27            
28             sub _center_caret {
29 0     0     my $ep = _ep_ref();
30 0           my $line = $ep->GetCurrentLine();
31 0           $ep->ScrollToLine( $line - ( $ep->LinesOnScreen / 2 ));
32 0           $ep->EnsureVisible($line);
33 0           $ep->EnsureCaretVisible();
34             }
35            
36             my @pos_stack;
37             sub _save_positions {
38 0     0     my $ep = _ep_ref();
39 0           my %pos;
40 0           $pos{document} = Kephra::Document::Data::current_nr();
41 0           $pos{pos} = $ep->GetCurrentPos;
42 0           $pos{line} = $ep->GetCurrentLine;
43 0           $pos{col} = $ep->GetColumn( $pos{pos} );
44 0           $pos{sel_begin} = $ep->GetSelectionStart;
45 0           $pos{sel_end} = $ep->GetSelectionEnd;
46 0           push @pos_stack, \%pos;
47             }
48            
49             sub _restore_positions {
50 0     0     my $ep = _ep_ref();
51 0           my %pos = %{ pop @pos_stack };
  0            
52 0 0         if (%pos) {
53 0 0         Kephra::Document::Change::to_number( $pos{document} )
54             if $pos{document} != Kephra::Document::Data::current_nr();
55 0           $ep->SetCurrentPos( $pos{pos} );
56 0 0         $ep->GotoLine( $pos{line} ) if $ep->GetCurrentLine != $pos{line};
57 0 0         if ( $ep->GetColumn( $ep->GetCurrentPos ) == $pos{col} ) {
58 0           $ep->SetSelection( $pos{sel_begin}, $pos{sel_end} );
59             } else {
60 0           my $npos = $ep->PositionFromLine( $pos{line} ) + $pos{col};
61 0           my $max = $ep->GetLineEndPosition( $pos{line} );
62 0 0         $npos = $max if $npos > $max;
63 0           $ep->SetCurrentPos($npos);
64 0           $ep->SetSelection( $npos, $npos );
65             }
66             }
67 0           &_let_caret_visible;
68             }
69            
70             sub _select_all_if_none {
71 0     0     my $ep = _ep_ref();
72 0           my ($start, $end) = $ep->GetSelection;
73 0 0         if ( $start == $end ) {
74 0           $ep->SelectAll;
75 0           ($start, $end) = $ep->GetSelection;
76             }
77 0           return $ep->GetTextRange( $start, $end );
78             }
79            
80             sub _selection_left_to_right {
81 0   0 0     my $ep = shift || _ep_ref();
82 0           my ($start, $end) = $ep->GetSelection;
83 0           my $pos = $ep->GetCurrentPos;
84 0 0         return -1 if $start == $end;
85 0 0         return $start == $pos ? 0 : 1;
86             }
87             sub _nearest_grid_pos { # position in document from given line and column
88 0     0     my $line = shift;
89 0           my $col = shift;
90 0   0       my $ep = shift || _ep_ref();
91 0 0         return unless defined $col;
92              
93             # first staight foreward attempt
94 0           my $lpos = $ep->PositionFromLine($line);
95 0           my $pos = $lpos + $col;
96 0 0 0       return $pos if $ep->GetColumn($pos) == $col
97             and $ep->LineFromPosition($pos) == $line;
98              
99             # if line too short take last pos of line
100 0           my $endpos = $ep->GetLineEndPosition($line);
101 0 0         return $endpos if $ep->GetColumn($endpos) < $col;
102              
103             # if tabs used calculate
104 0           my $ipos = $ep->GetLineIndentation($line);
105 0           my $icol = $ep->GetColumn($ipos);
106 0 0         return $ipos + $col - $icol if $icol <= $col;
107              
108             # if between indenting tabs take neares
109 0           my $tabsize = $ep->GetTabWidth();
110 0           my $tabs = $col / $tabsize;
111 0 0         return ($col % $tabsize < $tabsize / 2) ? $lpos + $tabs : $lpos + $tabs + 1;
112             }
113 0     0 0   sub can_paste { _ep_ref()->CanPaste }
114 0     0 0   sub can_copy { Kephra::Document::Data::attr('text_selected') }
115             #
116             # simple textedit
117             #
118 0     0 0   sub cut { _ep_ref()->Cut }
119             sub copy {
120 0     0 0   my $ep = _ep_ref();
121 0           $ep->Copy;
122 0 0         $ep->SelectionIsRectangle()
123             ? Kephra::Document::Data::set_value('copied_rect_selection',get_clipboard_text())
124             : Kephra::Document::Data::set_value('copied_rect_selection','');
125             }
126             sub paste {
127 0     0 0   my $lch = Kephra::Document::Data::get_value('copied_rect_selection');
128 0           my $cb = get_clipboard_text();
129 0 0 0       (defined $lch and $lch eq $cb) ? paste_rectangular($cb) : _ep_ref()->Paste;
130             }
131             sub paste_rectangular {
132 0   0 0 0   my $text = shift || get_clipboard_text();
133 0   0       my $ep = shift || _ep_ref();
134 0           my $dragpos = shift;
135 0           my $droppos = shift;
136             # all additional parameters have to be provided or no one
137 0 0 0       return -1 if defined $dragpos and not defined $droppos;
138              
139 0           my @lines = split( /[\r\n]+/, $text);
140 0 0         $droppos = $ep->GetCurrentPos unless defined $dragpos;
141 0           my $linenr = $ep->LineFromPosition( $droppos );
142 0           my $colnr = $ep->GetColumn($droppos );
143              
144 0 0         if (defined $dragpos){
145             # calculate real drop position if dragged foreward
146             # because selection is cut out before inserted and this changed droppos
147 0 0         if ($dragpos <= $droppos){
148 0           my $selwidth = length $lines[0];
149 0           my $dnddelta = $linenr - $ep->LineFromPosition( $dragpos );
150 0           my $max = scalar @lines;
151              
152             #$dnddelta = $max < $dnddelta ? $max : $dnddelta;
153             #$dnddelta *= $selwidth;
154             #$droppos -= $dnddelta;
155             #print "$dragpos ---$droppos\n";
156             }
157             }
158              
159 0           $ep->BeginUndoAction;
160 0 0         $ep->ReplaceSelection(''),$ep->SetCurrentPos($droppos) if defined $dragpos;
161              
162 0           my $insertpos;
163 0           for my $line (@lines){
164 0           $insertpos = $ep->PositionFromLine($linenr) + $colnr;
165 0           $insertpos += $colnr - $ep->GetColumn( $insertpos ) ;
166 0 0         $insertpos = $ep->GetLineEndPosition($linenr)
167             if $ep->LineFromPosition( $insertpos ) > $linenr;
168 0           $ep->InsertText( $insertpos, $line);
169 0           $linenr++;
170             }
171 0           $ep->EndUndoAction;
172             }
173             sub replace {
174 0     0 0   my $ep = _ep_ref();
175 0           my $text = get_clipboard_text();
176 0           copy();
177 0           _ep_ref()->ReplaceSelection($text);
178             }
179            
180 0     0 0   sub clear { _ep_ref()->Clear }
181             sub get_clipboard_text {
182 0     0 0   my $cboard = &Wx::wxTheClipboard;
183 0           my $text;
184 0           $cboard->Open;
185 0 0         if ( $cboard->IsSupported( &Wx::wxDF_TEXT ) ) {
186 0           my $data = Wx::TextDataObject->new;
187 0           my $ok = $cboard->GetData( $data );
188 0 0         if ( $ok ) {
189 0           $text = $data->GetText;
190             } else {
191             # todo: error handling
192             }
193             }
194 0           $cboard->Close;
195 0 0         return defined $text ? $text : -1;
196             }
197            
198             sub del_back_tab{
199 0     0 0   my $ep = _ep_ref();
200 0           my $pos = $ep->GetCurrentPos();
201 0           my $tab_size = Kephra::Document::Data::attr('tab_size');
202 0           my $deltaspace = $ep->GetColumn($pos--) % $tab_size;
203 0 0         $deltaspace = $tab_size unless $deltaspace;
204 0   0       do { $ep->CmdKeyExecute(&Wx::wxSTC_CMD_DELETEBACK) }
  0            
205             while $ep->GetCharAt(--$pos) == 32 and --$deltaspace;
206             }
207            
208             #
209             # Edit Selection
210             #
211 0     0 0   sub get_selection { _ep_ref()->GetSelectedText() }
212             sub move_target {
213 0     0 0   my $linedelta = shift;
214 0 0         return unless defined $linedelta;
215 0   0       my $ep = shift || _ep_ref();
216 0           my $targetstart = $ep->GetTargetStart();
217 0           my $targettext = $ep->GetTextRange($targetstart, $ep->GetTargetEnd());
218 0           $ep->BeginUndoAction;
219 0           $ep->ReplaceTarget('');
220 0           $ep->InsertText($targetstart+$linedelta, $targettext);
221 0           $ep->EndUndoAction;
222             }
223             sub move_selection {
224 0     0 0   my $linedelta = shift;
225 0 0         return unless defined $linedelta;
226 0   0       my $ep = shift || _ep_ref();
227 0           my ($selbegin, $selend) = $ep->GetSelection();
228 0           my $targettext = $ep->GetSelectedText();
229 0           $ep->BeginUndoAction;
230 0           $ep->ReplaceSelection('');
231 0           my $pos = $ep->GetCurrentPos;
232 0           $pos += $linedelta;
233 0           $ep->InsertText($pos, $targettext);
234 0           $ep->SetSelection($pos, $pos + $selend - $selbegin);
235 0           $ep->EndUndoAction;
236             }
237             sub move_lines {
238 0     0 0   my $linedelta = shift;
239 0 0         return unless defined $linedelta;
240 0   0       my $ep = shift || _ep_ref();
241              
242 0           my ( $selbegin, $selend) = $ep->GetSelection();
243 0           my $sellength = $selend - $selbegin;
244 0           my $selstartline = $ep->LineFromPosition($selbegin);
245 0           my $targetstart = $ep->GetTargetStart();
246 0           my $targetend = $ep->GetTargetEnd();
247 0           my $blockbegin = $ep->PositionFromLine($selstartline);
248 0           my $blockend = $ep->PositionFromLine( $ep->LineFromPosition($selend)+1 );
249 0           my $selcolumn = $selbegin - $blockbegin;
250              
251             # endmode is taken when last line on start or end of operation has no EOL
252             # then i take the the EOL char from the line before instead and have to
253             # insert in a pos before to keep consistent
254 0           my $endmode;
255 0 0 0       if ($blockend == $ep->GetLength()
256             or $ep->LineFromPosition($selend) + $linedelta >= $ep->GetLineCount()-1 ) {
257 0           $blockbegin = $ep->GetLineEndPosition($selstartline-1);
258 0           $blockend = $ep->GetLineEndPosition( $ep->LineFromPosition($selend) );
259 0           $endmode = 1;
260             }
261 0           $selstartline += $linedelta;
262 0           my $blocktext = $ep->GetTextRange($blockbegin, $blockend);
263 0           $ep->BeginUndoAction;
264 0           $ep->SetTargetStart( $blockbegin );
265 0           $ep->SetTargetEnd( $blockend );
266 0           $ep->ReplaceTarget('');
267 0 0         $selstartline = 0 if $selstartline < 0;
268 0 0         $selstartline = $ep->GetLineCount() if $selstartline > $ep->GetLineCount();
269 0 0         my $target = $endmode
270             ? $ep->GetLineEndPosition($selstartline-1)
271             : $ep->PositionFromLine($selstartline);
272 0           $ep->InsertText($target, $blocktext);
273 0           $selbegin = $ep->PositionFromLine($selstartline) + $selcolumn;
274 0           $ep->SetSelection($selbegin, $selbegin + $sellength);
275 0           $ep->SetTargetStart($targetstart );
276 0           $ep->SetTargetEnd( $targetend );
277 0           $ep->EndUndoAction;
278             }
279            
280             sub selection_move_left {
281 0   0 0 0   my $ep = shift || _ep_ref();
282 0           my ($selbegin, $selend) = $ep->GetSelection();
283 0 0 0       if ( $selbegin == $selend
284             or $ep->LineFromPosition( $selbegin ) != $ep->LineFromPosition( $selend ) ) {
285 0           Kephra::Edit::Format::dedent_tab();
286             }
287             else {
288 0           my $newpos = $ep->WordStartPosition($selbegin, 1);
289 0 0         my $move_delta = $newpos == $selbegin ? -1 : $newpos - $selbegin;
290 0           move_selection( $move_delta );
291             }
292             }
293            
294             sub selection_move_right{
295 0     0 0   my $ep = _ep_ref();
296 0           my ($selbegin, $selend) = $ep->GetSelection();
297 0           my $endline = $ep->LineFromPosition( $selend );
298 0 0 0       if ( $selbegin == $selend or $ep->LineFromPosition( $selbegin ) != $endline ) {
299 0           Kephra::Edit::Format::indent_tab();
300             }
301             else {
302 0           my $newpos = $ep->WordEndPosition($selend, 1);
303 0 0         my $move_delta = $newpos == $selend ? 1 : $newpos - $selend;
304 0 0 0       move_selection( $move_delta )
305             unless $endline == $ep->GetLineCount() - 1
306             and $ep->GetLineEndPosition($endline) == $selend;
307             }
308             }
309            
310             sub selection_move_up {
311 0   0 0 0   my $ep = shift || _ep_ref();
312 0           my ($selbegin, $selend) = $ep->GetSelection();
313 0           my $firstline = $ep->LineFromPosition( $selbegin );
314 0           my $lastline = $ep->LineFromPosition( $selend );
315            
316 0 0 0       if ( $selbegin != $selend and $firstline == $lastline) {
317 0           my $line = $firstline;
318 0           my $col = $ep->GetColumn( $selbegin );
319 0 0         return unless $line;
320 0           $line--;
321 0           move_selection( _nearest_grid_pos($line, $col) - $selbegin );
322             }
323 0           else { move_lines( -1, $ep ) }
324             }
325            
326             sub selection_move_down {
327 0   0 0 0   my $ep = shift || _ep_ref();
328 0           my ($selbegin, $selend) = $ep->GetSelection();
329 0           my $firstline = $ep->LineFromPosition( $selbegin );
330 0           my $lastline = $ep->LineFromPosition( $selend );
331            
332 0 0 0       if ($selbegin != $selend and $firstline == $lastline) {
333 0           my $line = $firstline;
334 0           my $col = $ep->GetColumn( $selbegin );
335 0 0         return if $line+1 == $ep->GetLineCount();
336 0           $line++;
337 0           move_selection( _nearest_grid_pos($line, $col) - $selend );
338             }
339 0           else { move_lines( 1, $ep ) }
340             }
341            
342             sub selection_move_page_up {
343 0   0 0 0   my $ep = shift || _ep_ref();
344 0           my ($selbegin, $selend) = $ep->GetSelection();
345 0           my $firstline = $ep->LineFromPosition( $selbegin );
346 0           my $lastline = $ep->LineFromPosition( $selend );
347 0           my $linedelta = $ep->LinesOnScreen;
348            
349 0 0 0       if ($selbegin != $selend and $firstline == $lastline) {
350 0           my $line = $firstline;
351 0           my $col = $ep->GetColumn( $selbegin );
352 0 0         return unless $line;
353 0           $line -= $linedelta;
354 0 0         $line = 0 if $line < 0;
355 0           move_selection( _nearest_grid_pos($line, $col) - $selbegin );
356             }
357 0           else { move_lines( -$linedelta, $ep ) }
358             }
359            
360             sub selection_move_page_down {
361 0   0 0 0   my $ep = shift || _ep_ref();
362 0           my ($selbegin, $selend) = $ep->GetSelection();
363 0           my $firstline = $ep->LineFromPosition( $selbegin );
364 0           my $lastline = $ep->LineFromPosition( $selend );
365 0           my $linedelta = $ep->LinesOnScreen;
366            
367 0 0 0       if ($selbegin != $selend and $firstline == $lastline) {
368 0           my $line = $firstline;
369 0           my $col = $ep->GetColumn( $selbegin );
370 0 0         return if $line+1 == $ep->GetLineCount();
371 0           $line += $linedelta;
372 0 0         $line = $ep->GetLineCount()-1 if $line >= $ep->GetLineCount();
373 0           move_selection( _nearest_grid_pos($line, $col) - $selend );
374             }
375 0           else { move_lines( $linedelta, $ep ) }
376             }
377            
378             #
379             sub insert {
380 0     0 0   my ($text, $pos) = @_;
381 0 0         return unless $text;
382 0           my $ep = _ep_ref();
383 0 0         $pos = $ep->GetCurrentPos unless defined $pos;
384 0           $ep->InsertText($pos, $text);
385 0           $pos += length $text;
386 0           $ep->SetSelection($pos, $pos);
387             }
388 0     0 0   sub insert_text { insert(@_) }
389 0     0 0   sub insert_at_pos { insert(@_) }
390             #
391             # Edit Line
392             #
393 0     0 0   sub cut_current_line { _ep_ref()->CmdKeyExecute(&Wx::wxSTC_CMD_LINECUT) }
394 0     0 0   sub copy_current_line{ _ep_ref()->CmdKeyExecute(&Wx::wxSTC_CMD_LINECOPY)}
395             sub double_current_line {
396 0     0 0   my $ep = _ep_ref();
397 0           my $pos = $ep->GetCurrentPos;
398 0           $ep->BeginUndoAction;
399 0           $ep->CmdKeyExecute(&Wx::wxSTC_CMD_LINECOPY);
400 0           $ep->CmdKeyExecute(&Wx::wxSTC_CMD_PASTE);
401 0           $ep->GotoPos($pos);
402 0           $ep->EndUndoAction;
403             }
404            
405             sub replace_current_line {
406 0     0 0   my $ep = _ep_ref();
407 0           my $line = $ep->GetCurrentLine;
408 0           $ep->BeginUndoAction;
409 0           $ep->GotoLine($line);
410 0           $ep->Paste;
411 0           $ep->SetSelection(
412             $ep->GetSelectionEnd,
413             $ep->GetLineEndPosition( $ep->GetCurrentLine )
414             );
415 0           $ep->Cut;
416 0           $ep->GotoLine($line);
417 0           $ep->EndUndoAction;
418             }
419            
420 0     0 0   sub del_current_line{_ep_ref()->CmdKeyExecute(&Wx::wxSTC_CMD_LINEDELETE)}
421 0     0 0   sub del_line_left {_ep_ref()->DelLineLeft() }
422 0     0 0   sub del_line_right {_ep_ref()->DelLineRight()}
423            
424 0     0 0   sub eval_newline_sub{}
425             1;
426             __END__