Padre::Plugin::SpellCheck::Dialog - Spell check dialog for Padre


Padre-Plugin-SpellCheck documentation Contained in the Padre-Plugin-SpellCheck distribution.

Index


Code Index:

NAME

Top

Padre::Plugin::SpellCheck::Dialog - Spell check dialog for Padre

VERSION

Top

version 1.21

DESCRIPTION

Top

This module implements the dialog window that will be used to interact with the user when mistakes have been spotted.

PUBLIC METHODS

Top

Constructor

my $dialog = PPS::Dialog->new( %params );

Create and return a new dialog window. The following params are needed:

text => $text

The text being spell checked.

offset => $offset

The offset of $text within the editor. 0 if spell checking the whole file.

error => [ $word, $pos ]

The first spotted error, on $word (at position $pos), with some associated $suggestions (a list reference).

engine => $engine

The $engine being used (a Padre::Plugin::SpellCheck::Engine object).

Instance methods

SEE ALSO

Top

For all related information (bug reporting, source code repository, etc.), refer to Padre::Plugin::SpellCheck.

AUTHORS

Top

COPYRIGHT AND LICENSE

Top


Padre-Plugin-SpellCheck documentation Contained in the Padre-Plugin-SpellCheck distribution.

package Padre::Plugin::SpellCheck::Dialog;
BEGIN {
  $Padre::Plugin::SpellCheck::Dialog::VERSION = '1.21';
}

# ABSTRACT: Spell check dialog for Padre

use warnings;
use strict;

use Class::XSAccessor accessors => {
	_autoreplace => '_autoreplace', # list of automatic replaces
	_engine      => '_engine',      # pps:engine object
	_error       => '_errorpos',    # first error spotted [ $word, $pos ]
	_label       => '_label',       # label hosting the misspelled word
	_list        => '_list',        # listbox listing the suggestions
	_offset      => '_offset',      # offset of _text within the editor
	_plugin      => '_plugin',      # reference to spellcheck plugin
	_sizer       => '_sizer',       # window sizer
	_text        => '_text',        # text being spellchecked
};

use Padre::Current;
use Padre::Wx   ();
use Padre::Util ('_T');
use Encode;

use base 'Wx::Dialog';


# -- constructor

sub new {
	my ( $class, %params ) = @_;

	# create object
	my $config = $params{plugin}->config;
	my $self   = $class->SUPER::new(
		Padre::Current->main,
		-1,
		sprintf( _T('Spelling (%s)'), $config->{dictionary} ),
		Wx::wxDefaultPosition,
		Wx::wxDefaultSize,
		Wx::wxDEFAULT_FRAME_STYLE | Wx::wxTAB_TRAVERSAL,
	);
	$self->SetIcon( Wx::GetWxPerlIcon() );
	$self->_error( $params{error} );
	$self->_engine( $params{engine} );
	$self->_offset( $params{offset} );
	$self->_text( $params{text} );
	$self->_plugin( $params{plugin} );
	$self->_autoreplace( {} );

	# create dialog
	$self->_create;
	$self->_update;

	return $self;
}

# -- public methods

# -- gui handlers

#
# $self->_on_butclose_clicked;
#
# handler called when the close button has been clicked.
#
sub _on_butclose_clicked {
	my $self = shift;
	$self->Destroy;
}

#
# $self->_on_butignore_all_clicked;
#
# handler called when the ignore all button has been clicked.
#
sub _on_butignore_all_clicked {
	my ($self) = @_;

	my $word = $self->_error->[0];
	$self->_engine->ignore($word);
	$self->_on_butignore_clicked;
}

#
# $self->_on_butignore_clicked;
#
# handler called when the ignore button has been clicked.
#
sub _on_butignore_clicked {
	my ($self) = @_;

	# remove the beginning of the text, up to after current error
	my $error = $self->_error;
	my ( $word, $pos ) = @$error;
	$pos += length $word;
	my $text = substr $self->_text, $pos;
	$self->_text($text);
	my $offset = $self->_offset + $pos;
	$self->_offset($offset);

	# FIXME: as soon as STC issue is resolved:
	# Include UTF8 characters from ignored word
	# to overall count of UTF8 characters
	# so we can set proper selections
	$self->_engine->_count_utf_chars($word);

	# try to find next error
	$self->_next;
}

#
# $self->_on_butreplace_all_clicked;
#
# handler called when the replace all button has been clicked.
#
sub _on_butreplace_all_clicked {
	my ($self) = @_;

	# get replacing word
	my $list = $self->_list;
	my $id = $list->GetNextItem( -1, Wx::wxLIST_NEXT_ALL, Wx::wxLIST_STATE_SELECTED );
	return if $id == -1;
	my $new = $list->GetItem($id)->GetText;

	# store automatic replacement
	my $old = $self->_error->[0];
	$self->_autoreplace->{$old} = $new;

	# do the replacement
	$self->_on_butreplace_clicked;
}

#
# $self->_on_butreplace_clicked;
#
# handler called when the replace button has been clicked.
#
sub _on_butreplace_clicked {
	my ($self) = @_;
	my $list = $self->_list;

	# get replacing word
	my $id = $list->GetNextItem( -1, Wx::wxLIST_NEXT_ALL, Wx::wxLIST_STATE_SELECTED );
	return if $id == -1;
	my $new = $list->GetItem($id)->GetText;

	# actually replace word in editor
	$self->_replace($new);

	# try to find next error
	$self->_next;
}


# -- private methods

#
# $self->_create;
#
# create the dialog itself.
#
# no params, no return values.
#
sub _create {
	my ($self) = @_;

	# create sizer that will host all controls
	my $sizer = Wx::GridBagSizer->new( 5, 5 );
	$sizer->AddGrowableCol(1);
	$sizer->AddGrowableRow(6);
	$self->_sizer($sizer);

	# create the controls
	$self->_create_labels;
	$self->_create_list;
	$self->_create_buttons;

	# wrap everything in a vbox to add some padding
	my $vbox = Wx::BoxSizer->new(Wx::wxVERTICAL);
	$vbox->Add( $sizer, 1, Wx::wxEXPAND | Wx::wxALL, 5 );
	$self->SetSizerAndFit($vbox);
	$vbox->SetSizeHints($self);

	# set focus on listbox
	$self->_list->SetFocus;
}

#
# $dialog->_create_buttons;
#
# create the buttons pane.
#
# no params. no return values.
#
sub _create_buttons {
	my ($self) = @_;

	my $ba  = Wx::Button->new( $self, -1,              _T('Add to dictionary') );
	my $br  = Wx::Button->new( $self, -1,              _T('Replace') );
	my $bra = Wx::Button->new( $self, -1,              _T('Replace all') );
	my $bi  = Wx::Button->new( $self, -1,              _T('Ignore') );
	my $bia = Wx::Button->new( $self, -1,              _T('Ignore all') );
	my $bc  = Wx::Button->new( $self, Wx::wxID_CANCEL, _T('Close') );
	Wx::Event::EVT_BUTTON( $self, $br,  \&_on_butreplace_clicked );
	Wx::Event::EVT_BUTTON( $self, $bra, \&_on_butreplace_all_clicked );
	Wx::Event::EVT_BUTTON( $self, $bi,  \&_on_butignore_clicked );
	Wx::Event::EVT_BUTTON( $self, $bia, \&_on_butignore_all_clicked );
	Wx::Event::EVT_BUTTON( $self, $bc,  \&_on_butclose_clicked );

	my $sizer = $self->_sizer;
	$sizer->Add( $ba,  Wx::GBPosition->new( 0, 2 ), Wx::GBSpan->new( 1, 1 ), Wx::wxEXPAND );
	$sizer->Add( $br,  Wx::GBPosition->new( 2, 2 ), Wx::GBSpan->new( 1, 1 ), Wx::wxEXPAND );
	$sizer->Add( $bra, Wx::GBPosition->new( 3, 2 ), Wx::GBSpan->new( 1, 1 ), Wx::wxEXPAND );
	$sizer->Add( $bi,  Wx::GBPosition->new( 4, 2 ), Wx::GBSpan->new( 1, 1 ), Wx::wxEXPAND );
	$sizer->Add( $bia, Wx::GBPosition->new( 5, 2 ), Wx::GBSpan->new( 1, 1 ), Wx::wxEXPAND );
	$sizer->Add( $bc,  Wx::GBPosition->new( 7, 2 ), Wx::GBSpan->new( 1, 1 ), Wx::wxEXPAND );

	$ba->Disable;
}

#
# $dialog->_create_labels;
#
# create the top labels.
#
# no params. no return values.
#
sub _create_labels {
	my ($self) = @_;
	my $sizer = $self->_sizer;

	# create the labels...
	my $label   = Wx::StaticText->new( $self, -1, _T('Not in dictionary:') );
	my $labword = Wx::StaticText->new( $self, -1, 'w' x 25 );
	$labword->SetBackgroundColour( Wx::Colour->new('#ffaaaa') );
	$labword->Refresh;
	$self->_label($labword);

	# ... and place them
	$sizer->Add( $label, Wx::GBPosition->new( 0, 0 ) );
	$sizer->Add( $labword, Wx::GBPosition->new( 0, 1 ), Wx::GBSpan->new( 1, 1 ), Wx::wxEXPAND );
}

#
# $dialog->_create_list;
#
# create the suggestions list.
#
# no params. no return values.
#
sub _create_list {
	my ($self) = @_;
	my $sizer = $self->_sizer;

	my $lab = Wx::StaticText->new( $self, -1, _T('Suggestions') );
	$sizer->Add( $lab, Wx::GBPosition->new( 1, 0 ), Wx::GBSpan->new( 1, 3 ), Wx::wxEXPAND );
	my $list = Wx::ListView->new(
		$self,
		-1,
		Wx::wxDefaultPosition,
		Wx::wxDefaultSize,
		Wx::wxLC_SINGLE_SEL,
	);
	Wx::Event::EVT_LIST_ITEM_ACTIVATED( $self, $list, \&_on_butreplace_clicked );
	$sizer->Add(
		$list,
		Wx::GBPosition->new( 2, 0 ),
		Wx::GBSpan->new( 5, 2 ),
		Wx::wxEXPAND
	);
	$self->_list($list);
}

#
# dialog->_next;
#
# try to find next mistake, and update dialog to show this new error. if
# no error, display a message and exit.
#
# no params. no return value.
#
sub _next {
	my ($self) = @_;
	my $autoreplace = $self->_autoreplace;

	{

		# try to find next mistake
		my ( $word, $pos ) = $self->_engine->check( $self->_text );
		$self->_error( [ $word, $pos ] );

		# no mistake means we're done
		if ( not defined $word ) {
			$self->Destroy;
			$self->GetParent->message( _T('Spell check finished.'), 'Padre' );
			return;
		}

		# check if we have hit a replace all word
		if ( exists $autoreplace->{$word} ) {
			$self->_replace( $autoreplace->{$word} );
			redo; # move on to next error
		}
	}

	# update gui with new error
	$self->_update;
}

#
# $self->_replace( $word );
#
# fix current error by replacing faulty word with $word.
#
# no param. no return value.
#
sub _replace {
	my ( $self, $new ) = @_;
	my $editor = Padre::Current->editor;

	# replace word in editor
	my $error  = $self->_error;
	my $offset = $self->_offset;
	my ( $word, $pos ) = @$error;
	my $from = $offset + $pos + $self->_engine->_utf_chars;
	my $to   = $from + length Encode::encode_utf8($word);
	$editor->SetSelection( $from, $to );
	$editor->ReplaceSelection($new);

	# FIXME: as soon as STC issue is resolved:
	# Include UTF8 characters from newly added word
	# to overall count of UTF8 characters
	# so we can set proper selections
	$self->_engine->_count_utf_chars($new);

	# remove the beginning of the text, up to after replaced word
	my $posold = $pos + length $word;
	my $posnew = $pos + length $new;
	my $text   = substr $self->_text, $posold;
	$self->_text($text);
	$offset += $posnew;
	$self->_offset($offset);
}

#
# self->_update;
#
# update the dialog box with current error.
#
sub _update {
	my ($self) = @_;
	my $error = $self->_error;
	my ( $word, $pos ) = @$error;

	# update selection in parent window
	my $editor = Padre::Current->editor;
	my $offset = $self->_offset;
	my $from   = $offset + $pos + $self->_engine->_utf_chars;
	my $to     = $from + length Encode::encode_utf8($word);
	$editor->goto_pos_centerize($from);
	$editor->SetSelection( $from, $to );

	# update label
	$self->_label->SetLabel($word);

	# update list
	my @suggestions = $self->_engine->suggestions($word);
	my $list        = $self->_list;
	$list->DeleteAllItems;
	my $i = 0;
	foreach my $w ( reverse @suggestions ) {
		next unless defined $w;
		my $item = Wx::ListItem->new;
		$item->SetText($w);
		my $idx = $list->InsertItem($item);
		last if ++$i == 25; # FIXME: should be a preference
	}

	# select first item
	my $item = $list->GetItem(0);
	$item->SetState(Wx::wxLIST_STATE_SELECTED);
	$list->SetItem($item);
}


1;




__END__