root/trunk/Padre/lib/Padre/Wx/Dialog/OpenResource.pm @ 11120

Revision 11120, 14.4 KB (checked in by azawawi, 6 months ago)

Perl tidy (25 files)

Line 
1package Padre::Wx::Dialog::OpenResource;
2
3use 5.008;
4use strict;
5use warnings;
6use Cwd              ();
7use Padre::DB        ();
8use Padre::Wx        ();
9use Padre::Wx::Icon  ();
10use Padre::MimeTypes ();
11
12our $VERSION = '0.58';
13our @ISA     = qw{
14        Padre::Wx::Role::MainChild
15        Wx::Dialog
16};
17
18use Class::XSAccessor {
19        accessors => {
20                _sizer                    => '_sizer',                    # window sizer
21                _search_text              => '_search_text',              # search text control
22                _matches_list             => '_matches_list',             # matches list
23                _status_text              => '_status_text',              # status label
24                _directory                => '_directory',                # searched directory
25                _matched_files            => '_matched_files',            # matched files list
26                _copy_button              => '_copy_button',              # copy button
27                _popup_button             => '_popup_button',             # popup button for options
28                _popup_menu               => '_popup_menu',               # options popup menu
29                _skip_vcs_files           => '_skip_vcs_files',           # Skip VCS files menu item
30                _skip_using_manifest_skip => '_skip_using_manifest_skip', # Skip using MANIFEST.SKIP menu item
31        }
32};
33
34# -- constructor
35sub new {
36        my ( $class, $main ) = @_;
37
38        # create object
39        my $self = $class->SUPER::new(
40                $main,
41                -1,
42                '',
43                Wx::wxDefaultPosition,
44                Wx::wxDefaultSize,
45                Wx::wxDEFAULT_FRAME_STYLE | Wx::wxTAB_TRAVERSAL,
46        );
47
48        $self->init_search;
49
50        # Dialog's icon as is the same as Padre
51        $self->SetIcon(Padre::Wx::Icon::PADRE);
52
53        # create dialog
54        $self->_create;
55
56        return $self;
57}
58
59
60#
61# Initialize search
62#
63sub init_search {
64        my $self = shift;
65
66        #Check if we have an open file so we can use its directory
67        my $doc = $self->current->document;
68        my $filename = ( defined $doc ) ? $doc->filename : undef;
69        my $dir;
70        if ($filename) {
71
72                # current document's project or base directory
73                $dir = Padre::Util::get_project_dir($filename)
74                        || File::Basename::dirname($filename);
75        } else {
76
77                # current working directory
78                $dir = Cwd::getcwd();
79        }
80
81
82        my $old_dir = $self->_directory;
83        if ( $old_dir && $old_dir ne $dir ) {
84
85                # Restart search if the project/current directory is different
86                $self->_matched_files(undef);
87        }
88
89        $self->_directory($dir);
90        $self->SetLabel( Wx::gettext('Open Resource') . ' - ' . $dir );
91}
92
93# -- event handler
94
95#
96# handler called when the ok button has been clicked.
97#
98sub _on_ok_button_clicked {
99        my ($self) = @_;
100
101        my $main = $self->main;
102        $self->Hide;
103
104        #Open the selected resources here if the user pressed OK
105        my @selections = $self->_matches_list->GetSelections();
106        foreach my $selection (@selections) {
107                my $filename = $self->_matches_list->GetClientData($selection);
108
109                # Fetch the recently used files from the database
110                require Padre::DB::RecentlyUsed;
111                my $recently_used = Padre::DB::RecentlyUsed->select( "where type = ? and value = ?", 'RESOURCE', $filename )
112                        || [];
113                my $found = scalar @$recently_used > 0;
114
115                eval {
116
117                        # try to open the file now
118                        if ( my $id = $main->find_editor_of_file($filename) ) {
119                                my $page = $main->notebook->GetPage($id);
120                                $page->SetFocus;
121                        } else {
122                                $main->setup_editors($filename);
123                        }
124                };
125                if ($@) {
126                        Wx::MessageBox(
127                                Wx::gettext('Error while trying to perform Padre action'),
128                                Wx::gettext('Error'),
129                                Wx::wxOK,
130                                $main,
131                        );
132                } else {
133
134                        # And insert a recently used tuple if it is not found
135                        # and the action is successful.
136                        if ( not $found ) {
137                                Padre::DB::RecentlyUsed->create(
138                                        name      => $filename,
139                                        value     => $filename,
140                                        type      => 'RESOURCE',
141                                        last_used => time(),
142                                );
143                        } else {
144                                Padre::DB->do(
145                                        "update recently_used set last_used = ? where name = ? and type = ?",
146                                        {}, time(), $filename, 'RESOURCE',
147                                );
148                        }
149                }
150        }
151
152}
153
154
155# -- private methods
156
157#
158# create the dialog itself.
159#
160sub _create {
161        my ($self) = @_;
162
163        # create sizer that will host all controls
164        my $sizer = Wx::BoxSizer->new(Wx::wxVERTICAL);
165        $self->_sizer($sizer);
166
167        # create the controls
168        $self->_create_controls;
169        $self->_create_buttons;
170
171        # wrap everything in a vbox to add some padding
172        $self->SetMinSize( [ 360, 340 ] );
173        $self->SetSizer($sizer);
174
175        # center/fit the dialog
176        $self->Fit;
177        $self->CentreOnParent;
178}
179
180#
181# create the buttons pane.
182#
183sub _create_buttons {
184        my ($self) = @_;
185        my $sizer = $self->_sizer;
186
187        $self->{ok_button} = Wx::Button->new(
188                $self, Wx::wxID_OK, Wx::gettext('&OK'),
189        );
190        $self->{ok_button}->SetDefault;
191        $self->{cancel_button} = Wx::Button->new(
192                $self, Wx::wxID_CANCEL, Wx::gettext('&Cancel'),
193        );
194
195        my $buttons = Wx::BoxSizer->new(Wx::wxHORIZONTAL);
196        $buttons->AddStretchSpacer;
197        $buttons->Add( $self->{ok_button},     0, Wx::wxALL | Wx::wxEXPAND, 5 );
198        $buttons->Add( $self->{cancel_button}, 0, Wx::wxALL | Wx::wxEXPAND, 5 );
199        $sizer->Add( $buttons, 0, Wx::wxALL | Wx::wxEXPAND | Wx::wxALIGN_CENTER, 5 );
200
201        Wx::Event::EVT_BUTTON( $self, Wx::wxID_OK, \&_on_ok_button_clicked );
202}
203
204#
205# create controls in the dialog
206#
207sub _create_controls {
208        my ($self) = @_;
209
210        # search textbox
211        my $search_label = Wx::StaticText->new(
212                $self, -1,
213                Wx::gettext('&Select an item to open (? = any character, * = any string):')
214        );
215        $self->_search_text(
216                Wx::TextCtrl->new(
217                        $self,                 -1, '',
218                        Wx::wxDefaultPosition, Wx::wxDefaultSize,
219                )
220        );
221
222        # matches result list
223        my $matches_label = Wx::StaticText->new(
224                $self, -1,
225                Wx::gettext('&Matching Items:')
226        );
227
228        $self->_matches_list(
229                Wx::ListBox->new(
230                        $self, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, [],
231                        Wx::wxLB_EXTENDED
232                )
233        );
234
235        # Shows how many items are selected and information about what is selected
236        $self->_status_text(
237                Wx::TextCtrl->new(
238                        $self,                 -1,                Wx::gettext('Current Directory: ') . $self->_directory,
239                        Wx::wxDefaultPosition, Wx::wxDefaultSize, Wx::wxTE_READONLY
240                )
241        );
242
243        my $folder_image = Wx::StaticBitmap->new(
244                $self, -1,
245                Padre::Wx::Icon::find("places/stock_folder")
246        );
247
248        $self->_copy_button(
249                Wx::BitmapButton->new(
250                        $self, -1,
251                        Padre::Wx::Icon::find("actions/edit-copy")
252                )
253        );
254
255
256        $self->_popup_button(
257                Wx::BitmapButton->new(
258                        $self, -1,
259                        Padre::Wx::Icon::find("actions/down")
260                )
261        );
262        $self->_popup_menu( Wx::Menu->new );
263        $self->_skip_vcs_files(
264                $self->_popup_menu->AppendCheckItem( -1, Wx::gettext("Skip version control system files") ) );
265        $self->_skip_using_manifest_skip(
266                $self->_popup_menu->AppendCheckItem( -1, Wx::gettext("Skip using MANIFEST.SKIP") ) );
267
268        $self->_skip_vcs_files->Check(1);
269        $self->_skip_using_manifest_skip->Check(1);
270
271        my $hb;
272        $self->_sizer->AddSpacer(10);
273        $self->_sizer->Add( $search_label, 0, Wx::wxALL | Wx::wxEXPAND, 2 );
274        $hb = Wx::BoxSizer->new(Wx::wxHORIZONTAL);
275        $hb->AddSpacer(2);
276        $hb->Add( $self->_search_text,  1, Wx::wxALIGN_CENTER_VERTICAL, 2 );
277        $hb->Add( $self->_popup_button, 0, Wx::wxALL | Wx::wxEXPAND,    2 );
278        $hb->AddSpacer(1);
279        $self->_sizer->Add( $hb,                  0, Wx::wxBOTTOM | Wx::wxEXPAND, 5 );
280        $self->_sizer->Add( $matches_label,       0, Wx::wxALL | Wx::wxEXPAND,    2 );
281        $self->_sizer->Add( $self->_matches_list, 1, Wx::wxALL | Wx::wxEXPAND,    2 );
282        $hb = Wx::BoxSizer->new(Wx::wxHORIZONTAL);
283        $hb->AddSpacer(2);
284        $hb->Add( $folder_image,       0, Wx::wxALL | Wx::wxEXPAND,    1 );
285        $hb->Add( $self->_status_text, 1, Wx::wxALIGN_CENTER_VERTICAL, 1 );
286        $hb->Add( $self->_copy_button, 0, Wx::wxALL | Wx::wxEXPAND,    1 );
287        $hb->AddSpacer(1);
288        $self->_sizer->Add( $hb, 0, Wx::wxBOTTOM | Wx::wxEXPAND, 5 );
289        $self->_setup_events();
290
291        return;
292}
293
294#
295# Adds various events
296#
297sub _setup_events {
298        my $self = shift;
299
300        Wx::Event::EVT_CHAR(
301                $self->_search_text,
302                sub {
303                        my $this  = shift;
304                        my $event = shift;
305                        my $code  = $event->GetKeyCode;
306
307                        $self->_matches_list->SetFocus
308                                if ( $code == Wx::WXK_DOWN )
309                                or ( $code == Wx::WXK_NUMPAD_PAGEDOWN )
310                                or ( $code == Wx::WXK_PAGEDOWN );
311
312                        $event->Skip(1);
313                }
314        );
315
316        Wx::Event::EVT_TEXT(
317                $self,
318                $self->_search_text,
319                sub {
320
321                        if ( not $self->_matched_files ) {
322                                $self->_search();
323                        }
324                        $self->_update_matches_list_box;
325
326                        return;
327                }
328        );
329
330        Wx::Event::EVT_LISTBOX(
331                $self,
332                $self->_matches_list,
333                sub {
334                        my $self         = shift;
335                        my @matches      = $self->_matches_list->GetSelections();
336                        my $num_selected = scalar @matches;
337                        if ( $num_selected == 1 ) {
338                                $self->_status_text->ChangeValue( $self->_path( $self->_matches_list->GetClientData( $matches[0] ) ) );
339                                $self->_copy_button->Enable(1);
340                        } elsif ( $num_selected > 1 ) {
341                                $self->_status_text->ChangeValue( $num_selected . " items selected" );
342                                $self->_copy_button->Enable(0);
343                        } else {
344                                $self->_status_text->ChangeValue('');
345                                $self->_copy_button->Enable(0);
346                        }
347
348                        return;
349                }
350        );
351
352        Wx::Event::EVT_LISTBOX_DCLICK(
353                $self,
354                $self->_matches_list,
355                sub {
356                        $self->_on_ok_button_clicked();
357                }
358        );
359
360        Wx::Event::EVT_BUTTON(
361                $self,
362                $self->_copy_button,
363                sub {
364                        my @matches      = $self->_matches_list->GetSelections();
365                        my $num_selected = scalar @matches;
366                        if ( $num_selected == 1 ) {
367                                if ( Wx::wxTheClipboard->Open() ) {
368                                        Wx::wxTheClipboard->SetData(
369                                                Wx::TextDataObject->new( $self->_matches_list->GetClientData( $matches[0] ) ) );
370                                        Wx::wxTheClipboard->Close();
371                                }
372                        }
373                }
374        );
375
376        Wx::Event::EVT_MENU(
377                $self,
378                $self->_skip_vcs_files,
379                sub { $self->_restart_search; },
380        );
381        Wx::Event::EVT_MENU(
382                $self,
383                $self->_skip_using_manifest_skip,
384                sub { $self->_restart_search; },
385        );
386
387        Wx::Event::EVT_BUTTON(
388                $self,
389                $self->_popup_button,
390                sub {
391                        my ( $self, $event ) = @_;
392                        $self->PopupMenu(
393                                $self->_popup_menu,
394                                $self->_popup_button->GetPosition->x,
395                                $self->_popup_button->GetPosition->y + $self->_popup_button->GetSize->GetHeight
396                        );
397                }
398        );
399
400        $self->_show_recent_while_idle;
401}
402
403#
404# Restarts search
405#
406sub _restart_search {
407        my $self = shift;
408        $self->_search();
409        $self->_update_matches_list_box;
410}
411
412#
413# Focus on it if it shown or restart its state and show it if it is hidden.
414#
415sub show {
416        my $self = shift;
417
418        $self->init_search;
419
420        if ( $self->IsShown ) {
421                $self->SetFocus;
422        } else {
423                my $editor = $self->current->editor;
424                if ($editor) {
425                        my $selection        = $editor->GetSelectedText;
426                        my $selection_length = length $selection;
427                        if ( $selection_length > 0 ) {
428                                $self->_search_text->ChangeValue($selection);
429                                $self->_restart_search;
430                        } else {
431                                $self->_search_text->ChangeValue('');
432                        }
433                } else {
434                        $self->_search_text->ChangeValue('');
435                }
436
437                $self->_show_recent_while_idle;
438
439                $self->Show(1);
440        }
441}
442
443#
444# Shows recently opened stuff while idle
445#
446sub _show_recent_while_idle {
447        my $self = shift;
448
449        Wx::Event::EVT_IDLE(
450                $self,
451                sub {
452                        $self->_show_recently_opened_resources;
453
454                        # focus on the search text box
455                        $self->_search_text->SetFocus;
456
457                        # unregister from idle event
458                        Wx::Event::EVT_IDLE( $self, undef );
459                }
460        );
461}
462
463#
464# Shows the recently opened resources
465#
466sub _show_recently_opened_resources {
467        my $self = shift;
468
469        # Fetch them from Padre's RecentlyUsed database table
470        require Padre::DB::RecentlyUsed;
471        my $recently_used = Padre::DB::RecentlyUsed->select( "where type = ?", 'RESOURCE' ) || [];
472        my @recent_files = ();
473        foreach my $e (@$recently_used) {
474                push @recent_files, $self->_path( $e->value );
475        }
476        @recent_files = sort { File::Basename::fileparse($a) cmp File::Basename::fileparse($b) } @recent_files;
477
478        # Show results in matching items list
479        $self->_matched_files( \@recent_files );
480        $self->_update_matches_list_box;
481
482        # No need to store them anymore
483        $self->_matched_files(undef);
484}
485
486#
487# Search for files and cache result
488#
489sub _search {
490        my $self = shift;
491
492        $self->_status_text->ChangeValue( Wx::gettext("Reading items. Please wait...") );
493
494        require Padre::Task::OpenResource::SearchTask;
495        my $search_task = Padre::Task::OpenResource::SearchTask->new(
496                dialog                   => $self,
497                directory                => $self->_directory,
498                skip_vcs_files           => $self->_skip_vcs_files->IsChecked,
499                skip_using_manifest_skip => $self->_skip_using_manifest_skip->IsChecked,
500        );
501        $search_task->schedule;
502
503        return;
504}
505
506#
507# Update matches list box from matched files list
508#
509sub _update_matches_list_box {
510        my $self = shift;
511
512        return if not $self->_matched_files;
513
514        my $search_expr = $self->_search_text->GetValue();
515
516        #quote the search string to make it safer
517        #and then tranform * and ? into .* and .
518        $search_expr = quotemeta $search_expr;
519        $search_expr =~ s/\\\*/.*?/g;
520        $search_expr =~ s/\\\?/./g;
521
522        #Populate the list box now
523        $self->_matches_list->Clear();
524        my $pos = 0;
525        foreach my $file ( @{ $self->_matched_files } ) {
526                my $filename = File::Basename::fileparse($file);
527                if ( $filename =~ /^$search_expr/i ) {
528
529                        # Display package name if it is a Perl file
530                        my $pkg = '';
531                        my $mime_type = Padre::MimeTypes->guess_mimetype( undef, $file );
532                        if ( $mime_type eq 'application/x-perl' or $mime_type eq 'application/x-perl6' ) {
533                                my $contents = Padre::Util::slurp($file);
534                                if ( $contents && $$contents =~ /\s*package\s+(.+);/ ) {
535                                        $pkg = "  ($1)";
536                                }
537                        }
538                        $self->_matches_list->Insert( $filename . $pkg, $pos, $file );
539                        $pos++;
540                }
541        }
542        if ( $pos > 0 ) {
543                $self->_matches_list->Select(0);
544                $self->_status_text->ChangeValue( $self->_path( $self->_matches_list->GetClientData(0) ) );
545                $self->_status_text->Enable(1);
546                $self->_copy_button->Enable(1);
547                $self->{ok_button}->Enable(1);
548        } else {
549                $self->_status_text->ChangeValue('');
550                $self->_status_text->Enable(0);
551                $self->_copy_button->Enable(0);
552                $self->{ok_button}->Enable(0);
553        }
554
555        return;
556}
557
558#
559# Cleans a path on various platforms
560#
561sub _path {
562        my ( $self, $path ) = @_;
563        if (Padre::Constant::WIN32) {
564                $path =~ s/\//\\/g;
565        }
566        return $path;
567}
568
5691;
570
571__END__
572
573=head1 NAME
574
575Padre::Wx::Dialog::OpenResource - Open Resource dialog
576
577=head1 DESCRIPTION
578
579=head2 Open Resource (Shortcut: C<Ctrl+Shift+R>)
580
581This opens a nice dialog that allows you to find any file that exists
582in the current document or working directory. You can use C<?> to replace
583a single character or C<*> to replace an entire string. The matched files list
584are sorted alphabetically and you can select one or more files to be opened in
585Padre when you press the B<OK> button.
586
587You can simply ignore F<CVS>, F<.svn> and F<.git> folders using a simple check-box
588(enhancement over Eclipse).
589
590=head1 AUTHOR
591
592Ahmad M. Zawawi C<< <ahmad.zawawi at gmail.com> >>
593
594=head1 COPYRIGHT & LICENSE
595
596Copyright 2008-2010 The Padre development team as listed in Padre.pm.
597
598This program is free software; you can redistribute
599it and/or modify it under the same terms as Perl itself.
600
601=cut
602
603# Copyright 2008-2010 The Padre development team as listed in Padre.pm.
604# LICENSE
605# This program is free software; you can redistribute it and/or
606# modify it under the same terms as Perl 5 itself.
Note: See TracBrowser for help on using the browser.