1
package Padre::Wx::Replace;
7
Padre::Wx::Replace - Find and Replace Widget
11
C<Padre::Wx:Main> implements Padre's Find and Replace dialog box.
20
use Params::Util qw{_STRING};
23
use Padre::Wx::Role::Main ();
24
use Padre::Wx::History::ComboBox ();
25
our $VERSION = '0.92';
35
my $find = Padre::Wx::Replace->new($main);
37
Create and return a C<Padre::Wx::Replace> search and replace widget.
45
# Create the Wx dialog
46
my $self = $class->SUPER::new(
49
Wx::gettext('Find and Replace'),
52
Wx::CAPTION | Wx::CLOSE_BOX | Wx::SYSTEM_MENU | Wx::RESIZE_BORDER
55
# The text to search for
56
$self->{find_text} = Padre::Wx::History::ComboBox->new(
65
# The text to replace with
66
$self->{replace_text} = Padre::Wx::History::ComboBox->new(
75
# "Case Sensitive" option
76
$self->{find_case} = Wx::CheckBox->new(
79
Wx::gettext('Case &sensitive'),
81
Wx::Event::EVT_CHECKBOX(
85
$_[0]->{find_text}->SetFocus;
89
# "Find as Regex" option
90
$self->{find_regex} = Wx::CheckBox->new(
93
Wx::gettext('Regular &Expression'),
95
Wx::Event::EVT_CHECKBOX(
99
$_[0]->{find_text}->SetFocus;
103
# "Find First and Close" option
104
$self->{find_first} = Wx::CheckBox->new(
107
Wx::gettext('Close Window on &Hit'),
109
Wx::Event::EVT_CHECKBOX(
113
$_[0]->{find_text}->SetFocus;
117
# "Find in Reverse" option
118
$self->{find_reverse} = Wx::CheckBox->new(
121
Wx::gettext('Search &Backwards'),
123
Wx::Event::EVT_CHECKBOX(
125
$self->{find_reverse},
127
$_[0]->{find_text}->SetFocus;
131
# The "Replace All" option
132
$self->{replace_all} = Wx::CheckBox->new(
135
Wx::gettext('Replace &All'),
137
Wx::Event::EVT_CHECKBOX(
139
$self->{replace_all},
141
$_[0]->{find_text}->SetFocus;
146
$self->{find_button} = Wx::Button->new(
149
Wx::gettext('&Find'),
151
Wx::Event::EVT_BUTTON(
153
$self->{find_button},
158
Wx::Event::EVT_KEY_DOWN(
159
$self->{find_button},
161
$self->hotkey( $_[1], $self->{find_button} );
165
# The "Replace" button
166
$self->{replace_button} = Wx::Button->new(
169
Wx::gettext('&Replace'),
171
Wx::Event::EVT_BUTTON(
173
$self->{replace_button},
175
$_[0]->replace_button;
178
Wx::Event::EVT_KEY_DOWN(
179
$self->{replace_button},
181
$self->hotkey( $_[1], $self->{replace_button} );
184
$self->{replace_button}->SetDefault;
187
$self->{close_button} = Wx::Button->new(
190
Wx::gettext('&Close'),
192
Wx::Event::EVT_BUTTON(
194
$self->{close_button},
201
$self->{find_regex}->MoveAfterInTabOrder( $self->{find_text} );
202
$self->{replace_text}->MoveAfterInTabOrder( $self->{find_regex} );
203
$self->{find_case}->MoveAfterInTabOrder( $self->{replace_regex} );
204
$self->{find_reverse}->MoveAfterInTabOrder( $self->{find_case} );
205
$self->{find_first}->MoveAfterInTabOrder( $self->{find_reverse} );
206
$self->{replace_all}->MoveAfterInTabOrder( $self->{find_first} );
207
$self->{find_button}->MoveAfterInTabOrder( $self->{replace_all} );
208
$self->{replace_button}->MoveAfterInTabOrder( $self->{find_button} );
209
$self->{close_button}->MoveAfterInTabOrder( $self->{replace_button} );
212
# Find sizer begins here
213
my $find = Wx::StaticBoxSizer->new(
225
Wx::gettext('Find Text:'),
228
Wx::ALIGN_LEFT | Wx::ALIGN_CENTER_VERTICAL | Wx::ALL,
234
Wx::GROW | Wx::ALIGN_CENTER_VERTICAL | Wx::ALL,
240
Wx::ALIGN_LEFT | Wx::LEFT | Wx::RIGHT | Wx::TOP,
244
# Replace sizer begins here
245
my $replace = Wx::StaticBoxSizer->new(
249
Wx::gettext('Replace'),
257
Wx::gettext('Replace Text:'),
260
Wx::ALIGN_LEFT | Wx::ALIGN_CENTER_VERTICAL | Wx::ALL,
264
$self->{replace_text},
266
Wx::GROW | Wx::ALIGN_CENTER_VERTICAL | Wx::ALL,
270
# The layout grid for the options
271
my $grid = Wx::FlexGridSizer->new( 2, 2, 0, 0 );
272
$grid->AddGrowableCol(1);
276
Wx::ALIGN_LEFT | Wx::LEFT | Wx::RIGHT | Wx::TOP,
280
$self->{find_reverse},
282
Wx::ALIGN_LEFT | Wx::LEFT | Wx::RIGHT | Wx::TOP,
288
Wx::ALIGN_LEFT | Wx::LEFT | Wx::RIGHT | Wx::TOP,
292
$self->{replace_all},
294
Wx::ALIGN_LEFT | Wx::LEFT | Wx::RIGHT | Wx::TOP,
298
# Options sizer begins here
299
my $options = Wx::StaticBoxSizer->new(
303
Wx::gettext('Options')
310
Wx::ALIGN_CENTER_HORIZONTAL | Wx::GROW | Wx::ALL,
314
# Sizer for the buttons
315
my $bottom = Wx::BoxSizer->new(Wx::HORIZONTAL);
317
$self->{find_button},
319
Wx::GROW | Wx::RIGHT,
323
$self->{replace_button},
325
Wx::GROW | Wx::LEFT | Wx::RIGHT,
329
$self->{close_button},
335
# Fill the sizer for the overall dialog
336
my $sizer = Wx::FlexGridSizer->new( 1, 1, 0, 0 );
337
$sizer->AddGrowableCol(0);
341
Wx::ALIGN_CENTER_HORIZONTAL | Wx::GROW | Wx::ALL,
347
Wx::ALIGN_CENTER_HORIZONTAL | Wx::GROW | Wx::ALL,
353
Wx::ALIGN_CENTER_HORIZONTAL | Wx::GROW | Wx::ALL,
359
Wx::ALIGN_RIGHT | Wx::ALL,
363
# Let the widgets control the dialog size
364
$self->SetSizer($sizer);
365
$sizer->SetSizeHints($self);
376
Grab currently selected text, if any, and place it in find combo box.
377
Bring up the dialog or perform search for string's next occurrence
378
if dialog is already displayed.
380
TO DO: if selection is more than one line then consider it as the limit
381
of the search and not as the string to be used.
387
my $main = $self->main;
388
my $current = $self->current;
390
# No search if no file is open (TO DO ??)
391
return unless $current->editor;
393
# Do we have a default search term?
394
my $text = $current->text;
395
unless ( defined $text ) {
398
unless ( length $text ) {
399
if ( $main->has_findfast ) {
400
my $fast = $main->findfast->find_term;
401
$text = $fast if length $fast;
405
# TO DO: if selection is more than one lines then consider it as the
406
# limit of the search and not as the string to be used.
407
$text = '' if $text =~ /\n/;
409
# Hide the Fast Find if visible
410
$self->main->show_findfast(0);
412
# Clear out and reset the dialog, then prepare the new find
413
$self->{find_text}->refresh($text);
414
$self->{replace_text}->refresh;
415
if ( $self->IsShown ) {
416
return $self->find_button;
419
if ( length $text ) {
420
# Go straight to the replace field
421
$self->{replace_text}->SetFocus;
423
$self->{find_text}->SetFocus;
433
######################################################################
442
Executed when Find button is clicked.
444
Performs search on the term specified in the dialog.
450
my $main = $self->main;
452
# Generate the search object
453
my $search = $self->as_search;
455
$main->error('Not a valid search');
457
# Move the focus back to the search text
458
# so they can tweak their search.
459
$self->{find_text}->SetFocus;
463
# Apply the search to the current editor
464
$main->search_next($search);
466
# If we're only searching once, we won't need the dialog any more
467
if ( $self->{find_first}->GetValue ) {
488
# As we leave the Find dialog, return the user to the current editor
489
# window so they don't need to click it.
490
my $editor = $self->current->editor;
491
$editor->SetFocus if $editor;
498
=head2 replace_button
500
$self->replace_button;
502
Executed when the Replace button is clicked.
504
Replaces one appearance of the Find Text with the Replace Text.
506
If search window is still open, run C<search> on the whole text,
511
# TO DO: The change to this function that turned it into a dual-purpose function
512
# unintentionally transfered responsibility for the implementation of
513
# "Replace All" from the main class to a dialog class.
514
# This was a mistake, the dialog should not be where this is implemented.
515
# Revert this change and restore the independent "Replace All" code, so
516
# that the dialog goes back to acting only as controller.
519
my $main = $self->main;
521
# Generate the search object
522
my $search = $self->as_search;
524
$main->error('Not a valid search');
526
# Move the focus back to the search text
527
# so they can tweak their search.
528
$self->{find_text}->SetFocus;
532
# If we are replacing everything, hand off to the other method
533
if ( $self->{replace_all}->GetValue ) {
534
return $self->replace_all;
538
my $changed = $main->replace_next($search);
541
sprintf( Wx::gettext('No matches found for "%s".'), $self->{find_text}->GetValue ),
542
Wx::gettext('Search and Replace'),
546
# Move the focus back to the search text
547
# so they can change it if they want.
548
$self->{find_text}->SetFocus;
558
Executed when Replace All button is clicked.
560
Replace all appearances of given string in the current document.
566
my $main = $self->main;
568
# Generate the search object
569
my $search = $self->as_search;
571
$main->error('Not a valid search');
575
# Apply the search to the current editor
576
my $number_of_changes = $main->replace_all($search);
577
if ($number_of_changes) {
579
$number_of_changes == 1 ? Wx::gettext('Replaced %d match') : Wx::gettext('Replaced %d matches');
581
# remark: It would be better to use gettext for plural handling, but wxperl does not seem to support this at the moment.
583
sprintf( $message_text, $number_of_changes ),
584
Wx::gettext('Search and Replace')
588
sprintf( Wx::gettext('No matches found for "%s".'), $self->{find_text}->GetValue ),
589
Wx::gettext('Search and Replace'),
593
# Move the focus back to the search text
594
# so they can change it if they want.
595
$self->{find_text}->SetFocus;
603
#####################################################################
610
Integration with L<Padre::Search>. Generates a search instance for the
611
currently configured information in the Find dialog.
613
Returns a L<Padre::Search> object, or C<undef> if current state of the
614
dialog does not result in a valid search.
620
require Padre::Search;
622
find_term => $self->{find_text}->GetValue,
623
find_case => $self->{find_case}->GetValue,
624
find_regex => $self->{find_regex}->GetValue,
625
find_reverse => $self->{find_reverse}->GetValue,
626
replace_term => $self->{replace_text}->GetValue,
630
# Adds Ultraedit-like hotkeys for quick find/replace triggering
636
$self->find_button if $event->GetKeyCode == ord 'F';
637
$self->replace_button if $event->GetKeyCode == ord 'R';
638
$self->close if $event->GetKeyCode == Wx::K_ESCAPE;
640
if ( $event->GetKeyCode == Wx::K_TAB ) {
642
$index = 1 if $sender->GetId == $self->{find_button}->GetId;
643
$index = 2 if $sender->GetId == $self->{replace_button}->GetId;
644
$index = 3 if $sender->GetId == $self->{close_button}->GetId;
646
if ( $event->ShiftDown ) {
652
my @elements = qw(replace_all find_button replace_button close_button find_regex);
653
$self->{ $elements[$index] }->SetFocus;
663
=head1 COPYRIGHT & LICENSE
665
Copyright 2008-2011 The Padre development team as listed in Padre.pm.
666
This program is free software; you can redistribute
667
it and/or modify it under the same terms as Perl itself.
668
The full text of the license can be found in the
669
LICENSE file included with this module.
673
# Copyright 2008-2011 The Padre development team as listed in Padre.pm.
675
# This program is free software; you can redistribute it and/or
676
# modify it under the same terms as Perl 5 itself.