File Coverage

lib/Kephra/App/EditPanel.pm
Criterion Covered Total %
statement 6 235 2.5
branch 0 118 0.0
condition 0 64 0.0
subroutine 2 61 3.2
pod 0 40 0.0
total 8 518 1.5


line stmt bran cond sub pod time code
1             package Kephra::App::EditPanel;
2             our $VERSION = '0.14';
3              
4 1     1   1244 use strict;
  1         2  
  1         39  
5 1     1   6 use warnings;
  1         3  
  1         4506  
6             #
7             # internal API to config und app pointer
8             #
9             my $ref;
10             my %mouse_commands;
11 0     0     sub _ref { $ref }
12 0 0   0     sub _set_ref { $ref = $_[0] if is($_[0]) }
13 0     0     sub _all_ref { Kephra::Document::Data::get_all_ep() }
14 0 0   0 0   sub is { 1 if ref $_[0] eq 'Wx::StyledTextCtrl'}
15 0     0     sub _config { Kephra::API::settings()->{editpanel} }
16             # splitter_pos
17             sub new {
18 0     0 0   my $ep = Wx::StyledTextCtrl->new( Kephra::App::Window::_ref() );
19 0 0         $ep->DragAcceptFiles(1) if Wx::wxMSW();
20 0           return $ep;
21             }
22 0 0   0 0   sub gets_focus { Wx::Window::SetFocus( _ref() ) if is( _ref() ) }
23              
24             # general settings
25             sub apply_settings_here {
26 0   0 0 0   my $ep = shift || _ref() || _create();
27 0 0         return unless is($ep);
28 0           my $conf = _config();
29              
30 0           load_font($ep);
31              
32             # indicators: caret, selection, whitespaces...
33 0           Kephra::App::EditPanel::Indicator::apply_all_here($ep);
34              
35             # Margins on left side
36 0           Kephra::App::EditPanel::Margin::apply_settings_here($ep);
37              
38             #misc: scroll width, codepage, wordchars
39 0           apply_autowrap_settings_here($ep);
40              
41 0 0         $ep->SetScrollWidth($conf->{scroll_width})
42             unless $conf->{scroll_width} eq 'auto';
43              
44             #wxSTC_CP_UTF8 Wx::wxUNICODE()
45 0           $ep->SetCodePage(65001);#
46 0           set_word_chars_here($ep);
47              
48             # internal
49 0           $ep->SetLayoutCache(&Wx::wxSTC_CACHE_PAGE);
50 0           $ep->SetBufferedDraw(1);
51 0 0         $conf->{contextmenu}{visible} eq 'default' ? $ep->UsePopUp(1) : $ep->UsePopUp(0);
52              
53 0           Kephra::Edit::eval_newline_sub();
54 0           Kephra::Edit::Marker::define_marker($ep);
55 0           connect_events($ep);
56             Kephra::EventTable::add_call ( 'editpanel.focus', 'editpanel', sub {
57 0 0   0     Wx::Window::SetFocus( _ref() ) unless $Kephra::temp{dialog}{active};
58 0 0         }, __PACKAGE__ ) if $conf->{auto}{focus};
59             Kephra::EventTable::add_call( 'document.text.change', 'update_edit_pos', sub {
60 0     0     Kephra::Document::Data::attr('edit_pos', _ref()->GetCurrentPos());
61 0           }, __PACKAGE__);
62             }
63              
64             sub connect_events {
65 0   0 0 0   my $ep = shift || _ref();
66 0           my $trigger = \&Kephra::EventTable::trigger;
67 0           my $config = _config();
68 0           my $selection;
69             my $rectangular_mode;
70 0           my ($dragpos,$droppos);
71              
72             # override sci presets
73 0           Wx::Event::EVT_DROP_FILES ($ep, \&Kephra::File::add_dropped);
74             Wx::Event::EVT_STC_START_DRAG ($ep, -1, sub {
75 0     0     my ( $ep, $event) = @_;
76 0           $dragpos = $ep->GetCurrentPos();
77 0           $selection = $ep->GetSelectedText();
78 0           $rectangular_mode = $ep->SelectionIsRectangle();
79 0           $event->Skip;
80 0           });
81 0     0     Wx::Event::EVT_STC_DRAG_OVER ($ep, -1, sub { $droppos = $_[1]->GetPosition });
  0            
82             Wx::Event::EVT_STC_DO_DROP ($ep, -1, sub {
83 0     0     my ( $ep, $event) = @_;
84 0 0         $rectangular_mode
85             ? Kephra::Edit::paste_rectangular($selection, $ep, $dragpos, $droppos)
86             : $event->Skip;
87 0           });
88              
89 0     0     Wx::Event::EVT_ENTER_WINDOW ($ep, sub { &$trigger('editpanel.focus')} );
  0            
90             Wx::Event::EVT_LEFT_DOWN ($ep, sub {
91 0     0     my ($ep, $event) = @_;
92 0           my $nr = Kephra::App::EditPanel::Margin::in_nr( $event->GetX, $ep );
93 0 0         if ($nr == -1) { Kephra::Edit::copy() if clicked_on_selection($event) }
  0 0          
94 0           else { Kephra::App::EditPanel::Margin::on_left_click($ep, $event, $nr) }
95              
96 0           $event->Skip;
97 0           });
98             Wx::Event::EVT_MIDDLE_DOWN ($ep, sub {
99 0     0     my ($ep, $event) = @_;
100 0           my $nr = Kephra::App::EditPanel::Margin::in_nr( $event->GetX, $ep );
101             # click is above text area
102 0 0         if ($nr == -1) {
103 0 0         if ($event->LeftIsDown){
104 0           Kephra::Edit::paste();
105 0           set_caret_on_cursor($event);
106             }
107             # just middle clicked
108             else {
109 0 0         if ($ep->GetSelectedText){
110 0 0         if (clicked_on_selection($event, $ep)) {
111 0           Kephra::Edit::Search::set_selection_as_find_item();
112 0           Kephra::Edit::Search::find_next();
113             }
114 0           else { insert_selection_at_cursor($event, $ep) }
115             }
116 0           else { Kephra::Edit::Goto::last_edit() }
117             }
118             }
119 0           else { Kephra::App::EditPanel::Margin::on_middle_click($ep, $event, $nr) }
120 0           });
121             Wx::Event::EVT_RIGHT_DOWN ($ep, sub {
122 0     0     my ($ep, $event) = @_;
123 0           my $nr = Kephra::App::EditPanel::Margin::in_nr( $event->GetX, $ep );
124 0 0         if ($nr == -1) {
  0            
125 0 0         if ($event->LeftIsDown){
126 0 0         Kephra::Edit::_selection_left_to_right($ep)
127             ? Kephra::Edit::cut()
128             : Kephra::Edit::clear();
129 0           set_caret_on_cursor($event);
130             } else {
131 0           my $mconf = $config->{contextmenu};
132 0 0         if ($mconf->{visible} eq 'custom'){
133 0 0         my $menu_id = $ep->GetSelectedText
134             ? $mconf->{ID_selection} : $mconf->{ID_normal};
135 0           my $menu = Kephra::App::ContextMenu::get($menu_id);
136 0 0         $ep->PopupMenu($menu, $event->GetX, $event->GetY) if $menu;
137             }
138             }
139             } else {Kephra::App::EditPanel::Margin::on_right_click($ep, $event, $nr)}
140 0           });
141             #Wx::EVT_SET_FOCUS ($ep, sub {});
142 0           Wx::Event::EVT_STC_SAVEPOINTREACHED($ep, -1, \&Kephra::File::savepoint_reached);
143 0           Wx::Event::EVT_STC_SAVEPOINTLEFT($ep, -1, \&Kephra::File::savepoint_left);
144             # -DEP
145             #Wx::Event::EVT_STC_MARGINCLICK ($ep, -1, \&Kephra::App::EditPanel::Margin::on_left_click);
146 0     0     Wx::Event::EVT_STC_CHANGE ($ep, -1, sub {&$trigger('document.text.change')} );
  0            
147             Wx::Event::EVT_STC_UPDATEUI ($ep, -1, sub {
148 0     0     my ( $ep, $event) = @_;
149 0           my ( $sel_beg, $sel_end ) = $ep->GetSelection;
150 0           my $is_sel = $sel_beg != $sel_end;
151 0           my $was_sel = Kephra::Document::Data::attr('text_selected');
152 0           Kephra::Document::Data::attr('text_selected', $is_sel);
153 0 0 0       &$trigger('document.text.select') if $is_sel xor $was_sel;
154 0           &$trigger('caret.move');
155 0           });
156              
157             Wx::Event::EVT_KEY_DOWN ($ep, sub {
158 0     0     my ($ep, $event) = @_;
159             #$ep = _ref();
160 0           my $key = $event->GetKeyCode +
161             1000 * ($event->ShiftDown + $event->ControlDown*2 + $event->AltDown*4);
162             # reacting on shortkeys that are defined in the Commanlist
163             #print "$key\n";
164 0 0         return if Kephra::CommandList::run_cmd_by_keycode($key);
165             # reacting on Enter
166 0 0         if ($key == &Wx::WXK_RETURN) {
167 0 0         if ($config->{auto}{brace}{indention}) {
168 0           my $pos = $ep->GetCurrentPos - 1;
169 0           my $char = $ep->GetCharAt($pos);
170 0 0         if ($char == 123) {
    0          
171 0           return Kephra::Edit::Format::blockindent_open($pos);
172             } elsif ($char == 125) {
173 0           return Kephra::Edit::Format::blockindent_close($pos);
174             }
175             }
176 0 0         $config->{auto}{indention}
177             ? Kephra::Edit::Format::autoindent()
178             : $ep->CmdKeyExecute(&Wx::wxSTC_CMD_NEWLINE) ;
179             }
180             # scintilla handles the rest of the shortkeys
181 0           else { $event->Skip }
182             #SCI_SETSELECTIONMODE
183             #($key == 350){use Kephra::Ext::Perl::Syntax; Kephra::Ext::Perl::Syntax::check()};
184 0           });
185             }
186             sub create_mouse_binding {
187 0     0 0   my @cmd = qw(left-middle left-right left-selection
188             middle middle-selected middle-selection
189             );
190              
191 0 0         if (_config()->{control}{use_mouse_function}) {
192 0           my $config = _config()->{control}{mouse_function};
193             $mouse_commands{$_} = Kephra::Macro::create_from_cmd_list($config->{$_})
194 0           for @cmd
195             }
196 0     0     else { $mouse_commands{$_} = sub {} for @cmd }
  0            
197             }
198             sub set_caret_on_cursor {
199 0     0 0   my $event = shift;
200 0   0       my $ep = shift || _ref();
201 0 0 0       return unless ref $event eq 'Wx::MouseEvent' and is($ep);
202 0           my $pos = cursor_2_caret_pos($event, $ep);
203 0 0         $pos = $ep->GetCurrentPos() if $pos == -1;
204 0           $ep->SetSelection( $pos, $pos );
205             }
206             sub clicked_on_selection {
207 0     0 0   my $event = shift;
208 0   0       my $ep = shift || _ref();
209 0 0 0       return unless ref $event eq 'Wx::MouseEvent' and is($ep);
210 0           my ($start, $end) = $ep->GetSelection();
211 0           my $pos = cursor_2_caret_pos($event, $ep);
212 0 0 0       return 1 if $start != $end and $pos >= $start and $pos <= $end;
      0        
213             }
214             sub insert_selection_at_cursor {
215 0     0 0   my $event = shift;
216 0   0       my $ep = shift || _ref();
217 0           my $pos = cursor_2_caret_pos($event, $ep);
218 0 0         Kephra::Edit::insert_text($ep->GetSelectedText(), $pos) if $pos > -1;
219             }
220             sub cursor_2_caret_pos {
221 0     0 0   my $event = shift;
222 0   0       my $ep = shift || _ref();
223 0 0 0       return -1 unless ref $event eq 'Wx::MouseEvent' and is($ep);
224 0           my $pos = $ep->PositionFromPointClose($event->GetX, $event->GetY);
225 0 0         if ($pos == -1) {
226 0           my $width = Kephra::App::EditPanel::Margin::width($ep)
227             + Kephra::App::EditPanel::Margin::get_text_width();
228 0           my $y = $event->GetY;
229 0           my $line = $ep->LineFromPosition( $ep->PositionFromPointClose($width, $y) );
230 0           $pos = $ep->GetLineEndPosition ($line);
231 0           my $font_size = _config()->{font}{size};
232 0 0 0       if ($line == 0 and $y > $font_size + 12) {
233 0           my $lcc = 0;
234 0           $pos = $ep->PositionFromPointClose($width-1, $y);
235 0   0       while ($pos == -1 and $lcc < $ep->GetLineCount() ){
236 0           $lcc++; # line counter
237 0           $y += $font_size;
238 0           $pos = $ep->PositionFromPointClose($width, $y);
239             }
240 0 0         return -1 if $pos == -1;
241 0           return $ep->PositionFromLine( $ep->LineFromPosition($pos) - $lcc );
242             }
243             }
244 0           $pos;
245             }
246              
247             sub disconnect_events {
248 0   0 0 0   my $ep = shift || _ref();
249 0     0     Wx::Event::EVT_STC_CHANGE ($ep, -1, sub {});
  0            
250 0     0     Wx::Event::EVT_STC_UPDATEUI($ep, -1, sub {});
  0            
251             }
252              
253 0     0 0   sub set_contextmenu_custom { set_contextmenu('custom') }
254 0     0 0   sub set_contextmenu_default { set_contextmenu('default')}
255 0     0 0   sub set_contextmenu_none { set_contextmenu('none') }
256             sub set_contextmenu {
257 0     0 0   my $mode = shift;
258 0 0         $mode = 'custom' unless $mode;
259 0           my $ep = _ref();
260 0 0         $mode eq 'default' ? $ep->UsePopUp(1) : $ep->UsePopUp(0);
261 0           _config()->{contextmenu}{visible} = $mode;
262             }
263 0     0 0   sub get_contextmenu { _config()->{contextmenu}{visible} }
264             #
265 0     0 0   sub set_word_chars { set_word_chars_here($_) for @{_all_ref()} }
  0            
266             sub set_word_chars_here {
267 0   0 0 0   my $ep = shift || _ref();
268 0           my $conf = _config();
269 0 0         if ( $conf->{word_chars} ) {
270 0           $ep->SetWordChars( $conf->{word_chars} );
271             } else {
272 0           $ep->SetWordChars( '$%&-@_abcdefghijklmnopqrstuvwxyzäöüßABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ0123456789' );
273             }
274             }
275              
276              
277             # line wrap
278 0     0 0   sub apply_autowrap_settings { apply_autowrap_settings_here($_) for @{_all_ref()} }
  0            
279             sub apply_autowrap_settings_here {
280 0   0 0 0   my $ep = shift || _ref();
281 0           $ep->SetWrapMode( _config()->{line_wrap} );
282 0           Kephra::EventTable::trigger('editpanel.autowrap');
283             }
284              
285 0     0 0   sub get_autowrap_mode { _config()->{line_wrap} == &Wx::wxSTC_WRAP_WORD}
286             sub switch_autowrap_mode {
287 0 0   0 0   _config()->{line_wrap} = get_autowrap_mode()
288             ? &Wx::wxSTC_WRAP_NONE
289             : &Wx::wxSTC_WRAP_WORD;
290 0           apply_autowrap_settings();
291             }
292              
293             # font settings
294             sub load_font {
295 0   0 0 0   my $ep = shift || _ref();
296 0           my ( $fontweight, $fontstyle ) = ( &Wx::wxNORMAL, &Wx::wxNORMAL );
297 0           my $font = _config()->{font};
298 0 0         $fontweight = &Wx::wxLIGHT if $font->{weight} eq 'light';
299 0 0         $fontweight = &Wx::wxBOLD if $font->{weight} eq 'bold';
300 0 0         $fontstyle = &Wx::wxSLANT if $font->{style} eq 'slant';
301 0 0         $fontstyle = &Wx::wxITALIC if $font->{style} eq 'italic';
302 0           my $wx_font = Wx::Font->new( $font->{size}, &Wx::wxDEFAULT,
303             $fontstyle, $fontweight, 0, $font->{family} );
304 0 0         $ep->StyleSetFont( &Wx::wxSTC_STYLE_DEFAULT, $wx_font ) if $wx_font->Ok > 0;
305             }
306             sub change_font {
307 0     0 0   my ( $fontweight, $fontstyle ) = ( &Wx::wxNORMAL, &Wx::wxNORMAL );
308 0           my $font_config = _config()->{font};
309 0 0         $fontweight = &Wx::wxLIGHT if ( $$font_config{weight} eq 'light' );
310 0 0         $fontweight = &Wx::wxBOLD if ( $$font_config{weight} eq 'bold' );
311 0 0         $fontstyle = &Wx::wxSLANT if ( $$font_config{style} eq 'slant' );
312 0 0         $fontstyle = &Wx::wxITALIC if ( $$font_config{style} eq 'italic' );
313 0           my $oldfont = Wx::Font->new( $$font_config{size}, &Wx::wxDEFAULT, $fontstyle,
314             $fontweight, 0, $$font_config{family} );
315 0           my $newfont = Kephra::Dialog::get_font( $oldfont );
316              
317 0 0         if ( $newfont->Ok > 0 ) {
318 0           ($fontweight, $fontstyle) = ($newfont->GetWeight, $newfont->GetStyle);
319 0           $$font_config{size} = $newfont->GetPointSize;
320 0           $$font_config{family} = $newfont->GetFaceName;
321 0           $$font_config{weight} = 'normal';
322 0 0         $$font_config{weight} = 'light' if $fontweight == &Wx::wxLIGHT;
323 0 0         $$font_config{weight} = 'bold' if $fontweight == &Wx::wxBOLD;
324 0           $$font_config{style} = 'normal';
325 0 0         $$font_config{style} = 'slant' if $fontstyle == &Wx::wxSLANT;
326 0 0         $$font_config{style} = 'italic' if $fontstyle == &Wx::wxITALIC;
327 0           Kephra::Document::SyntaxMode::reload($_) for @{Kephra::Document::Data::all_nr()};
  0            
328 0           Kephra::App::EditPanel::Margin::apply_line_number_width();
329             }
330             }
331             #
332             sub zoom_in {
333 0   0 0 0   my $ep = shift || _ref();
334 0 0         $ep->SetZoom( $ep->GetZoom()+1 ) if $ep->GetZoom() < 45;
335             }
336             sub zoom_out {
337 0   0 0 0   my $ep = shift || _ref();
338 0 0         $ep->SetZoom( $ep->GetZoom()-1 ) if $ep->GetZoom() > -10;
339             }
340             sub zoom_normal {
341 0   0 0 0   my $ep = shift || _ref();
342 0           $ep->SetZoom( 0 ) ;
343             }
344             #
345             # auto indention
346 0     0 0   sub get_autoindention { Kephra::App::EditPanel::_config()->{auto}{indention} }
347             sub set_autoindention {
348 0     0 0   Kephra::App::EditPanel::_config()->{auto}{indention} = shift;
349 0           Kephra::Edit::eval_newline_sub();
350             }
351 0     0 0   sub switch_autoindention { set_autoindention( get_autoindention() ^ 1 ) }
352 0     0 0   sub set_autoindent_on { set_autoindention( 1 ) }
353 0     0 0   sub set_autoindent_off { set_autoindention( 0 ) }
354             #
355             # brace indention
356 0     0 0   sub get_braceindention { Kephra::App::EditPanel::_config()->{auto}{brace}{indention}}
357             sub set_braceindention {
358 0     0 0   Kephra::App::EditPanel::_config()->{auto}{brace}{indention} = shift;
359 0           Kephra::Edit::eval_newline_sub();
360             }
361 0     0 0   sub switch_braceindention { set_braceindention( get_braceindention() ^ 1 ) }
362 0     0 0   sub set_braceindent_on { set_braceindention( 1 ) }
363 0     0 0   sub set_braceindent_off { set_braceindention( 0 ) }
364             #
365             #
366 0     0 0   sub get_bracecompletion { Kephra::App::EditPanel::_config()->{auto}{brace}{make} }
367             sub set_bracecompletion {
368 0     0 0   Kephra::App::EditPanel::_config()->{auto}{brace}{make} = shift;
369             }
370 0     0 0   sub switch_bracecompletion{ set_bracecompletion( get_bracecompletion() ^ 1 ) }
371             1;
372             #EVT_STC_CHARADDED EVT_STC_MODIFIED
373             #wxSTC_CP_UTF8 wxSTC_CP_UTF16 Wx::wxUNICODE()
374             #wxSTC_WS_INVISIBLE wxSTC_WS_VISIBLEALWAYS
375             #$ep->StyleSetForeground (wxSTC_STYLE_CONTROLCHAR, Wx::Colour->new(0x55, 0x55, 0x55));
376             #$ep->CallTipShow(3,"testtooltip\n next line"); #tips
377             #SetSelectionMode(wxSTC_SEL_RECTANGLE);