File Coverage

blib/lib/Padre/Plugin/SpellCheck/Checker.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Padre::Plugin::SpellCheck::Checker;
2              
3 1     1   40295 use v5.10;
  1         4  
  1         49  
4 1     1   5 use warnings;
  1         2  
  1         26  
5 1     1   5 use strict;
  1         2  
  1         33  
6              
7 1     1   1228 use Encode;
  1         17498  
  1         107  
8 1     1   873 use Padre::Logger;
  0            
  0            
9             use Padre::Locale ();
10             use Padre::Unload ();
11             use Padre::Plugin::SpellCheck::FBP::Checker ();
12              
13             our $VERSION = '1.33';
14             use parent qw(
15             Padre::Plugin::SpellCheck::FBP::Checker
16             Padre::Plugin
17             );
18              
19             use Class::Accessor 'antlers';
20             has _autoreplace => ( is => 'rw' ); # list of automatic replaces
21             has _engine => ( is => 'rw' ); # pps:engine object
22             has _label => ( is => 'rw' ); # label hosting the misspelled word
23             has _list => ( is => 'rw' ); # listbox listing the suggestions
24             has _offset => ( is => 'rw' ); # offset of _text within the editor
25             has _sizer => ( is => 'rw' ); # window sizer
26             has _text => ( is => 'rw' ); # text being spellchecked
27              
28              
29             #######
30             # Method new
31             #######
32             sub new {
33             my $class = shift;
34             my $main = shift;
35              
36             # Create the dialog
37             my $self = $class->SUPER::new($main);
38              
39             # define where to display main dialog
40             $self->CenterOnParent;
41             $self->SetTitle( sprintf( Wx::gettext('Spell-Checker v%s'), $VERSION ) );
42             $self->_set_up;
43              
44             return $self;
45             }
46              
47             #######
48             # Method _set_up
49             #######
50             sub _set_up {
51             my $self = shift;
52             my $main = $self->main;
53             my $current = $main->current;
54              
55             my $text_spell = $self->config_read->{Engine};
56             my $iso_name = $self->config_read->{$text_spell};
57              
58             #Thanks alias
59             my $status_info = "$text_spell => " . $self->padre_locale_label($iso_name);
60             $self->{status_info}->GetStaticBox->SetLabel($status_info);
61              
62              
63             # TODO: maybe grey out the menu option if
64             # no file is opened?
65             unless ( $current->document ) {
66             $main->message( Wx::gettext('No document opened.'), 'Padre' );
67             return;
68             }
69              
70             my $mime_type = $current->document->mimetype;
71             require Padre::Plugin::SpellCheck::Engine;
72             my $engine = Padre::Plugin::SpellCheck::Engine->new( $mime_type, $iso_name, $text_spell );
73             $self->_engine($engine);
74              
75             # fetch text to check
76             my $selection = $current->text;
77             my $wholetext = $current->document->text_get;
78              
79             my $text = $selection || $wholetext;
80             $self->_text($text);
81              
82             my $offset = $selection ? $current->editor->GetSelectionStart : 0;
83             $self->_offset($offset);
84              
85             # try to find a mistake
86             my @error = $engine->check($text);
87             my ( $word, $pos ) = @error;
88             $self->{error} = \@error;
89              
90             # no mistake means bbb we're done
91             if ( not defined $word ) {
92              
93             return;
94             }
95              
96             $self->_autoreplace( {} );
97             $self->_update;
98              
99             return;
100             }
101              
102             #######
103             # Method _update;
104             # update the dialog box with current error. aa
105             #######
106             sub _update {
107             my $self = shift;
108             my $main = $self->main;
109             my $current = $main->current;
110             my $editor = $current->editor;
111              
112             my $error = $self->{error};
113             my ( $word, $pos ) = @{$error};
114              
115             # update selection in parent window
116             my $offset = $self->_offset;
117             my $from = $offset + $pos + $self->_engine->_utf_chars;
118              
119             my $to = $from + length Encode::encode_utf8($word);
120             $editor->goto_pos_centerize($from);
121             $editor->SetSelection( $from, $to );
122              
123             # update label
124             $self->labeltext->SetLabel('Not in dictionary:');
125             $self->label->SetLabel($word);
126              
127             # update list
128             my @suggestions = $self->_engine->get_suggestions($word);
129              
130             $self->list->DeleteAllItems;
131             my $i = 0;
132             foreach my $w ( reverse @suggestions ) {
133             next unless defined $w;
134             my $item = Wx::ListItem->new;
135             $item->SetText($w);
136             my $idx = $self->list->InsertItem($item);
137             last if ++$i == 32; #TODO Fixme: should be a preference, why
138             }
139              
140             # select first item
141             my $item = $self->list->GetItem(0);
142             $item->SetState(Wx::wxLIST_STATE_SELECTED);
143             $self->list->SetItem($item);
144              
145             return;
146             }
147              
148              
149             #######
150             # dialog->_next;
151             #
152             # try to find next mistake, and update dialog to show this new error. if
153             # no error, display a message and exit.
154             #
155             # no params. no return value.
156             #######
157             sub _next {
158             my ($self) = @_;
159             my $autoreplace = $self->_autoreplace;
160              
161             # try to find next mistake
162             my @error = $self->_engine->check( $self->_text );
163             my ( $word, $pos ) = @error;
164             $self->{error} = \@error;
165              
166             # no mistake means we're done
167             if ( not defined $word ) {
168             $self->list->DeleteAllItems;
169             $self->label->SetLabel('Click Close');
170             $self->labeltext->SetLabel('Spell check finished:...');
171             $self->{replace}->Disable;
172             $self->{replace_all}->Disable;
173             $self->{ignore}->Disable;
174             $self->{ignore_all}->Disable;
175             return;
176             }
177              
178             # check if we have hit a replace all word
179             if ( exists $autoreplace->{$word} ) {
180             $self->_replace( $autoreplace->{$word} );
181             redo; # move on to next error
182             }
183              
184             # update gui with new error
185             $self->_update;
186             return;
187             }
188              
189             #######
190             # Method _replace( $word );
191             #
192             # fix current error by replacing faulty word with $word.
193             #
194             # no param. no return value.
195             #######
196             sub _replace {
197             my ( $self, $new ) = @_;
198             my $main = $self->main;
199             my $editor = $main->current->editor;
200              
201             # replace word in editor
202             my $error = $self->{error};
203             my ( $word, $pos ) = @{$error};
204             my $offset = $self->_offset;
205             my $from = $offset + $pos + $self->_engine->_utf_chars;
206              
207             # say 'length '.length Encode::encode_utf8($word);
208             my $to = $from + length Encode::encode_utf8($word);
209             $editor->SetSelection( $from, $to );
210             $editor->ReplaceSelection($new);
211              
212             # FIXME: as soon as STC issue is resolved:
213             # Include UTF8 characters from newly added word
214             # to overall count of UTF8 characters
215             # so we can set proper selections
216             $self->_engine->_count_utf_chars($new);
217              
218             # remove the beginning of the text, up to after replaced word
219             my $posold = $pos + length $word;
220             my $posnew = $pos + length $new;
221             my $text = substr $self->_text, $posold;
222             $self->_text($text);
223             $offset += $posnew;
224             $self->_offset($offset);
225              
226             return;
227             }
228              
229              
230             ########
231             # Event Handlers
232             ########
233              
234             #######
235             # Event Handler _on_ignore_all_clicked;
236             #######
237             sub _on_ignore_all_clicked {
238             my $self = shift;
239              
240             my $error = $self->{error};
241             my ( $word, $pos ) = @{$error};
242             $self->_engine->set_ignore_word($word);
243             $self->_on_ignore_clicked;
244              
245             return;
246             }
247              
248             #######
249             # Event Handler$self->_on_ignore_clicked;
250             #######
251             sub _on_ignore_clicked {
252             my $self = shift;
253              
254             # remove the beginning of the text, up to after current error
255             my $error = $self->{error};
256             my ( $word, $pos ) = @{$error};
257              
258             $pos += length $word;
259             my $text = substr $self->_text, $pos;
260             $self->_text($text);
261              
262             my $offset = $self->_offset + $pos;
263             $self->_offset($offset);
264              
265             # FIXME: as soon as STC issue is resolved:
266             # Include UTF8 characters from ignored word
267             # to overall count of UTF8 characters
268             # so we can set proper selections
269             $self->_engine->_count_utf_chars($word);
270              
271             # try to find next error
272             $self->_next;
273             return;
274             }
275              
276             #######
277             # Event Handler _on_replace_all_clicked;
278             #######
279             sub _on_replace_all_clicked {
280             my $self = shift;
281             my $error = $self->{error};
282             my ( $word, $pos ) = @{$error};
283              
284             # get replacing word
285             my $index = $self->list->GetNextItem( -1, Wx::wxLIST_NEXT_ALL, Wx::wxLIST_STATE_SELECTED );
286             return if $index == -1;
287             my $selected_word = $self->list->GetItem($index)->GetText;
288              
289             # store automatic replacement
290             $self->_autoreplace->{$word} = $selected_word;
291              
292             # do the replacement
293             $self->_on_replace_clicked;
294             return;
295             }
296              
297             #######
298             # Event Handler _on_replace_clicked;
299             #######
300             sub _on_replace_clicked {
301             my $self = shift;
302             my $event = shift;
303              
304             # get replacing word
305             my $index = $self->list->GetNextItem( -1, Wx::wxLIST_NEXT_ALL, Wx::wxLIST_STATE_SELECTED );
306             return if $index == -1;
307             my $selected_word = $self->list->GetItem($index)->GetText;
308              
309             # actually replace word in editor
310             $self->_replace($selected_word);
311              
312             # try to find next error
313             $self->_next;
314             return;
315             }
316              
317             #######
318             # Composed Method padre_local_label
319             # aspell to padre local label
320             #######
321             sub padre_locale_label {
322             my $self = shift;
323             my $local_dictionary = shift;
324              
325             my $lc_local_dictionary = lc( $local_dictionary ? $local_dictionary : 'en_GB' );
326             $lc_local_dictionary =~ s/_/-/;
327             require Padre::Locale;
328             my $label = Padre::Locale::label($lc_local_dictionary);
329              
330             return $label;
331             }
332              
333             1;
334              
335             __END__