Padre::Wx::Dialog::RegexEditor - dialog to make it easy to create a regular expression


Padre documentation Contained in the Padre distribution.

Index


Code Index:

NAME

Top

Padre::Wx::Dialog::RegexEditor - dialog to make it easy to create a regular expression

DESCRIPTION

Top

The Regex Editor provides an interface to easily create regular expressions used in Perl.

The user can insert a regular expression (the surrounding / characters are not needed) and a text. The Regex Editor will automatically display the matching text in the bottom right window.

At the top of the window the user can select any of the four regular expression modifiers:

Ignore case (i)
Single-line (s)
Multi-line (m)
Extended (x)

Global match

Allow the change/replacement of the // around the regular expression

Highlight the match in the source text instead of in a separate window

Display the captured groups in a tree hierarchy similar to Rx ?

  Group                  Span (character) Value
  Match 0 (Group 0)      4-7              the actual match

Display the various Perl variable containing the relevant values e.g. the @- and @+ arrays, the %+ hash $1, $2...

point out what to use instead of $@ and $' and $`

English explanation of the regular expression

COPYRIGHT & LICENSE

Top


Padre documentation Contained in the Padre distribution.

package Padre::Wx::Dialog::RegexEditor;

# The Regex Editor for Padre

use 5.008;
use strict;
use warnings;
use Padre::Wx             ();
use Padre::Wx::Icon       ();
use Padre::Wx::Role::Main ();

# RichTextCtrl
use Wx::RichText ();

our $VERSION = '0.86';
our @ISA     = qw{
	Padre::Wx::Role::Main
	Wx::Dialog
};


######################################################################
# Constructor

sub new {
	my $class  = shift;
	my $parent = shift;

	# Create the basic object
	my $self = $class->SUPER::new(
		$parent,
		-1,
		Wx::gettext('Regex Editor'),
		Wx::wxDefaultPosition,
		Wx::wxDefaultSize,
		Wx::wxDEFAULT_FRAME_STYLE,
	);

	# Set basic dialog properties
	$self->SetIcon(Padre::Wx::Icon::PADRE);
	$self->SetMinSize( [ 380, 500 ] );

	# create sizer that will host all controls
	my $sizer = Wx::BoxSizer->new(Wx::wxHORIZONTAL);
	$self->{sizer} = $sizer;

	# Create the controls
	$self->_create_controls($sizer);

	# Bind the control events
	$self->_bind_events;

	# Tune the size and position it appears
	$self->SetSizer($sizer);
	$self->Fit;
	$self->CentreOnParent;

	return $self;
}


#
# A private method that returns a hash of regex groups along with their meaning
#
sub _regex_groups {
	my $self = shift;

	return (
		'00' => {
			label => Wx::gettext('Character classes'),
			value => {
				'00.'  => Wx::gettext('Any character except a newline'),
				'01\d' => Wx::gettext('Any decimal digit'),
				'02\D' => Wx::gettext('Any non-digit'),
				'03\s' => Wx::gettext('Any whitespace character'),
				'04\S' => Wx::gettext('Any non-whitespace character'),
				'05\w' => Wx::gettext('Any word character'),
				'06\W' => Wx::gettext('Any non-word character'),
			}
		},
		'01' => {
			label => Wx::gettext('&POSIX Character classes'),
			value => {
				'00[:alpha:]'  => Wx::gettext('Alphabetic characters'),
				'01[:alnum:]'  => Wx::gettext('Alphanumeric characters'),
				'02[:ascii:]'  => Wx::gettext('7-bit US-ASCII character'),
				'03[:blank:]'  => Wx::gettext('Space and tab'),
				'04[:cntrl:]'  => Wx::gettext('Control characters'),
				'05[:digit:]'  => Wx::gettext('Digits'),
				'06[:graph:]'  => Wx::gettext('Visible characters'),
				'07[:lower:]'  => Wx::gettext('Lowercase characters'),
				'08[:print:]'  => Wx::gettext('Visible characters and spaces'),
				'09[:punct:]'  => Wx::gettext('Punctuation characters'),
				'10[:space:]'  => Wx::gettext('Whitespace characters'),
				'11[:upper:]'  => Wx::gettext('Uppercase characters'),
				'12[:word:]'   => Wx::gettext('Alphanumeric characters plus "_"'),
				'13[:xdigit:]' => Wx::gettext('Hexadecimal digits'),
			}
		},
		'02' => {
			label => Wx::gettext('&Quantifiers'),
			value => {
				'00*'     => Wx::gettext('Match 0 or more times'),
				'01+'     => Wx::gettext('Match 1 or more times'),
				'02?'     => Wx::gettext('Match 1 or 0 times'),
				'03{m}'   => Wx::gettext('Match exactly m times'),
				'05{n,}'  => Wx::gettext('Match at least n times'),
				'05{m,n}' => Wx::gettext('Match at least m but not more than n times'),
			}
		},
		'03' => {
			label => Wx::gettext('Miscellaneous'),
			value => {
				'00|'     => Wx::gettext('Alternation'),
				'01[ ]'   => Wx::gettext('Character set'),
				'02^'     => Wx::gettext('Beginning of line'),
				'03$'     => Wx::gettext('End of line'),
				'04\b'    => Wx::gettext('A word boundary'),
				'05\B'    => Wx::gettext('Not a word boundary'),
				'06(?# )' => Wx::gettext('A comment'),
			}
		},
		'04' => {
			label => Wx::gettext('Grouping constructs'),
			value => {
				'00( )'    => Wx::gettext('A group'),
				'01(?: )'  => Wx::gettext('Non-capturing group'),
				'02(?= )'  => Wx::gettext('Positive lookahead assertion'),
				'03(?! )'  => Wx::gettext('Negative lookahead assertion'),
				'04(?<=)'  => Wx::gettext('Positive lookbehind assertion'),
				'05(?<! )' => Wx::gettext('Negative lookbehind assertion'),
				'06\n'     => Wx::gettext('Backreference to the nth group'),
			}
		},

		# next list taken from perldoc perlre
		# most of these are not interesting for beginners so I am not sure we need to show them
		'05' => {
			label => Wx::gettext('Escape characters'),
			value => {
				'00\t'       => Wx::gettext('Tab'),
				'01\n'       => Wx::gettext('Newline'),
				'02\r'       => Wx::gettext('Return'),
				'03\f'       => Wx::gettext('Form feed'),
				'04\a'       => Wx::gettext('Alarm'),
				'05\e'       => Wx::gettext('Escape (Esc)'),
				'06\033'     => Wx::gettext('Octal character'),
				'07\x1B'     => Wx::gettext('Hex character'),
				'08\x{263a}' => Wx::gettext('Long hex character'),
				'09\cK'      => Wx::gettext('Control character'),
				'10\N{name}' => Wx::gettext("Unicode character 'name'"),
				'11\l'       => Wx::gettext('Lowercase next character'),
				'12\u'       => Wx::gettext('Uppercase next character'),
				'13\L'       => Wx::gettext('Lowercase till \E'),
				'14\U'       => Wx::gettext('Uppercase till \E'),
				'15\E'       => Wx::gettext('End case modification/metacharacter quoting'),
				'16\Q'       => Wx::gettext('Quote (disable) pattern metacharacters till \E'),
			}
		},
	);
}

sub _create_controls {
	my ( $self, $sizer ) = @_;

	# Dialog Controls, created in keyboard navigation order

	# Regex text field
	my $regex_label = Wx::StaticText->new( $self, -1, Wx::gettext('&Regular expression:') );
	$self->{regex} = Wx::TextCtrl->new(
		$self, -1, '', Wx::wxDefaultPosition, Wx::wxDefaultSize,
		Wx::wxRE_MULTILINE | Wx::wxWANTS_CHARS # Otherwise arrows will not work on win32
	);

	my %regex_groups = $self->_regex_groups;
	foreach my $code ( sort keys %regex_groups ) {
		my %sub_group   = %{ $regex_groups{$code} };
		my $button_name = $code . '_button';
		$self->{$button_name} = Wx::Button->new(
			$self, -1, $sub_group{label},
		);

		my $menu_name = $code . '_menu';

		Wx::Event::EVT_BUTTON(
			$self,
			$self->{$button_name},
			sub {
				my @pos  = $self->{$button_name}->GetPositionXY;
				my @size = $self->{$button_name}->GetSizeWH;
				$self->PopupMenu( $self->{$menu_name}, $pos[0], $pos[1] + $size[1] );
			},
		);

		$self->{$menu_name} = Wx::Menu->new;
		my %sub_group_value = %{ $sub_group{value} };
		foreach my $element ( sort keys %sub_group_value ) {
			my $label = $element;
			$label =~ s/^\d{2}//;
			my $menu_item = $self->{$menu_name}->Append( -1, $label . '  ' . $sub_group_value{$element} );

			Wx::Event::EVT_MENU(
				$self,
				$menu_item,
				sub {
					$_[0]->{regex}->WriteText($label);
				},
			);
		}
	}

	# Optionally toggle the visibility of the description field
	$self->{description_checkbox} = Wx::CheckBox->new(
		$self,
		-1,
		Wx::gettext('Show &Description'),
	);

	# Describe-the-regex text field
	$self->{description_text} = Wx::TextCtrl->new(
		$self, -1, '', Wx::wxDefaultPosition, Wx::wxDefaultSize,
		Wx::wxTE_MULTILINE | Wx::wxNO_FULL_REPAINT_ON_RESIZE
	);

	# Description is hidden by default
	$self->{description_text}->Hide;

	# Original input text field
	my $original_label = Wx::StaticText->new( $self, -1, Wx::gettext('&Original text:') );
	$self->{original_text} = Wx::TextCtrl->new(
		$self, -1, '', Wx::wxDefaultPosition, Wx::wxDefaultSize,
		Wx::wxTE_MULTILINE | Wx::wxNO_FULL_REPAINT_ON_RESIZE
	);

	# Matched readonly text field
	my $matched_label = Wx::StaticText->new( $self, -1, Wx::gettext('Matched text:') );
	$self->{matched_text} = Wx::RichTextCtrl->new(
		$self, -1, '', Wx::wxDefaultPosition, Wx::wxDefaultSize,
		Wx::wxRE_MULTILINE | Wx::wxRE_READONLY | Wx::wxWANTS_CHARS # Otherwise arrows will not work on win32
	);

	# Toggle the visibility of the replace (substitution) fields
	$self->{replace_checkbox} = Wx::CheckBox->new(
		$self,
		-1,
		Wx::gettext('Show Subs&titution'),
	);

	# Replace regex text field
	$self->{replace_label} = Wx::StaticText->new( $self, -1, Wx::gettext('&Replace text with:') );
	$self->{replace_text} = Wx::TextCtrl->new(
		$self, -1, '', Wx::wxDefaultPosition, Wx::wxDefaultSize,
		Wx::wxTE_MULTILINE | Wx::wxNO_FULL_REPAINT_ON_RESIZE
	);

	$self->{replace_label}->Hide;
	$self->{replace_text}->Hide;

	# Result from replace text field
	$self->{result_label} = Wx::StaticText->new( $self, -1, Wx::gettext('&Result from replace:') );
	$self->{result_text} = Wx::RichTextCtrl->new(
		$self, -1, '', Wx::wxDefaultPosition, Wx::wxDefaultSize,
		Wx::wxRE_MULTILINE | Wx::wxRE_READONLY | Wx::wxWANTS_CHARS # Otherwise arrows will not work on win32
	);

	$self->{result_label}->Hide;
	$self->{result_text}->Hide;

	# Insert regex into current document button_name
	$self->{insert_button} = Wx::Button->new(
		$self, -1, Wx::gettext('Insert'),
	);

	# Close button
	$self->{close_button} = Wx::Button->new(
		$self, Wx::wxID_CANCEL, Wx::gettext('&Close'),
	);

	my $buttons = Wx::BoxSizer->new(Wx::wxHORIZONTAL);
	$buttons->AddStretchSpacer;
	$buttons->Add( $self->{insert_button}, 0, Wx::wxALL, 1 );
	$buttons->Add( $self->{close_button},  0, Wx::wxALL, 1 );
	$buttons->AddStretchSpacer;

	# Modifiers
	my %m = $self->_modifiers();
	foreach my $name ( $self->_modifier_keys() ) {
		$self->{$name} = Wx::CheckBox->new(
			$self,
			-1,
			$m{$name}{name},
		);

		$self->{$name}->SetToolTip( Wx::ToolTip->new( $m{$name}{tooltip} ) );
	}

	# Dialog Layout

	my $modifiers = Wx::BoxSizer->new(Wx::wxHORIZONTAL);
	$modifiers->AddStretchSpacer;
	$modifiers->Add( $self->{ignore_case}, 0, Wx::wxALL, 1 );
	$modifiers->Add( $self->{single_line}, 0, Wx::wxALL, 1 );
	$modifiers->Add( $self->{multi_line},  0, Wx::wxALL, 1 );
	$modifiers->Add( $self->{extended},    0, Wx::wxALL, 1 );
	$modifiers->Add( $self->{global},      0, Wx::wxALL, 1 );

	$modifiers->AddStretchSpacer;

	my $regex = Wx::BoxSizer->new(Wx::wxVERTICAL);
	$regex->Add( $self->{regex}, 1, Wx::wxALL | Wx::wxEXPAND, 1 );

	my $regex_groups = Wx::BoxSizer->new(Wx::wxVERTICAL);
	foreach my $code ( sort keys %regex_groups ) {
		my $button_name = $code . '_button';
		$regex_groups->Add( $self->{$button_name}, 0, Wx::wxEXPAND, 1 );
	}

	my $combined = Wx::BoxSizer->new(Wx::wxHORIZONTAL);
	$combined->Add( $regex,        2, Wx::wxALL | Wx::wxEXPAND, 0 );
	$combined->Add( $regex_groups, 0, Wx::wxALL | Wx::wxEXPAND, 0 );

	# Vertical layout of the left hand side
	my $left = Wx::BoxSizer->new(Wx::wxVERTICAL);
	$left->Add( $modifiers, 0, Wx::wxALL | Wx::wxEXPAND, 2 );
	$left->AddSpacer(5);
	$left->Add( $regex_label, 0, Wx::wxALL | Wx::wxEXPAND, 1 );
	$left->Add( $combined,    0, Wx::wxALL | Wx::wxEXPAND, 2 );

	$left->Add( $self->{description_checkbox}, 0, Wx::wxALL | Wx::wxEXPAND, 1 );
	$left->Add( $self->{description_text},     2, Wx::wxALL | Wx::wxEXPAND, 1 );

	$left->Add( $original_label,        0, Wx::wxALL | Wx::wxEXPAND, 1 );
	$left->Add( $self->{original_text}, 1, Wx::wxALL | Wx::wxEXPAND, 1 );
	$left->Add( $matched_label,         0, Wx::wxALL | Wx::wxEXPAND, 1 );
	$left->Add( $self->{matched_text},  1, Wx::wxALL | Wx::wxEXPAND, 1 );

	$left->Add( $self->{replace_checkbox}, 0, Wx::wxALL | Wx::wxEXPAND, 1 );
	$left->Add( $self->{replace_label},    0, Wx::wxALL | Wx::wxEXPAND, 1 );
	$left->Add( $self->{replace_text},     1, Wx::wxALL | Wx::wxEXPAND, 1 );
	$left->Add( $self->{result_label},     0, Wx::wxALL | Wx::wxEXPAND, 1 );
	$left->Add( $self->{result_text},      1, Wx::wxALL | Wx::wxEXPAND, 1 );

	$left->AddSpacer(5);
	$left->Add( $buttons, 0, Wx::wxALL | Wx::wxEXPAND, 1 );

	# Main sizer
	$sizer->Add( $left, 1, Wx::wxALL | Wx::wxEXPAND, 5 );
}

sub _bind_events {
	my $self = shift;

	Wx::Event::EVT_TEXT(
		$self,
		$self->{regex},
		sub { $_[0]->run; },
	);
	Wx::Event::EVT_KEY_DOWN(
		$self,
		sub {
			my ($key_event) = $_[1];
			$self->Hide if $key_event->GetKeyCode == Wx::WXK_ESCAPE;
			return;
		}
	);
	Wx::Event::EVT_TEXT(
		$self,
		$self->{replace_text},
		sub { $_[0]->run; },
	);
	Wx::Event::EVT_TEXT(
		$self,
		$self->{original_text},
		sub { $_[0]->run; },
	);

	# Modifiers
	foreach my $name ( $self->_modifier_keys ) {
		Wx::Event::EVT_CHECKBOX(
			$self,
			$self->{$name},
			sub {
				$_[0]->box_clicked($name);
			},
		);
	}

	# Description checkbox
	Wx::Event::EVT_CHECKBOX(
		$self,
		$self->{description_checkbox},
		sub {

			# toggles the visibility of the description field
			if ( $self->{description_checkbox}->IsChecked ) {
				my $regex = $self->{regex}->GetValue;
				$self->{description_text}->SetValue( $self->_dump_regex($regex) );
			}
			$self->{description_text}->Show( $self->{description_checkbox}->IsChecked );
			$self->{sizer}->Layout;
		},
	);

	# Replace checkbox
	Wx::Event::EVT_CHECKBOX(
		$self,
		$self->{replace_checkbox},
		sub {

			$self->replace;

			# toggles the visibility of the replace fields
			foreach my $field (qw(replace_label replace_text result_label result_text)) {
				$self->{$field}->Show( $self->{replace_checkbox}->IsChecked );
			}
			$self->{sizer}->Layout;
		},
	);

	Wx::Event::EVT_KEY_DOWN(
		$self->{matched_text},
		sub {
			my ($key_event) = $_[1];
			$self->Hide if $key_event->GetKeyCode == Wx::WXK_ESCAPE;
			return;
		}
	);

	Wx::Event::EVT_KEY_DOWN(
		$self->{result_text},
		sub {
			my ($key_event) = $_[1];
			$self->Hide if $key_event->GetKeyCode == Wx::WXK_ESCAPE;
			return;
		}
	);

	Wx::Event::EVT_BUTTON(
		$self,
		$self->{insert_button},
		sub { shift->_insert_regex; },
	);
}

#
# A private method that inserts the current regex into the current document
#
sub _insert_regex {
	my $self = shift;

	my $match_part   = $self->{regex}->GetValue;
	my $replace_part = $self->{replace_text}->GetValue;

	my ($modifiers) = $self->_get_modifier_settings;

	my $editor = $self->current->editor or return;
	$editor->InsertText( $editor->GetCurrentPos, "s/$match_part/$replace_part/$modifiers" );

	return;
}


#
# A private method that returns a hash of regex modifiers
#
sub _modifiers {
	return (
		ignore_case => {
			mod     => 'i', name => Wx::gettext('Ignore case (&i)'),
			tooltip => Wx::gettext('Case-insensitive matching')
		},
		single_line => {
			mod     => 's', name => Wx::gettext('Single-line (&s)'),
			tooltip => Wx::gettext('"." also matches newline')
		},
		multi_line => {
			mod => 'm', name => Wx::gettext('Multi-line (&m)'),
			tooltip => Wx::gettext('"^" and "$" match the start and end of any line inside the string')
		},
		extended => {
			mod => 'x', name => Wx::gettext('Extended (&x)'),
			tooltip =>
				Wx::gettext('Extended regular expressions allow free formatting (whitespace is ignored) and comments')
		},
		global => {
			mod     => 'g', name => Wx::gettext('Global (&g)'),
			tooltip => Wx::gettext('Replace all occurrences of the pattern')
		},
	);
}

#
# returns the regex modifier keys in the order they appear in the GUI
#
sub _modifier_keys {
	return qw{ ignore_case single_line multi_line extended	global};
}


# -- public methods

sub show {
	my $self = shift;

	if ( $self->IsShown ) {
		$self->SetFocus;
	} else {
		my $editor = $self->current->editor;
		if ($editor) {
			my $selection        = $editor->GetSelectedText;
			my $selection_length = length $selection;
			if ( $selection_length > 0 ) {
				$self->{regex}->ChangeValue($selection);
			} else {
				$self->{regex}->ChangeValue('\w+');
			}
		} else {
			$self->{regex}->ChangeValue('\w+');
		}

		$self->{replace_text}->ChangeValue('Baz');
		$self->{original_text}->SetValue('Foo Bar');

		$self->Show;
	}

	$self->{regex}->SetFocus;

	return;
}

#
# Private method to dump the regular expression description as text
#
sub _dump_regex {
	if ( scalar @_ == 2 ) {
		my ( $self, $regex ) = @_;
		require PPIx::Regexp;
		return $self->_dump_regex( PPIx::Regexp->new("/$regex/"), '', 0 );
	}

	my ( $self, $parent, $str, $level ) = @_;

	$str   = '' unless $str;
	$level = 0  unless $level;
	my @children = $parent->isa('PPIx::Regexp::Node') ? $parent->children : ();
	foreach my $child (@children) {
		next if $child->content eq '';
		my $class_name = $child->class;
		$class_name =~ s/PPIx::Regexp:://;
		$str .= ( ' ' x ( $level * 4 ) ) . $class_name . '     (' . $child->content . ")\n";
		$str = $self->_dump_regex( $child, $str, $level + 1 );
	}
	return $str;
}

#
# Private method to return all the parsed regex elements as an array
#
sub _parse_regex_elements {
	my ( $parent, $position, @array ) = @_;
	$position = 0  unless $position;
	@array    = () unless @array;
	my @elements = $parent->isa('PPIx::Regexp::Node') ? $parent->elements : ();
	foreach my $element (@elements) {
		my $content = $element->content;
		next if $content eq '';
		my $class_name = $element->class;
		push @array,
			{
			element => $element,
			offset  => $position,
			len     => length $content
			};
		@array = _parse_regex_elements( $element, $position, @array );
		$position += length $content;
	}
	return @array;
}

#
# Returns the user input data of the dialog as a hashref
#
sub get_data {
	my $self = shift;

	my %data = (
		text => {
			regex         => $self->{regex}->GetValue,
			replace       => $self->{replace_text}->GetValue,
			original_text => $self->{original_text}->GetValue,
		},
		modifiers => [ $self->_get_modifier_settings ],
	);

	return \%data;
}

#
# Sets the user input data of the dialog given a hashref containing the results of get_data
#
sub set_data {
	my ( $self, $data_ref ) = @_;

	foreach my $text_field ( keys %{ $data_ref->{text} } ) {
		$self->{$text_field}->SetValue( $data_ref->{text}->{$text_field} );
	}

	my $modifier_string = $data_ref->{modifiers}->[0];
	my %modifiers       = $self->_modifiers();
	foreach my $name ( keys %modifiers ) {
		$self->{$name}->SetValue(1) if $modifier_string =~ s/$modifiers{$name}{mod}//;
	}

	return;
}

#
# Private method to get the modifier settings as two strings
# the first strings returns the active modifiers, the second the
# inactive ones
#
sub _get_modifier_settings {
	my $self = shift;

	my $active_modifiers   = '';
	my $inactive_modifiers = '';
	my %modifiers          = $self->_modifiers();
	foreach my $name ( keys %modifiers ) {
		if ( $self->{$name}->IsChecked ) {
			$active_modifiers .= $modifiers{$name}{mod};
		} else {
			$inactive_modifiers .= $modifiers{$name}{mod};
		}
	}

	return ( $active_modifiers, $inactive_modifiers );
}

sub run {
	my $self = shift;

	my $regex         = $self->{regex}->GetValue;
	my $original_text = $self->{original_text}->GetValue;

	# TODO what about white space only regexes?
	if ( $regex eq '' ) {
		$self->{matched_text}->BeginTextColour(Wx::wxRED);
		$self->{matched_text}->SetValue( Wx::gettext('Empty regex') );
		$self->{matched_text}->EndTextColour;
		return;
	}


	my ( $active, $inactive ) = $self->_get_modifier_settings;

	$self->{matched_text}->Clear;

	$self->{matched_text}->BeginTextColour(Wx::wxBLACK);

	my $match;
	my $match_start;
	my $match_end;
	my $result;

	my $warning;

	# XXX Ignore Win32::API warnings. It's ugly but it works :)
	local $SIG{__WARN__} = sub { $warning = $_[0] };

	# TODO loop on all matches in case of /g
	my $code = "\$result = \$original_text =~ /\$regex/$active; (\$match_start, \$match_end) = (\$-[0], \$+[0])";
	eval $code;
	if ($@) {
		$self->{matched_text}->BeginTextColour(Wx::wxRED);
		$self->{matched_text}->SetValue( sprintf( Wx::gettext('Match failure in %s:  %s'), $regex, $@ ) );
		$self->{matched_text}->EndTextColour;
		return;
	}

	if ($result) {
		$match = substr( $original_text, $match_start, $match_end - $match_start );
	}

	if ($warning) {
		$self->{matched_text}->BeginTextColour(Wx::wxRED);
		$self->{matched_text}->SetValue( sprintf( Wx::gettext('Match warning in %s:  %s'), $regex, $warning ) );
		$self->{matched_text}->EndTextColour;
		return;
	}

	if ( defined $match ) {
		if ( $match_start == $match_end ) {
			$self->{matched_text}->BeginTextColour(Wx::wxRED);
			$self->{matched_text}
				->SetValue( sprintf( Wx::gettext('Match with 0 width at character %s'), $match_start ) );
			$self->{matched_text}->EndTextColour;
		} else {
			my @chars = split( //, $original_text );
			my $pos = 0;
			foreach my $char (@chars) {
				if ( $pos == $match_start ) {
					$self->{matched_text}->BeginTextColour(Wx::wxRED);
					$self->{matched_text}->BeginUnderline;
				} elsif ( $pos == $match_end ) {
					$self->{matched_text}->EndTextColour;
					$self->{matched_text}->EndUnderline;
				}
				$self->{matched_text}->AppendText($char);
				$pos++;
			}
		}
	} else {
		$self->{matched_text}->BeginTextColour(Wx::wxRED);
		$self->{matched_text}->SetValue( Wx::gettext('No match') );
		$self->{matched_text}->EndTextColour;
	}
	$self->{matched_text}->EndTextColour;


	$self->replace;

	$self->{description_text}->SetValue( $self->_dump_regex($regex) ) if $self->{description_text}->IsShown;

	#	$self->{regex}->Clear;
	#	my @elements = _parse_regex_elements;
	#	foreach my $element (@elements) {
	#		my $class_name = $element->element->class;
	#		if ($class_name eq 'PPIx::Regexp::Token::CharClass::Simple') {
	#			$self->{regex}->BeginTextColour(Wx::wxRED);
	#		} elsif( $class_name eq 'PPIx::Regexp::Token::Quantifier') {
	#			$self->{regex}->BeginTextColour(Wx::wxBLUE);
	#		} elsif( $class_name eq 'PPIx::Regexp::Token::Operator') {
	#			$self->{regex}->BeginTextColour(Wx::wxLIGHT_GREY);
	#		} elsif( $class_name eq 'PPIx::Regexp::Structure::Capture') {
	#			$self->{regex}->BeginTextColour(Wx::wxCYAN);
	#		}
	#		$self->{regex}->AppendText($element->content);
	#	$self->{regex}->EndTextColour;
	#	}

	return;
}

sub replace {
	my $self = shift;
	return if !$self->{replace_checkbox}->IsChecked;

	my $regex       = $self->{regex}->GetValue;
	my $result_text = $self->{original_text}->GetValue;
	my $replace     = $self->{replace_text}->GetValue;

	my ( $active, $inactive ) = $self->_get_modifier_settings;

	$self->{result_text}->Clear;

	my $code = "\$result_text =~ s{\$regex}{$replace}$active";
	eval $code;
	if ($@) {
		$self->{result_text}->BeginTextColour(Wx::wxRED);
		$self->{result_text}->AppendText( sprintf( Wx::gettext('Replace failure in %s:  %s'), $regex, $@ ) );
		$self->{result_text}->EndTextColour;
		return;
	}

	if ( defined $result_text ) {
		$self->{result_text}->SetValue($result_text);
	}

	return;
}

sub box_clicked {
	my $self = shift;
	$self->run();
	return;
}

1;

__END__