File Coverage

blib/lib/PerlFM.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package PerlFM;
2              
3 1     1   24936 use warnings;
  1         3  
  1         33  
4 1     1   6 use strict;
  1         2  
  1         47  
5 1     1   399 use Gtk2;
  0            
  0            
6             use Gtk2::PathButtonBar;
7             use Gtk2::SimpleList;
8             use Cwd qw(chdir abs_path cwd);
9             use ZConf::Runner;
10             use File::Stat::Bits;
11             use String::ShellQuote;
12             use ZConf::Bookmarks;
13             use Gtk2::Chmod;
14             use File::MimeInfo::Magic;
15             use Dir::Watch;
16              
17             =head1 NAME
18              
19             PerlFM - A Perl based file manager.
20              
21             =head1 VERSION
22              
23             Version 0.2.0
24              
25             =cut
26              
27             our $VERSION = '0.2.0';
28              
29              
30             =head1 SYNOPSIS
31              
32             use PerlFM;
33             use Gtk2;
34            
35             my $pfm = PerlFM->new();
36            
37             Gtk2->init;
38            
39             my $window = Gtk2::Window->new();
40            
41             my %gui=$pfm->filemanager;
42            
43             $window->add($gui->{VB});
44            
45             $window->show;
46            
47             Gtk2-main;
48              
49             =head1 METHODES
50              
51             =head2 new
52              
53             Initiates the new function.
54              
55             =cut
56              
57             sub new {
58             my %args;
59             if(defined($_[1])){
60             %args= %{$_[1]};
61             }
62            
63             my $self={error=>undef, errorString=>'', defaultAction=>'view'};
64             bless $self;
65            
66             Gtk2->init;
67              
68             $self->{zcr}=ZConf::Runner->new;
69             $self->{zcbm}=ZConf::Bookmarks->new;
70              
71             return $self;
72             }
73              
74             =head2 addBM
75              
76             This is the call back that is called when the addBM button is clicked.
77              
78             =cut
79              
80             sub addBM{
81             my %h;
82             if (defined($_[1])) {
83             %h=%{$_[1]};
84             }
85              
86             my @selected=$_[1]{gui}{list}->get_selected_indices;
87            
88             my $entry=$_[1]{self}{data}{ $_[1]{gui}{id} }{data}{reverse}[$selected[0]];
89              
90             my $path=cwd().'/'.$entry;
91              
92             my $text='';
93             my $window = Gtk2::Dialog->new($text,
94             undef,
95             [qw/modal destroy-with-parent/],
96             'gtk-cancel' => 'cancel',
97             'gtk-ok' => 'accept',
98             );
99            
100             $window->set_position('center-always');
101            
102             $window->set_response_sensitive ('accept', 0);
103             $window->set_response_sensitive ('reject', 0);
104            
105             my $vbox = $window->vbox;
106             $vbox->set_border_width(5);
107            
108             my $label = Gtk2::Label->new_with_mnemonic('Add a new bookmark?');
109             $vbox->pack_start($label, 0, 0, 1);
110             $label->show;
111              
112             #path stuff
113             my $phbox=Gtk2::HBox->new;
114             $phbox->show;
115             my $plabel=Gtk2::Label->new('path: ');
116             $plabel->show;
117             $phbox->pack_start($plabel, 0, 1, 0);
118             my $pentry = Gtk2::Entry->new();
119             $pentry->set_editable(0);
120             $pentry->set_text($path);
121             $pentry->show;
122             $phbox->pack_start($pentry, 1, 1, 0);
123             $vbox->pack_start($phbox, 0, 0, 1);
124            
125             #name stuff
126             my $nhbox=Gtk2::HBox->new;
127             $nhbox->show;
128             my $nlabel=Gtk2::Label->new('name: ');
129             $nlabel->show;
130             $nhbox->pack_start($nlabel, 0, 1, 0);
131             my $nentry = Gtk2::Entry->new();
132             $nentry->set_text($path);
133             $nhbox->pack_start($nentry, 1, 1, 0);
134             $nentry->show;
135             $vbox->pack_start($nhbox, 0, 0, 1);
136              
137             #description stuff
138             my $dhbox=Gtk2::HBox->new;
139             $dhbox->show;
140             my $dlabel=Gtk2::Label->new('description: ');
141             $dlabel->show;
142             $dhbox->pack_start($dlabel, 0, 1, 0);
143             my $dentry = Gtk2::Entry->new();
144             $dentry->set_text($path);
145             $dhbox->pack_start($dentry, 1, 1, 0);
146             $dentry->show;
147             $vbox->pack_start($dhbox, 0, 0, 1);
148              
149             $dentry->signal_connect (changed => sub {
150             my $text = $dentry->get_text;
151             $window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
152             $window->set_response_sensitive ('reject', 1);
153             }
154             );
155              
156             $nentry->signal_connect (changed => sub {
157             my $text = $nentry->get_text;
158             $window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
159             $window->set_response_sensitive ('reject', 1);
160             }
161             );
162            
163             my $name;
164             my $description;
165             my $pressed;
166            
167             $window->signal_connect(response => sub {
168             $name=$nentry->get_text;
169             $description=$dentry->get_text;
170             $pressed=$_[1];
171             }
172             );
173             #runs the dailog and gets the response
174             #'cancel' means the user decided not to create a new set
175             #'accept' means the user wants to create a new set with the entered name
176             my $response=$window->run;
177            
178             $window->destroy;
179              
180             if ($pressed ne 'accept') {
181             #update the stuff
182             $_[1]{self}->update( $_[1]{gui}{id}, $_[1]{self} );
183             return undef;
184             }
185              
186             #add the bookmark
187             $_[1]{self}{zcbm}->addBookmark({
188             scheme=>'file',
189             name=>$name,
190             link=>$path,
191             description=>$description,
192             });
193            
194             #update the stuff
195             $_[1]{self}->update( $_[1]{gui}{id}, $_[1]{self} );
196             $_[1]{self}->updateBM( \%{$_[1]{gui}}, $_[1]{self} );
197             }
198              
199             =head2 app
200              
201             This invokes it as application.
202              
203             Upon the window being destroyed, it will exit.
204              
205             This method does not return. Upon being called it creates
206             a window and when that window is destroyed, it exits.
207              
208             =head3 args hash
209              
210             =head4 path
211              
212             This is the path to start in.
213              
214             =head4 hidden
215              
216             If this is set to true, hidden files will be shown.
217              
218             $args{path}='/tmp';
219             $args{hidden}=0;
220             $pfm->app(\%args);
221              
222             =cut
223              
224             sub app{
225             my $self=$_[0];
226             my %args;
227             if (defined($_[1])) {
228             %args=%{$_[1]};
229             }
230              
231             my %window=$self->window();
232             $window{window}->show;
233              
234             $window{window}->signal_connect('delete-event'=>sub{
235             exit 0;
236             }
237             );
238              
239             Gtk2->main;
240             }
241              
242             =head2 askYN
243              
244             This is used in a few places to present a yes/no dialog.
245              
246              
247              
248             =cut
249              
250             sub askYN{
251             my $text=$_[0];
252              
253             my $window = Gtk2::Dialog->new($text,
254             undef,
255             [qw/modal destroy-with-parent/],
256             'gtk-cancel' => 'cancel',
257             'gtk-ok' => 'ok',
258             );
259            
260             $window->set_position('center-always');
261              
262             $window->set_response_sensitive ('accept', 0);
263             $window->set_response_sensitive ('reject', 0);
264              
265             my $vbox = $window->vbox;
266             $vbox->set_border_width(5);
267              
268             my $label = Gtk2::Label->new_with_mnemonic($text);
269             $vbox->pack_start($label, 0, 0, 1);
270             $label->show;
271              
272             my $pressed;
273              
274             $window->signal_connect(response => sub {
275             $pressed=$_[1];
276             }
277             );
278             #runs the dailog and gets the response
279             #'cancel' means the user decided not to create a new set
280             #'ok' means the user wants to create a new set with the entered name
281             my $response=$window->run;
282              
283             $window->destroy;
284              
285             return $pressed;
286             }
287              
288             =head2 checkForUpdate
289              
290             This checks for any updates to a directory.
291              
292             One arguement is accepted and it is a
293              
294             $pfm->checkForUpdate($guiID);
295              
296             =cut
297              
298             sub checkForUpdate{
299             my $self=$_[0];
300             my $guiID=$_[1];
301              
302             if (!defined($self->{gui}{$guiID})) {
303             return undef;
304             }
305              
306             if ( $self->{gui}{$guiID}{watcher}->check() ) {
307             $self->update($guiID, $self);
308             }
309              
310             return 1;
311             }
312              
313             =head2 chmod
314              
315             This is the call back that is called when a chmod key/button is pressed.
316              
317             =cut
318              
319             sub chmod{
320             my @selected=$_[1]->{gui}{list}->get_selected_indices;
321            
322             #get the entry
323             my $entry=$_[1]{self}{data}{ $_[1]{gui}{id} }{data}{reverse}[$selected[0]];
324              
325             #
326             my %returned=Gtk2::Chmod->ask($entry);
327              
328             #return if ok was pressed
329             if ($returned{pressed} ne 'ok') {
330             #update the stuff
331             $_[1]{self}->update( $_[1]{gui}{id}, $_[1]{self} );
332             return undef;
333             }
334              
335             #process each entry
336             my $int=0;
337             while (defined($selected[$int])) {
338             $entry=$_[1]{self}{data}{ $_[1]{gui}{id} }{data}{reverse}[$selected[$int]];
339            
340             #choose the proper method for file/directory
341             if (-d $entry) {
342             #use chmod binary if needed
343             if ($returned{recursive}) {
344             system('chmod -R '.shell_quote($returned{dirmode}).' '.shell_quote($entry) );
345             }else {
346             chmod(oct($returned{dirmode}), $entry);
347             }
348             }else {
349             chmod(oct($returned{filemode}), $entry);
350             }
351              
352             $int++;
353             }
354              
355             #update the stuff
356             $_[1]{self}->update( $_[1]{gui}{id}, $_[1]{self} );
357             }
358              
359             =head2 chown
360              
361             This is the call back that is called when a mkdir key/button is pressed.
362              
363             =cut
364              
365             sub chown{
366             my $text='';
367             my $window = Gtk2::Dialog->new($text,
368             undef,
369             [qw/modal destroy-with-parent/],
370             'gtk-cancel' => 'cancel',
371             'gtk-save' => 'accept',
372             );
373            
374             $window->set_position('center-always');
375            
376             $window->set_response_sensitive ('accept', 0);
377             $window->set_response_sensitive ('reject', 0);
378            
379             my $vbox = $window->vbox;
380             $vbox->set_border_width(5);
381            
382             my $label = Gtk2::Label->new_with_mnemonic('Change user/group ownership?');
383             $vbox->pack_start($label, 0, 0, 1);
384             $label->show;
385              
386             #group stuff
387             my $ghbox=Gtk2::HBox->new;
388             $ghbox->show;
389             my $glabel=Gtk2::Label->new('group: ');
390             $glabel->show;
391             $ghbox->pack_start($glabel, 0, 1, 0);
392             my $gentry = Gtk2::Entry->new();
393             $gentry->show;
394             $ghbox->pack_start($gentry, 0, 1, 0);
395             $vbox->pack_start($ghbox, 0, 0, 1);
396            
397             #user stuff
398             my $uhbox=Gtk2::HBox->new;
399             $uhbox->show;
400             my $ulabel=Gtk2::Label->new('user');
401             $ulabel->show;
402             $uhbox->pack_start($ulabel, 0, 1, 0);
403             my $uentry = Gtk2::Entry->new();
404             $uhbox->pack_start($uentry, 0, 1, 0);
405             $uentry->show;
406             $vbox->pack_start($uhbox, 0, 0, 1);
407              
408             #check button
409             my $recursivecheck=Gtk2::CheckButton->new('recursive');
410             $recursivecheck->show;
411             $vbox->pack_start($recursivecheck, 0, 0, 1);
412            
413             $uentry->signal_connect (changed => sub {
414             my $text = $uentry->get_text;
415             $window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
416             $window->set_response_sensitive ('reject', 1);
417             }
418             );
419              
420             $gentry->signal_connect (changed => sub {
421             my $text = $gentry->get_text;
422             $window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
423             $window->set_response_sensitive ('reject', 1);
424             }
425             );
426            
427             my $user;
428             my $group;
429             my $pressed;
430            
431             $window->signal_connect(response => sub {
432             $user=$uentry->get_text;
433             $group=$gentry->get_text;
434             $pressed=$_[1];
435             }
436             );
437             #runs the dailog and gets the response
438             #'cancel' means the user decided not to create a new set
439             #'accept' means the user wants to create a new set with the entered name
440             my $response=$window->run;
441            
442             $window->destroy;
443              
444             if ($pressed eq 'reject') {
445             return undef;
446             }
447              
448             #set the pressed to reject if
449             if (($user eq '' )&&($group eq '')) {
450             $pressed='reject'
451             }
452              
453             #convert the user to a uid if the ownership is not a digit
454             if ($user !~ /[[:digit:]]/) {
455             my ($login, $pass, $uid)=getpwnam($user);
456             if (defined($uid)) {
457             $user=$uid;
458             }
459             }
460              
461             #convert the user to a uid if the ownership is not a digit
462             if ($group !~ /[[:digit:]]/) {
463             my ($name,$passwd,$gid,$members)=getgrnam($user);
464             if (defined($gid)) {
465             $group=$gid;
466             }
467             }
468              
469             #gets the data
470             my %data=$_[1]{self}->datahash($_[1]{gui}{check}->get_active);
471              
472             #get the entries in question
473             my @entries;
474             my @selected=$_[1]{gui}{list}->get_selected_indices;
475             my $int=0;
476             while (defined($selected[$int])) {
477             my $entry=$data{reverse}[$selected[$int]];
478              
479             push(@entries, $entry);
480            
481             $int++;
482             }
483              
484              
485             #chown it
486             chown($user, $group, @entries);
487              
488             #update the stuff
489             $_[1]{self}->update( $_[1]{gui}{id}, $_[1]{self} );
490             }
491              
492              
493             =head2 datahash
494              
495             This builds the data hash for the current directory. This is primarily for
496             internal use.
497              
498             =cut
499              
500             sub datahash{
501             my $self=$_[0];
502             my $hidden=$_[1];
503            
504             my $path=cwd;
505            
506             my %data;
507             $data{names}={};
508              
509             #populates data hash
510             opendir(FILEMANAGER, $path);
511             my $entry=readdir(FILEMANAGER);
512             while (defined($entry)) {
513             my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat($entry);
514              
515             my $add=1;
516              
517             #if it is a hidden file, check if it should be added or not
518             if ($entry =~ /^\./) {
519             $add=0;
520             if ($hidden || (($entry eq '.')||($entry eq '..')) ) {
521             $add=1;
522             }
523             }
524              
525             if ($add) {
526             if (-d $entry) {
527             $entry=$entry.'/';
528             }
529            
530             $data{names}{$entry}={
531             dev=>$dev, inode=>$ino, mode=>$mode, nlink=>$nlink,
532             uid=>$uid, gid=>$gid, rdev=>$rdev, size=>$size,
533             atime=>$atime, mtime=>$mtime, ctime=>$ctime,
534             blksize=>$blksize, blocks=>$blocks,
535             };
536            
537             }
538             $entry=readdir(FILEMANAGER);
539             }
540             closedir(FILEMANAGER);
541            
542             #sort the entries
543             my @entries=keys(%{$data{names}});
544             @entries=sort(@entries);
545              
546             #puts them all together
547             my @r1;
548             my @r2;
549             my $int=0;
550             while (defined($entries[$int])) {
551             if (-d $entries[$int]) {
552             push(@r1, $entries[$int]);
553             }else {
554             push(@r2, $entries[$int]);
555             }
556              
557             $int++;
558             }
559             my @sortedentries=@r1;
560             push(@sortedentries, @r2);
561              
562             $data{reverse}=\@sortedentries;
563              
564             #
565             my @dirs;
566             $int=0;
567             while (defined($entries[$int])) {
568             if (-d $entries[$int]) {
569             push(@dirs, $entries[$int]);
570             }
571              
572             $int++;
573             }
574              
575             my @sorteddirs=sort(@dirs);
576              
577             $data{dirreverse}=\@sorteddirs;
578              
579             #this puts together the mode strings
580             my $mode='';
581             $int=0;
582             while (defined( $data{reverse}[$int] )) {
583             my $entry=$data{reverse}[$int];
584             my $bmode=$data{names}{$entry}{mode};
585              
586             #user read
587             if (S_IRUSR & $bmode) {
588             $mode=$mode.'r';
589             }else {
590             $mode=$mode.'-';
591             }
592             #user write
593             if (S_IWUSR & $bmode) {
594             $mode=$mode.'w';
595             }else {
596             $mode=$mode.'-';
597             }
598             #user exec
599             if (S_ISUID & $bmode) {
600             $mode=$mode.'s';
601             }else {
602             if (S_IXUSR & $bmode) {
603             $mode=$mode.'x';
604             }else {
605             $mode=$mode.'-';
606             }
607             }
608              
609             #group read
610             if (S_IRGRP & $bmode) {
611             $mode=$mode.'r';
612             }else {
613             $mode=$mode.'-';
614             }
615             #group write
616             if (S_IWGRP & $bmode) {
617             $mode=$mode.'w';
618             }else {
619             $mode=$mode.'-';
620             }
621             #group exec
622             if (S_ISGID & $bmode) {
623             $mode=$mode.'s';
624             }else {
625             if (S_IXGRP & $bmode) {
626             $mode=$mode.'x';
627             }else {
628             $mode=$mode.'-';
629             }
630             }
631              
632             #other read
633             if (S_IROTH & $bmode) {
634             $mode=$mode.'r';
635             }else {
636             $mode=$mode.'-';
637             }
638             #other write
639             if (S_IWOTH & $bmode) {
640             $mode=$mode.'w';
641             }else {
642             $mode=$mode.'-';
643             }
644             #other exec
645             if (S_IXOTH & $bmode) {
646             $mode=$mode.'x';
647             }else {
648             $mode=$mode.'-';
649             }
650              
651             $data{names}{$entry}{mode}=$mode;
652              
653             $mode='';
654             $int++;
655             }
656              
657             return %data;
658             }
659              
660             =head2 delete
661              
662             This is a call back the handles deleting files.
663              
664             =cut
665              
666             sub delete{
667              
668             #ask if it should delete them
669             my $returned=askYN('Delete the selected files?');
670             #if not, return
671             if ($returned ne 'ok') {
672             #update the stuff
673             $_[1]{self}->update( $_[1]{gui}{id}, $_[1]{self} );
674             return undef;
675             }
676            
677              
678             my @selected=$_[1]->{gui}{list}->get_selected_indices;
679              
680             #gets the data
681             #my %data=$_[1]{self}->datahash($_[1]{gui}{check}->get_active);
682            
683              
684             my $int=0;
685             while (defined($selected[$int])) {
686             my $entry=$_[1]{self}{data}{ $_[1]{gui}{id} }{data}{reverse}[$selected[$int]];
687             if (-d $entry) {
688             rmdir($entry);
689             }
690             if (-f $entry) {
691             unlink($entry);
692             }
693              
694             $int++;
695             }
696              
697             #update the stuff
698             $_[1]{self}->update( $_[1]{gui}{id}, $_[1]{self} );
699             }
700              
701             =head2 deleteBM
702              
703             This is the call back used when the currently seleced book mark
704             is being deleted.
705              
706             =cut
707              
708             sub deleteBM{
709             #ask if it should delete them
710             my $returned=askYN('Delete the selected bookmarks?');
711             #if not, return
712             if ($returned ne 'ok') {
713             #update the stuff
714             $_[1]{self}->update( $_[1]{gui}{id}, $_[1]{self} );
715             return undef;
716             }
717              
718             my @selected=$_[1]->{gui}{bmlist}->get_selected_indices;
719            
720             my $int=0;
721             while (defined($selected[$int])) {
722             my $bmID=$_[1]{self}->{bookmarkReverse}[$selected[$int]];
723              
724             $_[1]{self}->{zcbm}->delBookmark('file', $bmID);
725              
726             $int++;
727             }
728            
729             #update the stuff
730             $_[1]{self}->update( $_[1]{gui}{id}, $_[1]{self} );
731             $_[1]{self}->updateBM( \%{$_[1]{gui}}, $_[1]{self} );
732             }
733              
734             =head2 editBM
735              
736             This is the call back used for editing the current bookmark.
737              
738             =cut
739              
740             sub editBM{
741             my @selected=$_[1]->{gui}{bmlist}->get_selected_indices;
742              
743             #make sure something is selected
744             if (!defined($selected[0])) {
745             return undef;
746             }
747              
748             #gets the bookmark ID
749             my $bmid=$_[1]{self}->{bookmarkReverse}[$selected[0]];
750              
751             if (!defined($bmid)) {
752             return undef;
753             }
754              
755             #gets the bookmark
756             my %bookmark=$_[1]{self}->{zcbm}->getBookmark('file', $bmid);
757             if (!defined( $bookmark{name} )) {
758             return undef;
759             }
760              
761             my $window = Gtk2::Dialog->new('',
762             undef,
763             [qw/modal destroy-with-parent/],
764             'gtk-cancel' => 'cancel',
765             'gtk-ok' => 'accept',
766             );
767            
768             $window->set_position('center-always');
769            
770             $window->set_response_sensitive ('accept', 0);
771             $window->set_response_sensitive ('reject', 0);
772            
773             my $vbox = $window->vbox;
774             $vbox->set_border_width(5);
775            
776             my $label = Gtk2::Label->new_with_mnemonic('Edit a bookmark?');
777             $vbox->pack_start($label, 0, 0, 1);
778             $label->show;
779              
780             #path stuff
781             my $phbox=Gtk2::HBox->new;
782             $phbox->show;
783             my $plabel=Gtk2::Label->new('path: ');
784             $plabel->show;
785             $phbox->pack_start($plabel, 0, 1, 0);
786             my $pentry = Gtk2::Entry->new();
787             $pentry->set_editable(1);
788             $pentry->set_text($bookmark{link});
789             $pentry->show;
790             $phbox->pack_start($pentry, 1, 1, 0);
791             $vbox->pack_start($phbox, 0, 0, 1);
792            
793             #name stuff
794             my $nhbox=Gtk2::HBox->new;
795             $nhbox->show;
796             my $nlabel=Gtk2::Label->new('name: ');
797             $nlabel->show;
798             $nhbox->pack_start($nlabel, 0, 1, 0);
799             my $nentry = Gtk2::Entry->new();
800             $nentry->set_text($bookmark{name});
801             $nhbox->pack_start($nentry, 1, 1, 0);
802             $nentry->show;
803             $vbox->pack_start($nhbox, 0, 0, 1);
804              
805             #description stuff
806             my $dhbox=Gtk2::HBox->new;
807             $dhbox->show;
808             my $dlabel=Gtk2::Label->new('description: ');
809             $dlabel->show;
810             $dhbox->pack_start($dlabel, 0, 1, 0);
811             my $dentry = Gtk2::Entry->new();
812             $dentry->set_text($bookmark{description});
813             $dhbox->pack_start($dentry, 1, 1, 0);
814             $dentry->show;
815             $vbox->pack_start($dhbox, 0, 0, 1);
816              
817             $dentry->signal_connect (changed => sub {
818             my $text = $dentry->get_text;
819             $window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
820             $window->set_response_sensitive ('reject', 1);
821             }
822             );
823              
824             $pentry->signal_connect (changed => sub {
825             my $text = $dentry->get_text;
826             $window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
827             $window->set_response_sensitive ('reject', 1);
828             }
829             );
830              
831             $nentry->signal_connect (changed => sub {
832             my $text = $nentry->get_text;
833             $window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
834             $window->set_response_sensitive ('reject', 1);
835             }
836             );
837            
838             my $name;
839             my $description;
840             my $pressed;
841             my $path;
842            
843             $window->signal_connect(response => sub {
844             $name=$nentry->get_text;
845             $description=$dentry->get_text;
846             $path=$pentry->get_text;
847             $pressed=$_[1];
848             }
849             );
850             #runs the dailog and gets the response
851             #'cancel' means the user decided not to create a new set
852             #'accept' means the user wants to create a new set with the entered name
853             my $response=$window->run;
854            
855             $window->destroy;
856              
857             if ($pressed ne 'accept') {
858             #update the stuff
859             $_[1]{self}->update( $_[1]{gui}{id}, $_[1]{self} );
860             return undef;
861             }
862              
863             #add the bookmark
864             $_[1]{self}{zcbm}->modBookmark({
865             scheme=>'file',
866             bmid=>$bmid,
867             name=>$name,
868             link=>$path,
869             description=>$description,
870             });
871            
872             #update the stuff
873             $_[1]{self}->update( $_[1]{gui}{id}, $_[1]{self} );
874             $_[1]{self}->updateBM( { gui=>$_[1]{gui}, self=>$_[1]{self} });
875              
876             }
877              
878             =head2 filemanager
879              
880             This returns a hash that contains the various elements.
881              
882             =head3 args hash
883              
884             =head4 path
885              
886             This is the path to start in.
887              
888             =head4 hidden
889              
890             If this is set to true, hidden files will be shown.
891              
892             $args{path}='/tmp';
893             $args{hidden}=0;
894            
895             my %gui=$pfm->filemanager(\%args);
896            
897             #get it again after it has been created
898             my $guiID=$gui{id};
899             %gui=%{$pfm->{gui}{$guID}};
900              
901             =cut
902              
903             sub filemanager{
904             my $self=$_[0];
905             my %args;
906             if(defined($_[1])){
907             %args= %{$_[1]};
908             }
909            
910             $self->errorblank;
911            
912             if (!defined($args{path})) {
913             $args{path}=cwd;
914             }
915              
916             #go to the specified path
917             chdir($args{path});
918              
919             #init the gui hash
920             my %gui;
921             $gui{id}=rand().rand();
922              
923             #turn on view hidden by default
924             $gui{hidden}=$args{hidden};
925              
926             #this is what will be returned
927             $gui{VB}=Gtk2::VBox->new;
928             $gui{VB}->show;
929              
930             #puts together the button box
931             $gui{buttonHB}=Gtk2::HBox->new;
932             $gui{buttonHB}->show;
933             #menu init
934             $gui{menubar}=Gtk2::MenuBar->new;
935             $gui{menubarmenu}=Gtk2::MenuItem->new('_m');
936             $gui{menubar}->show;
937             $gui{menubarmenu}->show;
938             $gui{menu}=Gtk2::Menu->new;
939             $gui{menu}->show;
940             $gui{menuTearoff}=Gtk2::TearoffMenuItem->new;
941             $gui{menuTearoff}->show;
942             $gui{menu}->append($gui{menuTearoff});
943             $gui{menubarmenu}->set_submenu($gui{menu});
944             $gui{menubar}->append($gui{menubarmenu});
945             #check
946             $gui{check}=Gtk2::CheckMenuItem->new('show _hidden');
947             $gui{check}->show;
948             $gui{check}->set_active($gui{hidden});
949             $gui{check}->signal_connect(toggled=>sub{
950             $_[1]{self}{gui}{ $_[1]{id} }{hidden}=$_[1]{self}{gui}{ $_[1]{id} }{check}->get_active;
951             $_[1]{self}->update( $_[1]{id}, $_[1]{self} );
952             },
953             {
954             self=>$self,
955             id=>$gui{id},
956             }
957             );
958             $gui{menu}->append($gui{check});
959             $gui{menuS0}=Gtk2::SeparatorMenuItem->new();
960             $gui{menuS0}->show;
961             $gui{menu}->append($gui{menuS0});
962             #delete menu item
963             $gui{delete}=Gtk2::MenuItem->new('_delete');
964             $gui{delete}->show;
965             $gui{delete}->signal_connect(activate=>\&delete,
966             {
967             gui=>\%gui,
968             self=>$self,
969             }
970             );
971             $gui{menu}->append($gui{delete});
972             #mkdir menu item
973             $gui{mkdir}=Gtk2::MenuItem->new('_mkdir');
974             $gui{mkdir}->show;
975             $gui{mkdir}->signal_connect(activate=>\&mkdir,
976             {
977             gui=>\%gui,
978             self=>$self,
979             }
980             );
981             $gui{menu}->append($gui{mkdir});
982             $gui{menuS1}=Gtk2::SeparatorMenuItem->new();
983             $gui{menuS1}->show;
984             $gui{menu}->append($gui{menuS1});
985             #chmod
986             $gui{chmod}=Gtk2::MenuItem->new('_chmod');
987             $gui{chmod}->show;
988             $gui{chmod}->signal_connect(activate=>\&chmod,
989             {
990             gui=>\%gui,
991             self=>$self,
992             }
993             );
994             $gui{menu}->append($gui{chmod});
995             #chown
996             $gui{chown}=Gtk2::MenuItem->new('ch_own');
997             $gui{chown}->show;
998             $gui{chown}->signal_connect(activate=>\&chown,
999             {
1000             gui=>\%gui,
1001             self=>$self,
1002             }
1003             );
1004             $gui{menu}->append($gui{chown});
1005             $gui{menuS2}=Gtk2::SeparatorMenuItem->new();
1006             $gui{menuS2}->show;
1007             $gui{menu}->append($gui{menuS2});
1008             #show directories
1009             $gui{showdirectories}=Gtk2::MenuItem->new('show directories (_l)');
1010             $gui{showdirectories}->show;
1011             $gui{showdirectories}->signal_connect(activate=>sub{
1012             #gets the current page
1013             my $cp=$_[1]{self}{gui}{ $_[1]{id} }{DBnotebook}->get_current_page;
1014            
1015             if ($cp ne '0') {
1016             $_[1]{self}{gui}{ $_[1]{id} }{DBnotebook}->set_current_page(0);
1017             $_[1]{self}{gui}{ $_[1]{id} }{hpaned}->set_position(230);
1018             $_[1]{self}{gui}{ $_[1]{id} }{dirlist}->grab_focus;
1019             }else {
1020             my $pos=$_[1]{self}{gui}{ $_[1]{id} }{hpaned}->get_position();
1021             if ($pos ne '0') {
1022             $_[1]{self}{gui}{ $_[1]{id} }{hpaned}->set_position(0);
1023             $_[1]{self}{gui}{ $_[1]{id} }{list}->grab_focus;
1024             }else {
1025             $_[1]{self}{gui}{ $_[1]{id} }{hpaned}->set_position(230);
1026             $_[1]{self}{gui}{ $_[1]{id} }{dirlist}->grab_focus;
1027             }
1028             }
1029             },
1030             {
1031             id=>$gui{id},
1032             self=>$self,
1033             }
1034             );
1035             $gui{menu}->append($gui{showdirectories});
1036             #show bookmarks
1037             $gui{showbookmarks}=Gtk2::MenuItem->new('show _bookmarks');
1038             $gui{showbookmarks}->show;
1039             $gui{showbookmarks}->signal_connect(activate=>sub{
1040             #gets the current page
1041             my $cp=$_[1]{self}{gui}{ $_[1]{id} }{DBnotebook}->get_current_page;
1042            
1043             if ($cp ne '1') {
1044             $_[1]{self}{gui}{ $_[1]{id} }{DBnotebook}->set_current_page(1);
1045             $_[1]{self}{gui}{ $_[1]{id} }{hpaned}->set_position(230);
1046             $_[1]{self}{gui}{ $_[1]{id} }{bmlist}->grab_focus;
1047             }else {
1048             my $pos=$_[1]{self}{gui}{ $_[1]{id} }{hpaned}->get_position();
1049             if ($pos ne '0') {
1050             $_[1]{self}{gui}{ $_[1]{id} }{hpaned}->set_position(0);
1051             $_[1]{self}{gui}{ $_[1]{id} }{list}->grab_focus;
1052             }else {
1053             $_[1]{self}{gui}{ $_[1]{id} }{hpaned}->set_position(230);
1054             $_[1]{self}{gui}{ $_[1]{id} }{bmlist}->grab_focus;
1055             }
1056             }
1057             },
1058             {
1059             id=>$gui{id},
1060             self=>$self,
1061             }
1062             );
1063             $gui{menu}->append($gui{showbookmarks});
1064             $gui{menuS3}=Gtk2::SeparatorMenuItem->new();
1065             $gui{menuS3}->show;
1066             $gui{menu}->append($gui{menuS3});
1067             #quit
1068             $gui{quit}=Gtk2::MenuItem->new('_quit');
1069             $gui{quit}->show;
1070             $gui{quit}->signal_connect(activate=>sub{
1071             Gtk2->main_quit;
1072             exit 0;
1073             },
1074             {
1075             id=>$gui{id},
1076             self=>$self,
1077             }
1078             );
1079             $gui{menu}->append($gui{quit});
1080             #put it together
1081             $gui{buttonHB}->pack_start($gui{menubar}, 0, 0, 0);
1082             $gui{VB}->pack_start($gui{buttonHB}, 0, 1, 0);
1083              
1084             #rmenu init
1085             $gui{rmenubarmenu}=Gtk2::MenuItem->new('_r');
1086             $gui{rmenubarmenu}->show;
1087             $gui{menubar}->append($gui{rmenubarmenu});
1088              
1089             #This is the pathbuttonbar
1090             $gui{PB}=Gtk2::PathButtonBar->new({
1091             exec=>'chdir("/".${$myself}->{path}); '.
1092             '${$myself}->{vars}{pfm}->update( ${$myself}->{vars}{id}, ${$myself}->{vars}{pfm} ); ',
1093             vars=>{
1094             pfm=>$self,
1095             id=>$gui{id},
1096             },
1097             });
1098             $gui{buttonHB}->pack_start($gui{PB}->{vbox}, 1, 1, 0);
1099            
1100             #init the hpaned
1101             $gui{hpaned}=Gtk2::HPaned->new;
1102             $gui{hpaned}->set_position(0);
1103             $gui{hpaned}->show;
1104             $gui{VB}->pack_start($gui{hpaned}, 1, 1, 0);
1105              
1106             #initialize the notebook
1107             $gui{DBnotebook}=Gtk2::Notebook->new;
1108             $gui{DBnotebook}->show;
1109             $gui{DBnotebookDL}=Gtk2::Label->new('Directories');
1110             $gui{DBnotebookDL}->show;
1111             $gui{DBnotebookDB}=Gtk2::Label->new('Bookmarks');
1112             $gui{DBnotebookDB}->show;
1113             $gui{hpaned}->add1($gui{DBnotebook});
1114              
1115             #the directory list
1116             $gui{dirlistSW}=Gtk2::ScrolledWindow->new;
1117             $gui{dirlistSW}->show;
1118             $gui{dirlist}=Gtk2::SimpleList->new(
1119             'Directories'=>'text',
1120             );
1121             $gui{dirlist}->get_selection->set_mode ('multiple');
1122             $gui{dirlist}->show;
1123             $gui{dirlist}->signal_connect(row_activated=>sub{
1124             my @selected=$_[3]->{self}{gui}{ $_[3]{id} }{dirlist}->get_selected_indices;
1125              
1126             chdir($_[3]{self}{data}{ $_[3]{id} }{data}{dirreverse}[$selected[0]]);
1127              
1128             $_[3]{self}->update( $_[3]{id}, $_[3]{self} );
1129             },
1130             {
1131             self=>$self,
1132             id=>$gui{id},
1133             }
1134             );
1135             $gui{dirlistSW}->add($gui{dirlist});
1136             $gui{DBnotebook}->append_page($gui{dirlistSW}, $gui{DBnotebookDL});
1137              
1138             #bookmark stuff
1139             $gui{bmlistVB}=Gtk2::VBox->new;
1140             $gui{bmlistVB}->show;
1141             $gui{bmlistSW}=Gtk2::ScrolledWindow->new;
1142             $gui{bmlistSW}->show;
1143             #put the buttons together for book marks
1144             $gui{bmlist}=Gtk2::SimpleList->new(
1145             'Bookmarks'=>'text',
1146             );
1147             $gui{bmlist}->get_selection->set_mode ('multiple');
1148             $gui{bmlist}->show;
1149             $gui{bmlist}->signal_connect(row_activated=>sub{
1150             my @selected=$_[3]->{self}{gui}{ $_[3]{id} }{bmlist}->get_selected_indices;
1151              
1152             my $bmID=$_[3]{self}->{bookmarkReverse}[$selected[0]];
1153              
1154             #get the bookmark and make sure we have a link
1155             my %bookmark=$_[3]{self}->{zcbm}->getBookmark('file', $bmID);
1156             if (!defined($bookmark{link})) {
1157             return undef;
1158             }
1159              
1160             #cd to the specified directory
1161             chdir($bookmark{link});
1162              
1163             #update it
1164             $_[3]{self}->update( $_[3]{id} , $_[3]{self} );
1165             },
1166             {
1167             self=>$self,
1168             id=>$gui{id},
1169             }
1170             );
1171             $gui{bmlistSW}->add($gui{bmlist});
1172             $gui{bmlistVB}->pack_start($gui{bmlistSW}, 1, 1, 0);
1173             #put the buttons together
1174             $gui{bmlistBB}=Gtk2::HBox->new;
1175             $gui{bmlistBB}->show;
1176             $gui{bmlistVB}->pack_start($gui{bmlistBB}, 0, 1, 0);
1177             #add
1178             $gui{bmlistAdd}=Gtk2::Button->new;
1179             $gui{bmlistAdd}->set_label('Add');
1180             $gui{bmlistAdd}->show;
1181             $gui{bmlistBB}->pack_start($gui{bmlistAdd}, 0, 1, 0);
1182             $gui{bmlistAdd}->signal_connect(clicked=>\&addBM,{self=>$self, gui=>\%gui});
1183             #del
1184             $gui{bmlistDel}=Gtk2::Button->new;
1185             $gui{bmlistDel}->set_label('Del');
1186             $gui{bmlistDel}->show;
1187             $gui{bmlistBB}->pack_start($gui{bmlistDel}, 0, 1, 0);
1188             $gui{bmlistDel}->signal_connect(clicked=>\&deleteBM,{self=>$self, gui=>\%gui});
1189             #edit
1190             $gui{bmlistEdit}=Gtk2::Button->new;
1191             $gui{bmlistEdit}->set_label('Edit');
1192             $gui{bmlistEdit}->show;
1193             $gui{bmlistBB}->pack_start($gui{bmlistEdit}, 0, 1, 0);
1194             $gui{bmlistEdit}->signal_connect(clicked=>\&editBM,{self=>$self, gui=>\%gui});
1195             #finish this tab
1196             $gui{DBnotebook}->append_page($gui{bmlistVB}, $gui{DBnotebookDB});
1197              
1198             #display the bookmark stuff by default
1199             $gui{DBnotebook}->set_current_page('0');
1200            
1201             #the list of names/files
1202             $gui{listSW}=Gtk2::ScrolledWindow->new;
1203             $gui{listSW}->show;
1204             $gui{list}=Gtk2::SimpleList->new(
1205             'Name'=>'text',
1206             'User'=>'text',
1207             'Group'=>'text',
1208             'Perms'=>'text',
1209             'Size'=>'text',
1210             'MTime'=>'text',
1211             'CTime'=>'text',
1212             'ATime'=>'text',
1213             );
1214             $gui{list}->get_selection->set_mode ('multiple');
1215             $gui{list}->show;
1216             $gui{list}->signal_connect(row_activated=>sub{
1217             my @selected=$_[3]->{self}{gui}{ $_[3]{id} }{list}->get_selected_indices;
1218              
1219             my $entry=$_[3]{self}{data}{ $_[3]{id} }{data}{reverse}[$selected[0]];
1220              
1221             if(-d $entry){
1222             chdir( $_[3]{self}{data}{ $_[3]{id} }{data}{reverse}[$selected[0]] );
1223             $_[3]{self}->update( $_[3]{id}, $_[3]{self} );
1224             }else {
1225             if (-x $entry) {
1226             #If it has a . in it, it may not be a executable file
1227             #but on a fat32 partition or the like.
1228             if ($entry =~ /\./) {
1229             system('zcrunner -o '.shell_quote($entry).' &');
1230             }else {
1231             system(shell_quote($entry).' &');
1232             }
1233             }else {
1234             system('zcrunner -o '.shell_quote($entry).' &');
1235             }
1236            
1237             }
1238             },
1239             {
1240             self=>$self,
1241             id=>$gui{id},
1242             }
1243             );
1244             $gui{list}->signal_connect('cursor-changed'=>sub{
1245             my $self=$_[1]{self};
1246             my $id=$_[1]{id};
1247            
1248             $self->updateRmenu($id);
1249             },
1250             {
1251             self=>$self,
1252             id=>$gui{id},
1253             }
1254             );
1255             $gui{listSW}->add($gui{list});
1256             $gui{hpaned}->add2($gui{listSW});
1257              
1258             #adds the watcher
1259             $gui{watcher}=Dir::Watch->new();
1260              
1261             $gui{timer}=Glib::Timeout->add('2000',
1262             sub{
1263             #this should never happen, but check any ways
1264             if (!defined( $_[0]{id} )) {
1265             return 0;
1266             }
1267             #remove it if needed
1268             if (!defined( $_[0]{self}->{gui}{$_[0]{id}} )) {
1269             return 0;
1270             }
1271              
1272             $_[0]{self}->checkForUpdate($_[0]{id});
1273             return 1;
1274             },
1275             {
1276             self=>$self,
1277             id=>$gui{id},
1278             }
1279             );
1280              
1281             #save the gui
1282             $self->{gui}{$gui{id}}=\%gui;
1283              
1284             $self->update($gui{id}, $self);
1285              
1286             #update the bookmarks
1287             $self->updateBM({ gui=>\%gui, self=>$self });
1288              
1289             return %gui;
1290             }
1291              
1292             =head2 getAction
1293              
1294             This fetches the default action.
1295              
1296             my $action=$self->getAction;
1297              
1298             =cut
1299              
1300             sub getAction{
1301             my $self=$_[0];
1302              
1303             if (!defined($self->{defaultAction})) {
1304             return 'view';
1305             }
1306              
1307             return $self->{defaultAction};
1308             }
1309              
1310             =head2 mkdir
1311              
1312             This is the call back that is called when a mkdir key/button is pressed.
1313              
1314             =cut
1315              
1316             sub mkdir{
1317             my $text='';
1318             my $window = Gtk2::Dialog->new($text,
1319             undef,
1320             [qw/modal destroy-with-parent/],
1321             'gtk-cancel' => 'cancel',
1322             'gtk-ok' => 'accept',
1323             );
1324            
1325             $window->set_position('center-always');
1326            
1327             $window->set_response_sensitive ('accept', 0);
1328             $window->set_response_sensitive ('reject', 0);
1329            
1330             my $vbox = $window->vbox;
1331             $vbox->set_border_width(5);
1332            
1333             my $label = Gtk2::Label->new_with_mnemonic('Name for new directory?');
1334             $vbox->pack_start($label, 0, 0, 1);
1335             $label->show;
1336            
1337             my $entry = Gtk2::Entry->new();
1338             $vbox->pack_end($entry, 0, 0, 1);
1339             $entry->show;
1340            
1341             $entry->signal_connect (changed => sub {
1342             my $text = $entry->get_text;
1343             $window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
1344             $window->set_response_sensitive ('reject', 1);
1345             }
1346             );
1347            
1348             my $value;
1349             my $pressed;
1350            
1351             $window->signal_connect(response => sub {
1352             $value=$entry->get_text;
1353             $pressed=$_[1];
1354             }
1355             );
1356             #runs the dailog and gets the response
1357             #'cancel' means the user decided not to create a new set
1358             #'accept' means the user wants to create a new set with the entered name
1359             my $response=$window->run;
1360            
1361             $window->destroy;
1362            
1363             #set the pressed to reject if
1364             if (($value eq '' )&&($pressed eq 'accept')) {
1365             $pressed='reject'
1366             }
1367            
1368             if ($pressed eq 'accept') {
1369             mkdir($value);
1370             }
1371              
1372             #update the stuff
1373             $_[1]{self}->update( $_[1]{gui}{id}, $_[1]{self} );
1374             }
1375              
1376             =head2 runViaNew
1377              
1378             This is the call back that is called when
1379             a entry is asked to be run via new a new
1380             action.
1381              
1382             =cut
1383              
1384             sub runViaNew{
1385             my $self=$_[1];
1386             my $guiID=$_[2];
1387             my $item=$_[3];
1388            
1389             my $text='';
1390             my $window = Gtk2::Dialog->new('Run Via New Action',
1391             undef,
1392             [qw/modal destroy-with-parent/],
1393             'gtk-cancel' => 'cancel',
1394             'gtk-ok' => 'accept',
1395             );
1396            
1397             $window->set_position('center-always');
1398            
1399             $window->set_response_sensitive ('accept', 0);
1400             $window->set_response_sensitive ('reject', 0);
1401            
1402             my $vbox = $window->vbox;
1403             $vbox->set_border_width(5);
1404            
1405             my $label = Gtk2::Label->new_with_mnemonic('Name for new action?');
1406             $vbox->pack_start($label, 0, 0, 1);
1407             $label->show;
1408            
1409             my $entry = Gtk2::Entry->new();
1410             $vbox->pack_end($entry, 0, 0, 1);
1411             $entry->show;
1412            
1413             $entry->signal_connect (changed => sub {
1414             my $text = $entry->get_text;
1415             $window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
1416             $window->set_response_sensitive ('reject', 1);
1417             }
1418             );
1419            
1420             my $action;
1421             my $pressed;
1422            
1423             $window->signal_connect(response => sub {
1424             $action=$entry->get_text;
1425             $pressed=$_[1];
1426             }
1427             );
1428             #runs the dailog and gets the response
1429             #'cancel' means the user decided not to create a new set
1430             #'accept' means the user wants to create a new set with the entered name
1431             my $response=$window->run;
1432            
1433             $window->destroy;
1434            
1435             #set the pressed to reject if
1436             if (($action eq '' )&&($pressed eq 'accept')) {
1437             $pressed='reject';
1438             return;
1439             }
1440            
1441             if ($pressed eq 'accept') {
1442             system('zcrunner -a '.shell_quote($action).' -o '.shell_quote($item).' &');
1443             }
1444              
1445             #update the stuff
1446             $self->{zcr}->readSet();
1447             $self->{zcrUpdate}=1;
1448             $self->update( $guiID, $self );
1449             $self->updateRmenu($guiID);
1450             }
1451              
1452             =head2 setAction
1453              
1454             This sets the default action to use with the ZConf::Runner.
1455              
1456             One arguement is taken and that is the name of the action.
1457              
1458             $pfm-setAction($action);
1459              
1460             =cut
1461              
1462             sub setAction{
1463             my $self=$_[0];
1464             my $action=$_[1];
1465              
1466             if (!defined($action)) {
1467             return undef;
1468             }
1469              
1470             $self->{defaultAction}=$action;
1471              
1472             return undef;
1473             }
1474              
1475             =head2 setActionCB
1476              
1477             This is the call back used by the set default action
1478             button.
1479              
1480             =cut
1481              
1482             sub setActionCB{
1483             my $self=$_[1];
1484             my $guiID=$_[2];
1485            
1486             my $text='';
1487             my $window = Gtk2::Dialog->new('Set Default Action',
1488             undef,
1489             [qw/modal destroy-with-parent/],
1490             'gtk-cancel' => 'cancel',
1491             'gtk-ok' => 'accept',
1492             );
1493            
1494             $window->set_position('center-always');
1495            
1496             $window->set_response_sensitive ('accept', 0);
1497             $window->set_response_sensitive ('reject', 0);
1498            
1499             my $vbox = $window->vbox;
1500             $vbox->set_border_width(5);
1501            
1502             my $label = Gtk2::Label->new_with_mnemonic('New default action?');
1503             $vbox->pack_start($label, 0, 0, 1);
1504             $label->show;
1505            
1506             my $entry = Gtk2::Entry->new();
1507             $vbox->pack_end($entry, 0, 0, 1);
1508             $entry->show;
1509            
1510             $entry->signal_connect (changed => sub {
1511             my $text = $entry->get_text;
1512             $window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
1513             $window->set_response_sensitive ('reject', 1);
1514             }
1515             );
1516            
1517             my $action;
1518             my $pressed;
1519            
1520             $window->signal_connect(response => sub {
1521             $action=$entry->get_text;
1522             $pressed=$_[1];
1523             }
1524             );
1525             #runs the dailog and gets the response
1526             #'cancel' means the user decided not to create a new set
1527             #'accept' means the user wants to create a new set with the entered name
1528             my $response=$window->run;
1529            
1530             $window->destroy;
1531            
1532             #set the pressed to reject if
1533             if (($action eq '' )&&($pressed eq 'accept')) {
1534             $pressed='reject';
1535             return;
1536             }
1537            
1538             $self->setAction($action);
1539              
1540             $self->update( $guiID, $self );
1541             $self->updateRmenu($guiID);
1542             }
1543              
1544             =head2 update
1545              
1546             This is the is used by callbacks for updating.
1547              
1548             $pfm->update($gui{id}, $self);
1549              
1550             =cut
1551              
1552             sub update{
1553             # my $self=$_[0];
1554             # my %gui;
1555             # if (defined($_[1])) {
1556             # %gui=%{$_[1]};
1557             # }
1558             my $guiID=$_[1];
1559             my $self=$_[2];
1560              
1561             # if (!defined($gui{VB})) {
1562             # warn('PerlFM update: The passed GUI hash does not appear to be something returned by the filemanager method');
1563             # return undef;
1564             # }
1565              
1566             #set the window title
1567             if (defined($self->{window})) {
1568             $self->{window}{window}->set_title('pfm: '.cwd);
1569             }
1570              
1571             #gets the data
1572             my %datahash=$self->datahash($self->{gui}{$guiID}{hidden});
1573             $self->{data}{$guiID}{data}=\%datahash;
1574              
1575             $self->{test}="3\n\n";
1576              
1577             my @listdata;
1578             my @dirlistdata;
1579             my @dirlistdata2;
1580             my $int=0;
1581             while (defined( $datahash{reverse}[$int] )) {
1582             my $entry=$datahash{reverse}[$int];
1583             my $atime=localtime($datahash{names}{$entry}{atime});
1584             my $ctime=localtime($datahash{names}{$entry}{ctime});
1585             my $mtime=localtime($datahash{names}{$entry}{mtime});
1586             my ($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell,$expire) = getpwuid($datahash{names}{$entry}{uid});
1587             if (!defined($name)) {
1588             $name=$datahash{names}{$entry}{uid};
1589             }
1590             my ($gname,$gpasswd,$ggid,$members) = getgrgid($datahash{names}{$entry}{gid});
1591             if (!defined($gname)) {
1592             $gname=$datahash{names}{$entry}{gid};
1593             }
1594              
1595             if (-d $entry) {
1596             my @row=(
1597             $entry,
1598             );
1599             my @row2=(
1600             $entry,
1601             $name,
1602             $gname,
1603             $datahash{names}{$entry}{mode},
1604             $datahash{names}{$entry}{size},
1605             $mtime,
1606             $ctime,
1607             $atime
1608             );
1609             push(@dirlistdata2, \@row2);
1610             push(@dirlistdata, \@row);
1611             }else {
1612             my @row=(
1613             $entry,
1614             $name,
1615             $gname,
1616             $datahash{names}{$entry}{mode},
1617             $datahash{names}{$entry}{size},
1618             $mtime,
1619             $ctime,
1620             $atime
1621             );
1622             push(@listdata, \@row);
1623             }
1624              
1625             $int++;
1626             }
1627              
1628             my @fulllist;
1629             push(@fulllist, @dirlistdata2);
1630             push(@fulllist, @listdata);
1631              
1632             @{$self->{gui}{$guiID}{list}->{data}}=@fulllist;
1633              
1634             @{$self->{gui}{$guiID}{dirlist}->{data}}=@dirlistdata;
1635              
1636             $self->{gui}{$guiID}{PB}->setPath(cwd);
1637              
1638             $self->{gui}{$guiID}{watcher}=Dir::Watch->new;
1639             }
1640              
1641             =head2 updateBM
1642              
1643             This is the method that is used for updating the bookmark selection.
1644              
1645             It is called automatically as needed.
1646              
1647             =cut
1648              
1649             sub updateBM{
1650             my %h;
1651             if (defined($_[1])) {
1652             %h=%{$_[1]};
1653             }
1654              
1655             $h{self}->errorblank;
1656              
1657             #get the list of bookmarks
1658             my @bookmarks=$h{self}->{zcbm}->listBookmarks('file');
1659             if ($h{self}->{zcbm}->{error}) {
1660             warn('PerlFM updatebm: listBookmarks fialed for ZConf::Bookmarks');
1661             return undef;
1662             }
1663              
1664             my %bmhash;
1665             my @names;
1666              
1667             #process the bookmarks
1668             my $int=0;
1669             while (defined($bookmarks[$int])) {
1670             my $bmID=$bookmarks[$int];
1671              
1672             my %bookmark=$h{self}->{zcbm}->getBookmark('file', $bmID);
1673              
1674             #puts it together if there was not a error with it
1675             if (!defined( $h{self}->{zcbm}->{error} )) {
1676             $bmhash{$bookmarks[$int]}=\%bookmark;
1677              
1678             push(@names, $bookmark{name});
1679             }
1680              
1681             $int++;
1682             }
1683            
1684             #sort the names
1685             @names=sort(@names);
1686              
1687             #save them for later recall as $h{gui} is not blessed
1688             $h{self}->{bookmarkNames}=\@names;
1689             $h{self}->{bookmarks}=\%bmhash;
1690             $h{self}->{bookmarkIDs}=\@bookmarks;
1691              
1692             #this will contain the reverse mappings
1693             my @reverse;
1694              
1695             #put it together
1696             $int=0;
1697             my %matched;#the bookmark ID will be defined if it is used...
1698             while (defined($names[$int])) {
1699             my $bmInt=0;
1700             while ($bookmarks[$bmInt]) {
1701             #make sure it has not been matched yet
1702             if (!$matched{$bookmarks[$bmInt]}) {
1703             if ( $bmhash{ $bookmarks[$bmInt] }{name} eq $names[$int] ) {
1704             push(@reverse, $bookmarks[$bmInt]);
1705             #mark it as matched
1706             $matched{$bookmarks[$bmInt]}=1;
1707             }
1708             }
1709              
1710             $bmInt++;
1711             }
1712              
1713             $int++;
1714             }
1715              
1716             #this is a the reverse hash
1717             $h{self}->{bookmarkReverse}=\@reverse;
1718              
1719             @{$h{self}{gui}{ $h{gui}{id} }{bmlist}->{data}}=@names;
1720              
1721             }
1722              
1723             =head2 updateRmenu
1724              
1725             This updates the r menu and used by various callbacks.
1726              
1727             =cut
1728              
1729             sub updateRmenu{
1730             my $self=$_[0];
1731             my $guiID=$_[1];
1732              
1733             #update if needed
1734             if (defined($self->{zcrUpdate})) {
1735             if ($self->{zcrUpdate} eq '1'){
1736             $self->{zcr}->readSet();
1737             $self->{zcrUpdate}=0;
1738             }
1739             }
1740              
1741             #get the selected entry
1742             my @selected=$self->{gui}{ $guiID }{list}->get_selected_indices;
1743             #return if we don't have any thing
1744             if (!defined($selected[0])) {
1745             return undef;
1746             }
1747             my $entry=$self->{data}{ $guiID }{data}{reverse}[$selected[0]];
1748              
1749             #get the mimetype
1750             my $mimetype=mimetype($entry);
1751              
1752             #if that mime type is setup, get the available entries
1753             my $avail=$self->{zcr}->mimetypeIsSetup($mimetype);
1754             my @available;
1755             if ($avail) {
1756             @available=$self->{zcr}->listActions($mimetype);
1757             }
1758              
1759             #the enw rmenu
1760             my $rmenu=Gtk2::Menu->new;
1761             $rmenu->new;
1762              
1763             my $to=Gtk2::TearoffMenuItem->new;
1764             $to->show;
1765             $rmenu->append($to);
1766              
1767             #add the new itme
1768             my $new=Gtk2::MenuItem->new('run via a _new action');
1769             $new->show;
1770             $new->signal_connect(activate=>sub{
1771             $_[1]{self}->runViaNew($_[1]{self}, $_[1]{id}, $_[1]{entry});
1772             },
1773             {
1774             id=>$guiID,
1775             self=>$self,
1776             entry=>$entry,
1777             }
1778             );
1779             $rmenu->append($new);
1780              
1781             #add the new itme
1782             my $set=Gtk2::MenuItem->new('_set default action ('.$self->getAction.')');
1783             $set->show;
1784             $set->signal_connect(activate=>sub{
1785             $_[1]{self}->setActionCB($_[1]{self}, $_[1]{id});
1786             },
1787             {
1788             id=>$guiID,
1789             self=>$self,
1790             entry=>$entry,
1791             }
1792             );
1793             $rmenu->append($set);
1794              
1795             #add the refresh item
1796             my $refresh=Gtk2::MenuItem->new('_refresh');
1797             $refresh->show;
1798             $refresh->signal_connect(activate=>sub{
1799             $_[1]{self}->{zcr}->readSet;
1800             $_[1]{self}->updateRmenu($_[1]{id});
1801             },
1802             {
1803             id=>$guiID,
1804             self=>$self,
1805             entry=>$entry,
1806             }
1807             );
1808             $rmenu->append($refresh);
1809              
1810             my $so=Gtk2::SeparatorMenuItem->new();
1811             $so->show;
1812             $rmenu->append($so);
1813            
1814             #process all actions
1815             my @actions;
1816             my $int=0;
1817             while (defined($available[$int])) {
1818             $actions[$int]=Gtk2::MenuItem->new('_'.$int.' '.$available[$int]);
1819             $actions[$int]->show;
1820             $actions[$int]->signal_connect(activate=>sub{
1821             system('zcrunner -a '.shell_quote($_[1]{action}).' -o '.shell_quote($_[1]{entry}).' &');
1822             },
1823             {
1824             action=>$available[$int],
1825             entry=>$entry,
1826             }
1827             );
1828             $rmenu->append($actions[$int]);
1829             $int++;
1830             }
1831              
1832             #add the menu
1833             $self->{gui}{ $guiID }{rmenubarmenu}->set_submenu($rmenu);
1834              
1835             }
1836              
1837             =head2 window
1838              
1839             This returns a hash containing the various widgets.
1840              
1841             =head3 args hash
1842              
1843             =head4 path
1844              
1845             This is the path to start in.
1846              
1847             =head4 hidden
1848              
1849             If this is set to true, hidden files will be shown.
1850              
1851             $args{path}='/tmp';
1852             $args{hidden}=0;
1853             my %winhash=$pfm->window(\%args);
1854             $winhash{window}->show;
1855             Gtk2->init;
1856              
1857             =cut
1858              
1859             sub window{
1860             my $self=$_[0];
1861             my %args;
1862             if(defined($_[1])){
1863             %args= %{$_[1]};
1864             }
1865              
1866             my %window;
1867              
1868             $window{window}=Gtk2::Window->new;
1869             $window{window}->set_default_size(750, 400);
1870              
1871             $window{window}->set_title('pfm: '.cwd);
1872              
1873             #gets the GUI and add it
1874             my %gui=$self->filemanager(\%args);
1875             $window{fm}=\%gui;
1876             $window{window}->add($window{fm}{VB});
1877              
1878             $self->{window}=\%window;
1879              
1880             return %window;
1881             }
1882              
1883             =head2 errorblank
1884              
1885             This blanks the error storage and is only meant for internal usage.
1886              
1887             It does the following.
1888              
1889             $self->{error}=undef;
1890             $self->{errorString}="";
1891              
1892             =cut
1893              
1894             #blanks the error flags
1895             sub errorblank{
1896             my $self=$_[0];
1897            
1898             $self->{error}=undef;
1899             $self->{errorString}="";
1900            
1901             return 1;
1902             }
1903              
1904             =head1 AUTHOR
1905              
1906             Zane C. Bowers, C<< >>
1907              
1908             =head1 BUGS
1909              
1910             Please report any bugs or feature requests to C, or through
1911             the web interface at L. I will be notified, and then you'll
1912             automatically be notified of progress on your bug as I make changes.
1913              
1914              
1915              
1916              
1917             =head1 SUPPORT
1918              
1919             You can find documentation for this module with the perldoc command.
1920              
1921             perldoc PerlFM
1922              
1923              
1924             You can also look for information at:
1925              
1926             =over 4
1927              
1928             =item * RT: CPAN's request tracker
1929              
1930             L
1931              
1932             =item * AnnoCPAN: Annotated CPAN documentation
1933              
1934             L
1935              
1936             =item * CPAN Ratings
1937              
1938             L
1939              
1940             =item * Search CPAN
1941              
1942             L
1943              
1944             =back
1945              
1946              
1947             =head1 ACKNOWLEDGEMENTS
1948              
1949              
1950             =head1 COPYRIGHT & LICENSE
1951              
1952             Copyright 2009 Zane C. Bowers, all rights reserved.
1953              
1954             This program is free software; you can redistribute it and/or modify it
1955             under the same terms as Perl itself.
1956              
1957              
1958             =cut
1959              
1960             1; # End of PerlFM