package Kephra::Dialog::Search; our $VERSION = '0.27'; use strict; use warnings; sub _ID { 'search_dialog' } my $ref; sub _ref { $ref = ref $_[0] eq 'Wx::Dialog' ? $_[0] : $ref } my $highlight_search; # set 1 if searchbar turns red my $active; my %color = ( norm_fore => Wx::Colour->new( 0x00, 0x00, 0x55 ), norm_back => Wx::Colour->new( 0xff, 0xff, 0xff ), alert_fore => Wx::Colour->new( 0xff, 0x33, 0x33 ), alert_back => Wx::Colour->new( 0xff, 0xff, 0xff ), ); sub find { # call as find dialog my $d = ready(); my $selection = Kephra::App::EditPanel::_ref()->GetSelectedText; if ($selection and not $d->{selection_radio}->GetValue ) { Kephra::Edit::Search::set_find_item( $selection ); $d->{find_input}->SetValue( $selection ); } else {$d->{find_input}->SetValue( Kephra::Edit::Search::get_find_item())} $d->{replace_input}->SetValue( Kephra::Edit::Search::get_replace_item() ); Wx::Window::SetFocus( $d->{find_input} ); } sub replace { # call as replace dialog my $d = ready(); my $selection = Kephra::App::EditPanel::_ref()->GetSelectedText; if ( length $selection > 0 and not $d->{selection_radio}->GetValue ) { Kephra::Edit::Search::set_replace_item( $selection ); $d->{replace_input}->SetValue( $selection ); } else { $d->{replace_input}->SetValue(Kephra::Edit::Search::get_replace_item()); } $d->{find_input}->SetValue( $selection ); Wx::Window::SetFocus( $d->{replace_input} ); } sub ready { # display find and replace dialog if ( not $active ) { # prepare some internal var and for better handling my $edit_panel = Kephra::App::EditPanel::_ref(); my $attr = Kephra::Edit::Search::_attributes(); my $dsettings = Kephra::API::settings()->{dialog}{search}; my $l18n = Kephra::API::localisation(); my $label = $l18n->{dialog}{search}{label}; my $hint = $l18n->{dialog}{search}{hint}; my @find_history = (); my @replace_history = (); my $d_style = &Wx::wxNO_FULL_REPAINT_ON_RESIZE | &Wx::wxSYSTEM_MENU | &Wx::wxCAPTION | &Wx::wxMINIMIZE_BOX | &Wx::wxCLOSE_BOX; $d_style |= &Wx::wxSTAY_ON_TOP if $Kephra::config{app}{window}{stay_on_top}; $dsettings->{position_x} = 10 if $dsettings->{position_x} < 0; $dsettings->{position_y} = 10 if $dsettings->{position_y} < 0; $dsettings->{width} = Wx::wxMSW() ? 436 : 496; if ( Kephra::Edit::Search::_history()->{use} ) { @find_history = @{ Kephra::Edit::Search::get_find_history() }; @replace_history = @{ Kephra::Edit::Search::get_replace_history() }; } # init search and replace dialog and release Kephra::Edit::Search::_refresh_search_flags(); $active = 1; $Kephra::temp{dialog}{active}++; $highlight_search = 1; # make dialog window and main panel my $d = Wx::Dialog->new( Kephra::App::Window::_ref(), -1, $l18n->{dialog}{search}{title}, [ $dsettings->{position_x}, $dsettings->{position_y} ], [ -1 , 268 ], $d_style ); my $icon = Wx::Icon->new; my $icon_bmp = Kephra::CommandList::get_cmd_property ('view-dialog-find', 'icon'); $icon->CopyFromBitmap($icon_bmp) if ref $icon_bmp eq 'Wx::Bitmap'; $d->SetIcon($icon); $d->SetTransparent(1 - int ($dsettings->{transparency} * 255)) if defined $dsettings->{transparency}; _ref($d); # input boxes with labels $d->{find_label} = Wx::StaticText->new($d, -1, $label->{search_for} ); $d->{replace_label}= Wx::StaticText->new($d, -1, $label->{replace_with} ); $d->{find_input} = Wx::ComboBox->new ($d, -1,'', [-1,-1], [324,22], [@find_history], &Wx::wxTE_PROCESS_ENTER ); $d->{find_input}->SetDropTarget ( Kephra::Edit::Search::InputTarget->new($d->{find_input}, 'find')); $d->{replace_input} = Wx::ComboBox->new ($d, -1, '', [-1,-1], [324,22], [@replace_history], &Wx::wxTE_PROCESS_ENTER); $d->{replace_input}->SetDropTarget ( Kephra::Edit::Search::InputTarget->new($d->{replace_input}, 'replace')); $d->{sep_line} = Wx::StaticLine->new( $d, -1, [0,0], [420,1], &Wx::wxLI_HORIZONTAL,); # search attributes checkboxes $d->{inc_box} = Wx::CheckBox->new($d, -1, $label->{incremental}); $d->{case_box} = Wx::CheckBox->new($d, -1, $label->{case}); $d->{begin_box}= Wx::CheckBox->new($d, -1, $label->{word_begin}); $d->{word_box} = Wx::CheckBox->new($d, -1, $label->{whole_word}); $d->{regex_box}= Wx::CheckBox->new($d, -1, $label->{regex}); $d->{wrap_box} = Wx::CheckBox->new($d, -1, $label->{auto_wrap}); $d->{inc_box} ->SetValue( $attr->{incremental} ); $d->{case_box} ->SetValue( $attr->{match_case} ); $d->{begin_box}->SetValue( $attr->{match_word_begin} ); $d->{word_box} ->SetValue( $attr->{match_whole_word} ); $d->{regex_box}->SetValue( $attr->{match_regex} ); $d->{wrap_box} ->SetValue( $attr->{auto_wrap} ); # range radio group my $range_box = Wx::StaticBox->new($d, -1, $label->{search_in}, [-1,-1], [-1,-1], &Wx::wxSIMPLE_BORDER | &Wx::wxRAISED_BORDER, ); $d->{selection_radio}= Wx::RadioButton->new($d, -1, $label->{selection}); $d->{document_radio} = Wx::RadioButton->new($d, -1, $label->{document} ); $d->{all_open_radio} = Wx::RadioButton->new($d, -1, $label->{open_documents} ); ################### disable # buttons my $bmp = \&Kephra::Config::icon_bitmap; $d->{replace_back}=Wx::BitmapButton->new($d,-1,&$bmp('replace-previous.xpm')); $d->{replace_fore}=Wx::BitmapButton->new($d,-1,&$bmp('replace-next.xpm')); $d->{backward_button}=Wx::BitmapButton->new($d,-1,&$bmp('go-previous.xpm')); $d->{foreward_button}=Wx::BitmapButton->new($d,-1,&$bmp('go-next.xpm')); $d->{fast_back_button}=Wx::BitmapButton->new($d,-1,&$bmp('go-fast-backward.xpm')); $d->{fast_fore_button}=Wx::BitmapButton->new($d,-1,&$bmp('go-fast-forward.xpm')); $d->{first_button}=Wx::BitmapButton->new($d,-1,&$bmp('go-first.xpm')); $d->{last_button}=Wx::BitmapButton->new($d,-1,&$bmp('go-last.xpm')); $d->{search_button} = Wx::Button->new($d, -1, $label->{search} ); $d->{replace_button}= Wx::Button->new($d, -1, $label->{replace_all} ); $d->{confirm_button}= Wx::Button->new($d, -1, $label->{with_confirmation} ); $d->{close_button} = Wx::Button->new($d, -1, $l18n->{dialog}{general}{close} ); #tooltips / hints if ( $dsettings->{tooltips} ) { $d->{foreward_button}->SetToolTip( $hint->{forward}); $d->{backward_button}->SetToolTip( $hint->{backward}); $d->{fast_fore_button}->SetToolTip( $hint->{fast_forward}); $d->{fast_back_button}->SetToolTip( $hint->{fast_backward}); $d->{first_button}->SetToolTip( $hint->{document_start}); $d->{last_button}->SetToolTip( $hint->{document_end}); $d->{replace_fore}->SetToolTip( $hint->{replace_forward}); $d->{replace_back}->SetToolTip( $hint->{replace_backward}); $d->{case_box}->SetToolTip( $hint->{match_case}); $d->{begin_box}->SetToolTip( $hint->{match_word_begin}); $d->{word_box}->SetToolTip( $hint->{match_whole_word}); $d->{regex_box}->SetToolTip( $hint->{match_regex}); $d->{wrap_box}->SetToolTip( $hint->{auto_wrap}); $d->{inc_box}->SetToolTip( $hint->{incremental}); } # eventhandling Wx::Event::EVT_KEY_DOWN($d->{find_input}, \&find_input_keyfilter ); Wx::Event::EVT_KEY_DOWN($d->{replace_input}, \&replace_input_keyfilter ); Wx::Event::EVT_TEXT($d, $d->{find_input}, \&incremental_search ); Wx::Event::EVT_TEXT($d, $d->{replace_input}, sub { my $input = $d->{replace_input}; my $pos = $input->GetInsertionPoint; Kephra::Edit::Search::set_replace_item( $input->GetValue ); $input->SetInsertionPoint($pos); }); Wx::Event::EVT_CHECKBOX($d, $d->{case_box}, sub { $$attr{match_case} = $d->{case_box}->GetValue; Kephra::Edit::Search::_refresh_search_flags(); } ); Wx::Event::EVT_CHECKBOX($d, $d->{begin_box}, sub { $$attr{match_word_begin} = $d->{begin_box}->GetValue; Kephra::Edit::Search::_refresh_search_flags(); } ); Wx::Event::EVT_CHECKBOX($d, $d->{word_box}, sub { $$attr{match_whole_word} = $d->{word_box}->GetValue; Kephra::Edit::Search::_refresh_search_flags(); } ); Wx::Event::EVT_CHECKBOX($d, $d->{regex_box}, sub { $$attr{match_regex} = $d->{regex_box}->GetValue; Kephra::Edit::Search::_refresh_search_flags(); } ); Wx::Event::EVT_CHECKBOX($d, $d->{wrap_box}, sub { $$attr{auto_wrap} = $d->{wrap_box}->GetValue; } ); Wx::Event::EVT_CHECKBOX($d, $d->{inc_box}, sub { $$attr{incremental} = $d->{inc_box}->GetValue; } ); Wx::Event::EVT_RADIOBUTTON($d, $d->{selection_radio},sub {$attr->{in} = 'selection'}); Wx::Event::EVT_RADIOBUTTON($d, $d->{document_radio}, sub {$attr->{in} = 'document'}); Wx::Event::EVT_RADIOBUTTON($d, $d->{all_open_radio}, sub {$attr->{in} = 'open_docs'}); Wx::Event::EVT_BUTTON($d, $d->{foreward_button}, sub { &no_sel_range; Kephra::Edit::Search::find_next(); } ); Wx::Event::EVT_BUTTON($d, $d->{backward_button}, sub { &no_sel_range; Kephra::Edit::Search::find_prev(); } ); Wx::Event::EVT_BUTTON($d, $d->{fast_fore_button}, sub { &no_sel_range; Kephra::Edit::Search::fast_fore(); } ); Wx::Event::EVT_BUTTON($d, $d->{fast_back_button}, sub { &no_sel_range; Kephra::Edit::Search::fast_back(); } ); Wx::Event::EVT_BUTTON($d, $d->{first_button}, sub { &no_sel_range; Kephra::Edit::Search::find_first(); } ); Wx::Event::EVT_BUTTON($d, $d->{last_button}, sub { &no_sel_range; Kephra::Edit::Search::find_last(); } ); Wx::Event::EVT_BUTTON($d, $d->{replace_fore}, sub { &no_sel_range; Kephra::Edit::Search::replace_fore(); } ); Wx::Event::EVT_BUTTON($d, $d->{replace_back}, sub { &no_sel_range; Kephra::Edit::Search::replace_back(); } ); Wx::Event::EVT_BUTTON($d, $d->{search_button}, sub{ &Kephra::Edit::Search::find_first } ); Wx::Event::EVT_BUTTON($d, $d->{replace_button}, sub{ &Kephra::Edit::Search::replace_all } ); Wx::Event::EVT_BUTTON($d, $d->{confirm_button}, sub{ &Kephra::Edit::Search::replace_confirm } ); Wx::Event::EVT_BUTTON($d, $d->{close_button}, sub{ shift->Close() } ); Wx::Event::EVT_CLOSE( $d, \&quit_search_dialog ); my $ID = _ID(); my $add_call = \&Kephra::EventTable::add_call; &$add_call( 'find', $ID.'_color_refresh', \&colour_find_input, $ID); &$add_call( 'find.item.changed', $ID, sub { $d->{find_input}->SetValue(Kephra::Edit::Search::get_find_item()); $d->{find_input}->SetInsertionPointEnd; }, $ID); &$add_call( 'replace.item.changed', $ID, sub { $d->{replace_input}->SetValue(Kephra::Edit::Search::get_replace_item()); $d->{replace_input}->SetInsertionPointEnd; }, $ID); Kephra::EventTable::add_call('find.item.history.changed', $ID, sub { Kephra::App::_ref()->Yield(); my $cb = $d->{find_input}; $Kephra::temp{dialog}{search}{control} = 1; $cb->Clear(); $cb->Append($_) for @{ Kephra::Edit::Search::get_find_history() }; $cb->SetValue( Kephra::Edit::Search::get_find_item() ); $cb->SetInsertionPointEnd; $Kephra::temp{dialog}{search}{control} = 0; }, $ID); Kephra::EventTable::add_call('replace.item.history.changed', $ID, sub { my $cb = $d->{replace_input}; $Kephra::temp{dialog}{search}{control} = 1; $cb->Clear(); $cb->Append($_) for @{ Kephra::Edit::Search::get_replace_history() }; $cb->SetValue( Kephra::Edit::Search::get_replace_item() ); $cb->SetInsertionPointEnd; $Kephra::temp{dialog}{search}{control} = 0; }, $ID); # detecting and selecting search range # if selection is just on one line if ( $edit_panel->LineFromPosition( $edit_panel->GetSelectionStart ) != $edit_panel->LineFromPosition( $edit_panel->GetSelectionEnd ) ) { $attr->{in} = 'selection'; $d->{selection_radio}->SetValue(1); } elsif ( $attr->{in} eq 'open_docs' ) { $d->{all_open_radio}->SetValue(1); } else { $attr->{in} = 'document'; $d->{document_radio}->SetValue(1); } # asembling my $option_sizer = Wx::BoxSizer->new(&Wx::wxVERTICAL); $option_sizer->Add( $d->{inc_box}, 0, &Wx::wxTOP, 0 ); $option_sizer->Add( $d->{case_box}, 0, &Wx::wxTOP, 15 ); $option_sizer->Add( $d->{begin_box}, 0, &Wx::wxTOP, 5 ); $option_sizer->Add( $d->{word_box}, 0, &Wx::wxTOP, 5 ); $option_sizer->Add( $d->{regex_box}, 0, &Wx::wxTOP, 5 ); my $rbz = Wx::StaticBoxSizer->new( $range_box, &Wx::wxVERTICAL ); $rbz->Add( $d->{selection_radio}, 1, &Wx::wxTOP, 5 ); $rbz->Add( $d->{document_radio}, 1, &Wx::wxTOP, 5 ); $rbz->Add( $d->{all_open_radio}, 1, &Wx::wxTOP, 5 ); my $range_sizer = Wx::BoxSizer->new(&Wx::wxVERTICAL); $range_sizer->Add( $d->{wrap_box}, 0, &Wx::wxTOP|&Wx::wxALIGN_CENTER_HORIZONTAL, 0 ); $range_sizer->Add( $rbz, 0, &Wx::wxGROW | &Wx::wxTOP, 10 ); my $pad_grid = Wx::FlexGridSizer->new( 4, 2, 0 , 1 ); $pad_grid->Add( $d->{replace_back}, 0, &Wx::wxBOTTOM, 5); $pad_grid->Add( $d->{replace_fore}, 0, &Wx::wxBOTTOM, 5); $pad_grid->Add( $d->{backward_button}, 0, ,0); $pad_grid->Add( $d->{foreward_button}, 0, ,0); $pad_grid->Add( $d->{fast_back_button},0, ,0); $pad_grid->Add( $d->{fast_fore_button},0, ,0); $pad_grid->Add( $d->{first_button}, 0, ,0); $pad_grid->Add( $d->{last_button}, 0, ,0); my $pads_sizer = Wx::BoxSizer->new(&Wx::wxHORIZONTAL); $pads_sizer->Add( $option_sizer,0,&Wx::wxALIGN_LEFT ,0); $pads_sizer->Add( $range_sizer ,1,&Wx::wxALIGN_CENTER|&Wx::wxLEFT|&Wx::wxRIGHT|&Wx::wxGROW ,20); $pads_sizer->Add( $pad_grid ,0,&Wx::wxALIGN_RIGHT ,0); my $button_sizer = Wx::BoxSizer->new(&Wx::wxHORIZONTAL); $button_sizer->Add( $d->{search_button}, 0, &Wx::wxLEFT, 15 ); $button_sizer->Add( $d->{replace_button}, 0, &Wx::wxLEFT, 10 ); $button_sizer->Add( $d->{confirm_button}, 0, &Wx::wxLEFT, 10 ); $button_sizer->AddStretchSpacer; $button_sizer->Add( $d->{close_button}, 0, &Wx::wxALIGN_RIGHT|&Wx::wxRIGHT, 15 ); my $b_grid = Wx::FlexGridSizer->new( 3, 2, 10, 0 ); $b_grid->Add($d->{find_label}, 0, &Wx::wxLEFT | &Wx::wxRIGHT | &Wx::wxALIGN_CENTER_VERTICAL | &Wx::wxALIGN_RIGHT, 10); $b_grid->Add($d->{find_input}, 0, &Wx::wxTOP, 0); $b_grid->Add($d->{replace_label}, 0, &Wx::wxLEFT | &Wx::wxRIGHT | &Wx::wxALIGN_CENTER_VERTICAL | &Wx::wxALIGN_RIGHT, 10); $b_grid->Add($d->{replace_input}, 0, &Wx::wxTOP, 0); $b_grid->AddSpacer(5); $b_grid->Add($pads_sizer, 1, &Wx::wxTOP|&Wx::wxGROW, 5); my $d_sizer = Wx::BoxSizer->new(&Wx::wxVERTICAL); $d_sizer->Add($b_grid, 1, &Wx::wxTOP , 15); $d_sizer->Add($d->{sep_line}, 0, &Wx::wxTOP | &Wx::wxALIGN_CENTER_HORIZONTAL | &Wx::wxGROW, 8); $d_sizer->Add($button_sizer, 0, &Wx::wxTOP | &Wx::wxBOTTOM | &Wx::wxGROW , 9); $d->SetSizer($d_sizer); $d->Fit(); #$d->SetAutoLayout(1); # go $d->Show(1); return $d; } else { my $d = _ref(); $d->Iconize(0); $d->Raise; return $d; } } # end of dialog constuction ########################## # dialog event functions # switch back from search in text selection to search in document # because if once looked in selection the finding is selected and this # settings makes no longer sense sub no_sel_range { my $dialog = _ref(); if ( $dialog->{selection_radio}->GetValue ) { $dialog->{document_radio}->SetValue(1); Kephra::Edit::Search::_attributes()->{in} = 'document'; } #$dialog->Refresh; #$dialog->Layout(); } # find input function sub find_input_keyfilter { my ( $input, $event ) = @_; my $dialog = _ref(); my $wx_frame = $dialog->GetParent; my $key_code = $event->GetKeyCode; if ($key_code == &Wx::WXK_RETURN) { no_sel_range(); if ($event->ControlDown){Kephra::Edit::Search::find_first(); $dialog->Close; } elsif ($event->ShiftDown ){Kephra::Edit::Search::find_prev() } else {Kephra::Edit::Search::find_next() } return; } elsif ($key_code == &Wx::WXK_ESCAPE) { $dialog->Close } elsif ($key_code == &Wx::WXK_TAB){ if ( $event->ShiftDown ) { $dialog->{close_button}->SetFocus } else { $dialog->{replace_input}->SetFocus} } $event->Skip; } sub colour_find_input { my $input = _ref()->{find_input}; my $pos = $input->GetInsertionPoint; my $found_something = Kephra::Edit::Search::_find_pos() > -1 ? 1 : 0; return if $highlight_search eq $found_something; $highlight_search = $found_something; if ($found_something){ $input->SetForegroundColour( $color{norm_fore} ); $input->SetBackgroundColour( $color{norm_back} ); } else { $input->SetForegroundColour( $color{alert_fore} ); $input->SetBackgroundColour( $color{alert_back} ); } $input->SetInsertionPoint($pos); $input->Refresh; } sub incremental_search { if ( Kephra::Edit::Search::_attributes()->{incremental} and not $Kephra::temp{dialog}{search}{control} ) { my $input = _ref()->{find_input}; Kephra::Edit::Search::set_find_item($input->GetValue); Kephra::Edit::Search::first_increment(); } } # replace input function sub replace_input_keyfilter { my ($input, $event) = @_; my $dialog = _ref(); my $key_code = $event->GetKeyCode; if ($key_code == &Wx::WXK_RETURN ) { if ( $event->ControlDown ) { Kephra::Edit::Search::replace_all(); $dialog->Close; } elsif ( $event->AltDown ) { replace_confirm($dialog) } else { Kephra::Edit::Search::replace_all() } } if ( $key_code == &Wx::WXK_ESCAPE) { $dialog->Close } elsif ($key_code == &Wx::WXK_TAB){ if ( $event->ShiftDown ) { $dialog->{find_input}->SetFocus } else { $dialog->{inc_box}->SetFocus } } $event->Skip; } sub replace_all { Kephra::Edit::Search::replace_all() } sub replace_confirm { Kephra::Edit::Search::replace_confirm() } sub raise_if_active { _ref()->Raise if $active } sub quit_search_dialog { my ( $win, $event ) = @_; my $config = Kephra::API::settings()->{dialog}{search}; ($config->{position_x}, $config->{position_y} ) = $win->GetPositionXY if $config->{save_position} == 1; $active = 0; $Kephra::temp{dialog}{active}--; Kephra::EventTable::del_own_subscriptions( _ID() ); $win->Destroy(); } ####################### 1;