File Coverage

lib/Applications/BackupAndRestore.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 Applications::BackupAndRestore;
2 1     1   57913 use strict;
  1         2  
  1         32  
3 1     1   6 use warnings;
  1         2  
  1         96  
4            
5             our $VERSION = 0.021;
6             our $DEBUG = 1;
7            
8             =head1 NAME
9            
10             Applications::BackupAndRestore - a linux frontend for tar
11            
12             =head1 DESCRIPTION
13            
14             BackupAndRestore is a backup utility for making incremental backups by using GNU Tar.
15             Core features:
16            
17             =over
18            
19             =item *
20             Incremental backup with quick and easy restoration of files
21            
22             =item *
23             Handels different backup locations
24            
25             =item *
26             Full support for excluding files and folders and even file patterns (shell regex)
27            
28             =item *
29             Handels different store locations
30            
31             =item *
32             Log with all relevant information
33            
34             =head1 REQUIREMENTS
35            
36             A Linux
37            
38             L
39            
40             L
41            
42             A archive browser like file-roller (for now BackupAndRestore only supports this)
43            
44             =head1 GUI
45            
46             =head2 Backup & Restore
47            
48             The Backup & Restore utility is illustrated in Figure 1-1.
49            
50             =head3 Figure 1-1. Backup & Restore
51            
52             =begin html
53            
54            
55            
56             =end html
57            
58             =head1
59            
60             To start up Backup & Restore from a terminal window, type B and then press C.
61            
62             Backup & Restore has a List View where you see every single backup with time, date, changed files and the exact space required on your harddrive.
63            
64             Above the list view there is a File Chooser Button where you can select a folder to backup. Position the cursor over File Chooser Button and press the right mouse button. A pop-up menu appears. Choose a folder from the pop-up menu. Drag a folder icon and place it into the File Chooser Button. The window displays the contents of that backup.
65            
66             Right hand to the File Chooser Button there is a Recycle Button. The recycle button keeps a list of folders you have saved. For example, place the cursor over the recycle button on a Backup & Restore window; then press the left mouse button to see a list of directories whose contents you have previously saved. Choose an item from this list and the window changes to display the contents of that backup.
67            
68             Below the list view there is a backup button.
69            
70             =head2 Backup In Progress Notification
71            
72             The Backup In Progress Notification is illustrated in Figure 1-2.
73            
74             =head3 Figure 1-2. Backup In Progress Notification
75            
76             =begin html
77            
78            
79            
80             =end html
81            
82             =head2 Restore Dialog
83            
84             The Restore Dialog is illustrated in Figure 1-3.
85            
86             =head3 Figure 1-3. Restore Dialog
87            
88             =begin html
89            
90            
91            
92             =end html
93            
94             =head2 Restore In Progress Notification
95            
96             The Restore Dialog is illustrated in Figure 1-4.
97            
98             =head3 Figure 1-4. Restore In Progress Notification
99            
100             =begin html
101            
102            
103            
104             =end html
105            
106             =head2 Remove Backup Dialog
107            
108             The Remove Backup Dialog is illustrated in Figure 1-5.
109            
110             =head3 Figure 1-5. Remove Backup Dialog
111            
112             =begin html
113            
114            
115            
116             =end html
117            
118             =head1 Perl
119            
120             =head2 Synopsis
121            
122             use Applications::BackupAndRestore -run;
123            
124             use Applications::BackupAndRestore;
125            
126             =head2 Functions
127            
128             =cut
129            
130             # http://www.gnu.org/software/tar/manual/tar.html
131            
132             #use AutoSplit; autosplit('../Applications/BackupAndRestore', '../auto/', 0, 1, 1);
133            
134 1     1   11350 use Glib qw(TRUE FALSE);
  0            
  0            
135             use Gtk2;
136             use Gtk2::GladeXML;
137             use Gtk2::Gdk::Keysyms;
138             use Gnome2::GConf;
139            
140             use Cwd qw(abs_path);
141             use File::Basename qw(basename dirname);
142             use Number::Bytes::Human qw( format_bytes );
143             use POSIX qw(strftime);
144             use Unicode::UTF8simple;
145            
146             use Gtk2::Ex::FileLocator::RecycleButton;
147             use Applications::BackupAndRestore::Helper;
148            
149             # Globals
150             #
151            
152             our $TarOpenCmd = "file-roller";
153            
154             my $ApplicationName = 'BackupAndRestore';
155            
156             my $CurrentDat = "current.dat";
157             my $ProcessDat = "process.dat";
158             my $ExcludesFile = "excludes.txt";
159             my $DateTxt = "date.txt";
160            
161             my @ColumnTypes = qw(
162             Glib::String
163             Glib::String
164             Glib::String
165             Glib::String
166             Glib::UInt
167             Glib::UInt
168             Glib::String
169             Glib::UInt
170             Glib::String
171             Glib::String
172             Glib::Boolean
173             Glib::UInt
174             );
175            
176             use enum qw(
177             COL_DATE
178             COL_HDATE
179             COL_TIME
180             COL_NAME
181             COL_SIZE
182             COL_REAL_SIZE
183             COL_HSIZE
184             COL_FILES
185             COL_LABEL
186             COL_PATH
187             COL_LAST_BACKUP
188             COL_WEIGHT
189             );
190             use enum qw(
191             EXCLUDE_FOLDER
192             EXCLUDE_FILE
193             EXCLUDE_PATTERN
194             );
195            
196             my @SIGS = ( 'INT', 'TERM', 'KILL', 'ABRT', 'QUIT' );
197            
198             # import
199             #
200            
201             sub import {
202             my $class = shift;
203             my $run = 0;
204             foreach (@_) {
205             if (/^-?run$/) {
206             $class->run(@ARGV);
207             last;
208             }
209             }
210             }
211            
212             # AUTOLOAD
213             #
214            
215             use AutoLoader;
216             our $AUTOLOAD;
217            
218             sub AUTOLOAD {
219             my $this = shift;
220             my $name = substr $AUTOLOAD, rindex( $AUTOLOAD, ':' ) + 1;
221            
222             #printf "%s\n", $name if $DEBUG > 3;
223             my $widget = $this->{gladexml}->get_widget($name);
224             return $widget if ref $widget;
225             die "AUTOLOAD: Unknown widget '$name'";
226             }
227            
228             =head3 new
229            
230             Creates a new Window;
231            
232             my $window = new Applications::BackupAndRestore;
233            
234             =cut
235            
236             sub new {
237             my ($self) = @_;
238             my $class = ref($self) || $self;
239             my $this = bless {}, $class;
240            
241             #printf "%s\n", dirname abs_path $0 if $DEBUG > 3;
242            
243             chdir dirname abs_path $0 if -f $0;
244            
245             $this->{client} = Gnome2::GConf::Client->get_default;
246            
247             $this->{gladexml} = Gtk2::GladeXML->new("../bin/$ApplicationName.glade");
248             $this->{gladexml}->signal_autoconnect_from_package($this);
249            
250             return if $this->is_running;
251            
252             $this->init;
253            
254             return $this;
255             }
256            
257             sub is_running {
258             my ($this) = @_;
259            
260             if ( $this->gconf("pid") ) {
261            
262             printf "pid %d\n", $this->gconf("pid") || 0 if $DEBUG;
263            
264             my $cmd = sprintf "ps -p %d", $this->gconf("pid");
265             my $ps = `$cmd`;
266            
267             if ( $ps =~ /BackupAndRestor/o ) {
268             $this->is_running_notification->present;
269             Gtk2->main;
270             return 1;
271             }
272            
273             }
274            
275             printf "pid %d\n", $$ || 0 if $DEBUG;
276             $this->gconf( "pid", $$ );
277            
278             return;
279             }
280            
281             sub on_is_running_notification_delete_event {
282             my ($this) = @_;
283             print "on_is_running_notification_delete_event\n" if $DEBUG > 3;
284             Gtk2->main_quit;
285             return;
286             }
287            
288             =head3 run
289            
290             Opens the window. Run will not return until you close the window.
291            
292             use Applications::BackupAndRestore -run;
293            
294             or
295            
296             Applications::BackupAndRestore::run;
297            
298             =cut
299            
300             sub run {
301             Gtk2->init;
302             my $class = shift;
303             my $this = $class->new(@_);
304             if ($this) {
305             $this->window->present;
306             Gtk2->main;
307             }
308             return;
309             }
310            
311             =head3 show
312            
313             Displays the window and return.
314            
315             Applications::BackupAndRestore->new->show;
316            
317             =cut
318            
319             sub show {
320             my ($this) = @_;
321             $this->window->present;
322             return;
323             }
324            
325             =head1 BUGS & SUGGESTIONS
326            
327             If you run into a miscalculation please drop the author a note.
328            
329             =head1 ARRANGED BY
330            
331             HOOO@cpan.org
332            
333             =head1 COPYRIGHT
334            
335             This is free software; you can redistribute it and/or modify it
336             under the same terms as L itself.
337            
338             =cut
339            
340             # gconf
341             #
342            
343             sub gconf {
344             my ( $this, $key, $value ) = @_;
345             my $app_key = "/apps/" . $ApplicationName . "/$key";
346            
347             $this->{client}->set( $app_key, { type => 'string', 'value' => $value } )
348             if defined $value;
349            
350             return $this->{client}->get_string($app_key);
351             }
352            
353             # gui init
354             #
355            
356             sub init {
357             my ($this) = @_;
358             print "init $this\n" if $DEBUG > 0;
359            
360             $SIG{$_} = sub { $this->gtk_main_quit }
361             foreach @SIGS;
362            
363             # GUI init
364            
365             $this->{init} = TRUE;
366            
367             my $WindowIcon = "../share/BackupAndRestore/BackupAndRestore.svg";
368             $this->window->set_icon_from_file ($WindowIcon)
369             if -e $WindowIcon;
370            
371             $this->exclude_combo->set_active(0); # Gtk2::GladeXML macht es nicht
372            
373             my $button;
374             $this->{folder_recycle_button} = new Gtk2::Ex::FileLocator::RecycleButton;
375             $this->{folder_recycle_button}->show;
376             $this->folder_box->pack_start( $this->{folder_recycle_button}, FALSE, FALSE, 0 );
377             $this->{folder_recycle_button}->signal_connect( 'selection-changed', sub { $this->on_folder_recycle_button(@_) } );
378            
379             #configure;
380             $this->restore_extract_modifiction_time->set_active(
381             defined $this->gconf("restore-extract-modification-time")
382             ? $this->gconf("restore-extract-modification-time")
383             : 1
384             );
385             $this->restore_folder->set_current_folder( $this->gconf("restore-folder") || $ENV{HOME} );
386             $this->store_folder->set_current_folder( $this->gconf("store-folder") || $ENV{HOME} );
387             $this->store_folder_name->set_text( $this->gconf("store-folder-name") || "Backup" );
388             $this->folder->set_current_folder( $this->gconf("current-backup-folder") || $ENV{HOME} );
389            
390             $this->configure_expander;
391            
392             $this->build_tree;
393            
394             $this->log_init;
395             $this->log_add_text( "*** $ApplicationName\n", "Version: $VERSION\n", "\n", );
396             $this->log_add_text( "*** ", $this->get_tar_version );
397            
398             $this->{init} = FALSE;
399             $this->fill_tree;
400            
401             print "init done $this\n" if $DEBUG > 0;
402             }
403            
404             # build_tree
405             #
406            
407             sub build_tree {
408             my ($this) = @_;
409             print "build_tree\n" if $DEBUG > 0;
410            
411             #this will create a treeview, specify $tree_store as its model
412             my $tree_view = $this->tree_view;
413            
414             #
415            
416             #create a Gtk2::TreeViewColumn to add
417             #to $tree_view
418             my $tree_column = Gtk2::TreeViewColumn->new();
419             $tree_column->set_title( __ "Time" );
420             $tree_column->set_visible(FALSE);
421            
422             #create a renderer
423             my $renderer = Gtk2::CellRendererText->new;
424             $tree_column->pack_start( $renderer, FALSE );
425             $tree_column->add_attribute( $renderer, text => COL_TIME );
426            
427             #$tree_column->set_sort_column_id(COL_TIME);
428            
429             #add $tree_column to the treeview
430             $tree_view->append_column($tree_column);
431            
432             #
433            
434             #create a Gtk2::TreeViewColumn to add
435             #to $tree_view
436             $tree_column = Gtk2::TreeViewColumn->new();
437             $tree_column->set_title( __ "Date" );
438            
439             #create a renderer
440             $renderer = Gtk2::CellRendererPixbuf->new;
441             $renderer->set( 'icon-name' => 'tgz' );
442             $tree_column->pack_start( $renderer, FALSE );
443            
444             #create a renderer
445             $renderer = Gtk2::CellRendererText->new;
446             $tree_column->pack_start( $renderer, FALSE );
447             $tree_column->add_attribute( $renderer, text => COL_NAME );
448             $tree_column->add_attribute( $renderer, weight_set => COL_LAST_BACKUP );
449             $tree_column->add_attribute( $renderer, weight => COL_WEIGHT );
450            
451             #$tree_column->set_sort_column_id(COL_TIME);
452            
453             #add $tree_column to the treeview
454             $tree_view->append_column($tree_column);
455            
456             #
457            
458             #create a Gtk2::TreeViewColumn to add
459             #to $tree_view
460             $tree_column = Gtk2::TreeViewColumn->new();
461             $tree_column->set_title( __ "Changed files" );
462             $tree_column->set_visible(TRUE);
463            
464             #create a renderer
465             $renderer = Gtk2::CellRendererText->new;
466             $tree_column->pack_start( $renderer, FALSE );
467             $tree_column->add_attribute( $renderer, text => COL_FILES );
468             $tree_column->add_attribute( $renderer, weight_set => COL_LAST_BACKUP );
469             $tree_column->add_attribute( $renderer, weight => COL_WEIGHT );
470            
471             #$tree_column->set_sort_column_id(COL_PATH);
472            
473             #add $tree_column to the treeviewGtk2::CellRenderer
474             $tree_view->append_column($tree_column);
475            
476             #
477            
478             #create a Gtk2::TreeViewColumn to add
479             #to $tree_view
480             $tree_column = Gtk2::TreeViewColumn->new();
481             $tree_column->set_title( __ "Size" );
482            
483             #create a renderer
484             $renderer = Gtk2::CellRendererText->new;
485             $tree_column->pack_start( $renderer, FALSE );
486             $tree_column->add_attribute( $renderer, text => COL_HSIZE );
487             $tree_column->add_attribute( $renderer, weight_set => COL_LAST_BACKUP );
488             $tree_column->add_attribute( $renderer, weight => COL_WEIGHT );
489            
490             #$tree_column->set_sort_column_id(COL_SIZE);
491            
492             #add $tree_column to the treeview
493             $tree_view->append_column($tree_column);
494            
495             #
496            
497             #create a Gtk2::TreeViewColumn to add
498             #to $tree_view
499             $tree_column = Gtk2::TreeViewColumn->new();
500             $tree_column->set_title( __ "Size in bytes" );
501             $tree_column->set_visible(FALSE);
502            
503             #create a renderer
504             $renderer = Gtk2::CellRendererText->new;
505             $tree_column->pack_start( $renderer, FALSE );
506             $tree_column->add_attribute( $renderer, text => COL_SIZE );
507            
508             #$tree_column->set_sort_column_id(COL_SIZE);
509            
510             #add $tree_column to the treeviewGtk2::CellRenderer
511             $tree_view->append_column($tree_column);
512            
513             #
514            
515             #create a Gtk2::TreeViewColumn to add
516             #to $tree_view
517             $tree_column = Gtk2::TreeViewColumn->new();
518             $tree_column->set_title( __ "Real size in bytes" );
519             $tree_column->set_visible(FALSE);
520            
521             #create a renderer
522             $renderer = Gtk2::CellRendererText->new;
523             $tree_column->pack_start( $renderer, FALSE );
524             $tree_column->add_attribute( $renderer, text => COL_REAL_SIZE );
525            
526             #$tree_column->set_sort_column_id(COL_SIZE);
527            
528             #add $tree_column to the treeviewGtk2::CellRenderer
529             $tree_view->append_column($tree_column);
530            
531             #
532            
533             #create a Gtk2::TreeViewColumn to add
534             #to $tree_view
535             $tree_column = Gtk2::TreeViewColumn->new();
536             $tree_column->set_title( __ "Label" );
537            
538             #$tree_column->set_visible(FALSE);
539            
540             #create a renderer
541             $renderer = Gtk2::CellRendererText->new;
542             $tree_column->pack_start( $renderer, FALSE );
543             $tree_column->add_attribute( $renderer, text => COL_LABEL );
544             $tree_column->add_attribute( $renderer, weight_set => COL_LAST_BACKUP );
545             $tree_column->add_attribute( $renderer, weight => COL_WEIGHT );
546            
547             #$tree_column->set_sort_column_id(COL_PATH);
548            
549             #add $tree_column to the treeviewGtk2::CellRenderer
550             $tree_view->append_column($tree_column);
551            
552             #
553            
554             #create a Gtk2::TreeViewColumn to add
555             #to $tree_view
556             $tree_column = Gtk2::TreeViewColumn->new();
557             $tree_column->set_title( __ "Path" );
558             $tree_column->set_visible(FALSE);
559            
560             #create a renderer
561             $renderer = Gtk2::CellRendererText->new;
562             $tree_column->pack_start( $renderer, FALSE );
563             $tree_column->add_attribute( $renderer, text => COL_PATH );
564            
565             #$tree_column->set_sort_column_id(COL_PATH);
566            
567             #add $tree_column to the treeviewGtk2::CellRenderer
568             $tree_view->append_column($tree_column);
569            
570             }
571            
572             #backup
573             #
574            
575             sub on_backup_folder_changed {
576             my ($this) = @_;
577            
578             return unless $this->folder->get_filename;
579             return if abs_path( $this->folder->get_filename ) eq $this->gconf("current-backup-folder");
580            
581             printf "on_backup_folder_changed %s\n", abs_path $this->folder->get_filename if $DEBUG > 0;
582            
583             $this->gconf( "current-backup-folder", abs_path $this->folder->get_filename );
584             $this->fill_tree;
585             return;
586             }
587            
588             sub on_folder_recycle_button {
589             my ($this) = @_;
590            
591             return unless $this->{folder_recycle_button}->get_filename;
592             return if $this->folder->get_filename eq abs_path( $this->{folder_recycle_button}->get_filename );
593            
594             printf " on_folder_recycle_button %s\n", abs_path $this->{folder_recycle_button}->get_filename
595             if $DEBUG > 0;
596            
597             $this->folder->set_current_folder( abs_path $this->{folder_recycle_button}->get_filename );
598            
599             return;
600             }
601            
602             #tree
603             #
604            
605             use Tie::DataDumper;
606            
607             sub fill_tree {
608             my ($this) = @_;
609             return if $this->{init};
610            
611             $this->restore_button->set_sensitive(FALSE);
612            
613             #fill it with arbitry data
614            
615             my $folder = $this->get_store_folder;
616             printf "fill_tree %s\n", $folder if $DEBUG > 0;
617            
618             my $tree_store = Gtk2::TreeStore->new(@ColumnTypes);
619            
620             if ( -e $folder ) {
621             my ( $day_iter, $day, $day_folder_size, $day_folder_real_size, $day_folder_files ) = ( undef, "", 0, 0, 0 );
622             my $current_dat = "$folder/$CurrentDat";
623            
624             my $date_of_last_backup = $this->fetch_restore_date($folder);
625            
626             #printf "%s\n", $date_of_last_backup;
627            
628             my @filenames = reverse grep { m/\.tar\.bz2$/ } get_files($folder);
629            
630             foreach my $filename (@filenames) {
631            
632             # get basename
633             my $basename = basename( $filename, ".tar.bz2" );
634            
635             # calculate size
636             my $tardat = "$folder/$basename.dat.bz2";
637             my $size = ( -s $filename ) + ( -s $tardat );
638             $size += -s $current_dat unless $day;
639            
640             ################################################################
641             my $infofile = "$folder/$basename.info.txt";
642             my $info = $this->get_backup_info( $filename, $infofile );
643             ################################################################
644            
645             # append day folder
646             my ( $date, $time ) = split / /o, $basename;
647             if ( $date ne $day ) {
648             $tree_store->set(
649             $day_iter,
650             (
651             COL_SIZE, $day_folder_size, COL_REAL_SIZE, $day_folder_real_size, COL_HSIZE,
652             ( sprintf "%sB (%sB)", format_bytes($day_folder_size), format_bytes($day_folder_real_size) ),
653             COL_FILES, $day_folder_files,
654             )
655             ) if ref $day_iter;
656            
657             $day = $date;
658             $day_iter = $tree_store->append(undef);
659             $tree_store->set(
660             $day_iter,
661             (
662             COL_DATE, $date, COL_HDATE, format_date($date),
663             COL_TIME, $time, COL_NAME, format_date($date),
664             COL_SIZE, $day_folder_size, COL_FILES, $day_folder_files,
665             COL_PATH, $filename, COL_LABEL, "",
666             COL_LAST_BACKUP, FALSE, COL_WEIGHT, 600,
667             )
668             );
669            
670             $day_folder_size = 0;
671             $day_folder_real_size = 0;
672             $day_folder_files = 0;
673             }
674            
675             #printf "%s\n", $tardat unless -s $tardat if $DEBUG > 3;
676             $day_folder_size += $size;
677             $day_folder_real_size += $info->{size};
678             $day_folder_files += scalar @{ $info->{files} };
679            
680             # day-time column
681             my $iter = $tree_store->append($day_iter);
682             $tree_store->set(
683             $iter,
684             (
685             COL_DATE, $date,
686             COL_HDATE,
687             format_date($date),
688             COL_TIME, $time, COL_NAME, $time, COL_SIZE, $size,
689             COL_HSIZE,
690             ( sprintf "%sB (%sB)", format_bytes($size), format_bytes( $info->{size} ) ),
691             COL_FILES,
692             scalar @{ $info->{files} },
693             COL_LABEL,
694             __("$info->{label}")
695             . ( "$info->{label}" ? ", " : "" )
696             . (
697             $date_of_last_backup eq $basename
698             ? __("Last backup.")
699             : ""
700             ),
701             COL_PATH,
702             $filename,
703             COL_LAST_BACKUP,
704             $date_of_last_backup eq $basename,
705             COL_WEIGHT,
706             800,
707             )
708             );
709             }
710            
711             # append last day folder
712             $tree_store->set(
713             $day_iter,
714             (
715             COL_SIZE, $day_folder_size, COL_HSIZE,
716             ( sprintf "%sB (%sB)", format_bytes($day_folder_size), format_bytes($day_folder_real_size) ),
717             COL_FILES, $day_folder_files,
718             )
719             ) if ref $day_iter;
720             }
721            
722             $this->size_all->set_text( format_bytes( folder_size($folder) ) );
723            
724             #this will create a treeview, specify $tree_store as its model
725             $this->tree_view->set_model($tree_store);
726             $this->exclude_configure;
727            
728             #$this->window->set_sensitive(TRUE);
729             }
730            
731             sub get_backup_info {
732             my ( $this, $filename, $infoname ) = @_;
733            
734             tie my %info, 'Tie::DataDumper', $infoname
735             or warn "Problem tying %info: $!";
736            
737             $info{folders} = 0 unless exists $info{folders};
738             $info{files} = [] unless exists $info{files};
739             $info{label} = '' unless exists $info{label};
740             $info{size} = 0 unless exists $info{size};
741            
742             return \%info;
743             }
744            
745             #sub get_changed_files {
746             # my ( $this, $filename ) = @_;
747             # my $cmd = qq{ env LANG=en_GB.utf8 nice --adjustment=17 \\
748             # env LANG=en_GB.utf8 tar --list \\
749             # --file "$filename" \\
750             # | nice --adjustment=17 grep -E "[^//]\$"
751             # };
752             # printf "cmd %s\n", $cmd if $DEBUG > 0;
753             #
754             # my @changed_files = `$cmd`;
755             # chomp @changed_files;
756             #
757             # printf "changed_files %s\n", scalar @changed_files if $DEBUG > 3;
758             # return @changed_files;
759             #}
760            
761             sub get_store_folder {
762             my ($this) = @_;
763             return sprintf "%s%s", abs_path( $this->get_main_store_folder || "" ), abs_path( $this->folder->get_filename || "" );
764             }
765            
766             sub on_tree_view_button_press_event {
767             my ( $this, $widget, $event ) = @_;
768            
769             #print "on_tree_view_button_press_event $this, $widget", $event->type, "\n" if $DEBUG > 3;
770            
771             $this->restore_button->set_sensitive(TRUE);
772             $this->{tree_view_2button_press} = $event->type eq "2button-press";
773            
774             return;
775             }
776            
777             sub on_tree_view_button_release_event {
778             my ( $this, $widget, $event ) = @_;
779            
780             #print "on_tree_view_button_release_event %s %s %s\n", $this, $widget, $this->{tree_view_2button_press}, "" if $DEBUG > 3;
781            
782             my $selected = $this->tree_view->get_selection->get_selected;
783            
784             if ( ref $selected ) {
785            
786             if ( $this->{tree_view_2button_press} ) # double click
787             {
788             my $path = $this->tree_view->get_model->get( $selected, COL_PATH );
789            
790             printf "*** %s\n", $path if $DEBUG > 3;
791            
792             system $TarOpenCmd, $path;
793             }
794             else {
795             my $last_backup = $this->tree_view->get_model->get( $selected, COL_LAST_BACKUP );
796            
797             printf "*** %s\n", $last_backup ? 1 : 0 if $DEBUG > 3;
798            
799             if ($last_backup) {
800             $this->backup_remove_button->set_sensitive(TRUE);
801             }
802             else {
803             $this->backup_remove_button->set_sensitive(FALSE);
804             }
805            
806             }
807             }
808            
809             return;
810             }
811            
812             #backup
813             #
814            
815             sub on_backup_button_clicked {
816             my ($this) = @_;
817             print "on_backup_button_clicked $this\n" if $DEBUG > 3;
818            
819             $this->window->set_sensitive(FALSE);
820             $this->backup_changed_files_label->set_text(0);
821             $this->backup_folders_label->set_text(0);
822             $this->backup_elapsed_time_label->set_text( sprintf "%s", strtime(0) );
823             $this->backup_estimated_time_label->set_text( sprintf "%s / %s", map { strtime(0) } ( 0, 0 ) );
824             $this->backup_file_label->set_text("");
825             $this->backup_progress(0);
826             $this->backup_notification->present;
827            
828             $this->backup_folder;
829            
830             $this->{folder_recycle_button}->set_uri( $this->folder->get_uri );
831             $this->fill_tree;
832            
833             $this->backup_notification->hide;
834             $this->window->set_sensitive(TRUE);
835             return;
836             }
837            
838             sub rmdir_p {
839             my ($folder) = @_;
840             while ( rmdir $folder ) {
841             $folder = dirname $folder;
842             }
843             return;
844             }
845            
846             sub backup_folder {
847             my ($this) = @_;
848            
849             #$this->{backup_folder} = TRUE;
850            
851             my $date = strftime( "%F %X", localtime );
852            
853             $this->log_add_text( sprintf "\n%s\n", "*" x 42 );
854             $this->log_add_text( sprintf __("%s Starting backup . . .\n"), $date );
855            
856             $this->backup_progress(0);
857             $this->backup_notification->{startTime} = time;
858            
859             my $folder = abs_path $this->folder->get_filename;
860             my $store = $this->get_store_folder;
861             my $mainstore = $this->get_main_store_folder;
862            
863             my $current_dat = "$store/$CurrentDat";
864             my $process_dat = "$store/$ProcessDat";
865             my $archive = "$store/$date.tar.bz2";
866             my $tardat = "$store/$date.dat.bz2";
867             my $excludes = "$store/$ExcludesFile";
868             my $first = -e $excludes;
869            
870             $this->{cleanup} = sub {
871             print "cleanup backup @_\n" if $DEBUG > 3;
872             unlink $tardat;
873             unlink $process_dat;
874             unlink $archive;
875             unless ( -s $current_dat ) {
876             unlink $current_dat;
877             unlink $excludes;
878             rmdir_p $store;
879             }
880             if (@_) {
881             $this->gtk_main_quit;
882             exit;
883             }
884             };
885            
886             $SIG{$_} = $this->{cleanup} foreach @SIGS;
887            
888             system "mkdir", "-p", $store;
889             system "cp", $current_dat, $process_dat if -e $current_dat;
890            
891             system "touch", $current_dat;
892             $this->save_excludes;
893            
894             my $cmd = qq{ env LANG=en_GB.utf8 nice --adjustment=17 \\
895             env LANG=en_GB.utf8 tar --create \\
896             --verbose \\
897             --directory "$folder" \\
898             --file "$archive" \\
899             --listed-incremental "$process_dat" \\
900             --preserve-permissions \\
901             --ignore-failed-read \\
902             --exclude "$mainstore" \\
903             --exclude-from "$excludes" \\
904             --bzip2 \\
905             ./ 2>&1 \\
906             };
907            
908             printf "cmd %s\n", $cmd if $DEBUG > 3;
909             die $! unless $this->{tarpid} = open TAR, "-|", $cmd;
910            
911             my $size = 1;
912             my $files = {};
913             my $total_size = 1;
914             my $folders = 0;
915             my $utf8 = Unicode::UTF8simple->new;
916             while () {
917            
918             print $_ if $DEBUG > 3;
919            
920             #last unless $this->{backup_folder};
921             chomp;
922             $_ = $utf8->fromUTF8( "iso-8859-1", $_ );
923            
924             if (s|^\./||o) {
925             my $path = "$folder/$_";
926            
927             if ( -d $path ) {
928             $this->backup_folders_label->set_text( ++$folders );
929             Gtk2->main_iteration while Gtk2->events_pending;
930             }
931             else {
932             unless ( exists $files->{$path} ) {
933             $total_size += $files->{$path} = ( -s $path ) || 0;
934             $this->backup_changed_files_label->set_text( sprintf "%d [%sB]", scalar keys %$files,
935             format_bytes($total_size) );
936             }
937            
938             $files->{$path} = 0
939             unless defined $files->{$path}; # wegen: -s link = 0
940            
941             my @times =
942             map { strtime($_) } estimated_time( $this->backup_notification->{startTime}, $size, $total_size );
943            
944             $this->backup_elapsed_time_label->set_text( sprintf "%s", $times[0] );
945             $this->backup_estimated_time_label->set_text( sprintf "%s / %s", @times[ 1, 2 ] );
946             $this->backup_file_label->set_text( sprintf "%s [%sB]", $path, format_bytes( $files->{$path} ) );
947            
948             $this->backup_progress( $size / $total_size );
949            
950             $size += $files->{$path};
951             }
952            
953             }
954             elsif ( m|^tar: \./(.*?): Directory is new$|o
955             || m|^tar: \./(.*?): Directory has been renamed from.*$|o )
956             {
957            
958             #print "$1\n" if $DEBUG > 3;
959             $files->{$_} = -s $_ foreach get_files("$folder/$1");
960             $total_size += folder_size("$folder/$1");
961             $this->backup_changed_files_label->set_text( sprintf "%d [%sB]", scalar keys %$files, format_bytes($total_size) );
962             Gtk2->main_iteration while Gtk2->events_pending;
963             }
964             elsif (/^(tar: ).*?(Warning:)/o) {
965             print "$_\n" if $DEBUG > 3;
966             $this->log_add_text( $_, "\n" );
967             }
968             elsif (/^(tar: )?(Terminated|Killed|Hangup)$/o) {
969             print "$_\n" if $DEBUG > 3;
970             $this->log_add_text( $_, "\n" );
971             }
972             else {
973             print "$_\n" if $DEBUG > 3;
974             }
975             }
976             close TAR;
977            
978             # are there some heavy errors
979             if ($?) {
980             $this->log_add_text( sprintf __("Tar exited with status %s\n"), $? );
981            
982             if ( $? == 512 ) {
983             $this->log_add_text( __("The 512 exit status means tar thinks it failed for some reason.\n") );
984             $this->log_add_text( __("This could be caused by files with no permission.\n") );
985             $? = FALSE;
986             }
987             }
988            
989             if ($?) { # cancel / heavy error
990             &{ $this->{cleanup} }();
991             }
992             else { # everything is fine
993             my $retval = system qq{ nice --adjustment=17 \\
994             bzip2 -c9 "$process_dat" > "$tardat" \\
995             };
996             printf "bzip2 returned %s\n", $retval if $DEBUG > 3;
997            
998             $SIG{$_} = 'IGNORE' foreach @SIGS;
999            
1000             system "cp", $process_dat, $current_dat;
1001             unlink $process_dat;
1002            
1003             #store date of current backup
1004             $this->store_restore_date($archive);
1005            
1006             #store size
1007             my $infofile = "$store/$date.info.txt";
1008             my $info = $this->get_backup_info( $archive, $infofile );
1009             $info->{label} = "First Backup" unless $first;
1010             $info->{folders} = $folders;
1011             $info->{files} = [ keys %$files ];
1012             $info->{size} = $total_size;
1013             tied(%$info)->save;
1014             }
1015            
1016             #$this->{backup_folder} = FALSE;
1017             $this->{tarpid} = 0;
1018            
1019             if ( $this->backup_notification->{startTime} ) {
1020             $this->log_add_text( sprintf __("Changed files: %d\n"), scalar keys %$files );
1021             $this->log_add_text( sprintf __("Folders: %d\n"), $folders );
1022             $this->log_add_text( sprintf __("Total size: %s\n"), format_bytes($total_size) );
1023             $this->log_add_text( sprintf __("Total time: %s\n"),
1024             strtime( localtime( time - $this->backup_notification->{startTime} ) ) );
1025             }
1026            
1027             $this->log_add_text( sprintf __("%s Backup done.\n"), strftime( "%F %X", localtime ) )
1028             if $this->backup_notification->{startTime};
1029            
1030             $SIG{$_} = sub { $this->gtk_main_quit }
1031             foreach @SIGS;
1032             }
1033            
1034             sub backup_progress {
1035             my ( $this, $fraction ) = @_;
1036            
1037             $this->backup_progressbar->set_fraction($fraction);
1038             $this->backup_progressbar->set_text( sprintf "%.2f %%", $fraction * 100 );
1039            
1040             #$this->backup_notification->set_title( sprintf "Backup in progress %.2f %%", $fraction * 100 );
1041            
1042             Gtk2->main_iteration while Gtk2->events_pending;
1043            
1044             return;
1045             }
1046            
1047             sub on_cancel_backup {
1048             my ($this) = @_;
1049             printf "on_cancel_backup %s\n", $this->{tarpid} if $DEBUG > 3;
1050             system "pkill", "-P", $this->{tarpid};
1051             $this->backup_notification->{startTime} = 0;
1052             $this->backup_progressbar->set_fraction(1);
1053             $this->backup_progressbar->set_text( __ "Canceling Backup ..." );
1054            
1055             #$this->{backup_folder} = FALSE;
1056             $this->log_add_text( sprintf __("%s Backup canceled.\n"), strftime( "%F %X", localtime ) );
1057             return 1;
1058             }
1059            
1060             #exclude
1061             #
1062            
1063             sub exclude_configure {
1064             my ($this) = @_;
1065            
1066             #printf "exclude_configure %s\n", $this->get_excludes_filename if $DEBUG > 3;
1067            
1068             $this->exclude_clear;
1069            
1070             my $folder = abs_path $this->folder->get_filename || "";
1071            
1072             my $excludes = $this->get_excludes_filename;
1073            
1074             #unlink $excludes;
1075            
1076             if ( -e $excludes ) {
1077             my @excludes = `cat "$excludes"`;
1078             foreach (@excludes) {
1079             chomp;
1080             next unless $_;
1081             if ( -f "$folder/$_" ) {
1082             $this->exclude_add( EXCLUDE_FILE, "$folder/$_" );
1083             }
1084             elsif ( -d "$folder/$_" ) {
1085             $this->exclude_add( EXCLUDE_FOLDER, "$folder/$_" );
1086             }
1087             else {
1088             $this->exclude_add( EXCLUDE_PATTERN, $_ );
1089             }
1090             }
1091            
1092             }
1093             elsif ( $ENV{HOME} =~ /^\Q$folder/ ) {
1094             $this->exclude_add( EXCLUDE_PATTERN, "*Trash*" );
1095             $this->exclude_add( EXCLUDE_FILE, "$ENV{HOME}/.xsession-errors" );
1096             }
1097            
1098             }
1099            
1100             sub exclude_clear {
1101             my ($this) = @_;
1102             $this->exclude_box->remove($_) foreach $this->exclude_box->get_children;
1103             return;
1104             }
1105            
1106             sub on_exclude_add {
1107             my ($this) = @_;
1108            
1109             #printf "on_exclude_add %s\n", $this->exclude_combo->get_active if $DEBUG > 3;
1110             $this->exclude_add( $this->exclude_combo->get_active );
1111             return;
1112             }
1113            
1114             sub exclude_add {
1115             my ( $this, $index, @values ) = @_;
1116            
1117             #printf "exclude_add %s\n", $index if $DEBUG > 3;
1118            
1119             my $widget = undef;
1120             $widget = $this->exclude_folder_add(@values) if $index == EXCLUDE_FOLDER;
1121             $widget = $this->exclude_file_add(@values) if $index == EXCLUDE_FILE;
1122             $widget = $this->exclude_pattern_add(@values) if $index == EXCLUDE_PATTERN;
1123             return unless ref $widget;
1124            
1125             $this->exclude_combo->set_active($index);
1126            
1127             my $label = new Gtk2::Label( sprintf "%s:", $this->exclude_combo->get_active_text );
1128             my $remove_button = Gtk2::Button->new_from_stock('gtk-remove');
1129            
1130             my $hbox = new Gtk2::HBox( 0, 6 );
1131             $hbox->pack_start( $label, FALSE, FALSE, 0 );
1132             $hbox->pack_start( $widget, TRUE, TRUE, 0 );
1133             $hbox->pack_start( $remove_button, FALSE, FALSE, 0 );
1134             $hbox->show_all;
1135            
1136             $remove_button->signal_connect( 'clicked', sub { $this->exclude_folder_remove($hbox) } );
1137            
1138             $this->exclude_box->add($hbox);
1139            
1140             $this->save_excludes;
1141             return;
1142             }
1143            
1144             sub exclude_folder_remove {
1145             my ( $this, $widget ) = @_;
1146            
1147             #printf "exclude_folder_remove %d\n", $widget if $DEBUG > 3;
1148             $this->exclude_box->remove($widget);
1149             $this->save_excludes;
1150             return;
1151             }
1152            
1153             sub exclude_folder_add {
1154             my ( $this, $folder ) = @_;
1155            
1156             printf "exclude_folder_add %s\n", abs_path( $folder || $this->folder->get_filename )
1157             if $DEBUG > 3;
1158            
1159             my $widget = new Gtk2::FileChooserButton( __("Select folder"), 'select-folder' );
1160            
1161             $widget->set_current_folder( abs_path $folder || $this->folder->get_filename );
1162             $widget->{pattern} = abs_path $folder || $this->folder->get_filename;
1163            
1164             $widget->signal_connect(
1165             'selection-changed',
1166             sub {
1167             return unless $widget->get_filename;
1168             printf "** exclude_folder_set %s\n", abs_path $widget->get_filename
1169             if $DEBUG > 3;
1170             $widget->{pattern} = abs_path $widget->get_filename;
1171             $this->save_excludes;
1172             }
1173             );
1174            
1175             return $widget;
1176             }
1177            
1178             sub exclude_file_add {
1179             my ( $this, $file ) = @_;
1180            
1181             printf "** exclude_file_add %s\n", abs_path $file || $this->folder->get_filename
1182             if $DEBUG > 3;
1183            
1184             my $widget = new Gtk2::FileChooserButton( __("Select file"), 'open' );
1185            
1186             if ( $file and -f $file ) {
1187             $widget->set_filename($file);
1188             $widget->{pattern} = $file;
1189             }
1190             else {
1191             $widget->set_current_folder( abs_path $this->folder->get_filename );
1192             }
1193            
1194             $widget->signal_connect(
1195             'selection-changed',
1196             sub {
1197             return unless $widget->get_filename;
1198             printf "** exclude_file_set %s\n", abs_path $widget->get_filename
1199             if $DEBUG > 3;
1200             $widget->{pattern} = abs_path $widget->get_filename;
1201             $this->save_excludes;
1202             }
1203             );
1204            
1205             return $widget;
1206             }
1207            
1208             sub exclude_pattern_add {
1209             my ( $this, $pattern ) = @_;
1210            
1211             #printf "exclude_pattern_add %s\n", $pattern || "" if $DEBUG > 3;
1212             my $widget = new Gtk2::Entry;
1213            
1214             if ($pattern) {
1215             $widget->set_text($pattern);
1216             $widget->{pattern} = $pattern;
1217             }
1218            
1219             $widget->signal_connect(
1220             'changed',
1221             sub {
1222             $widget->{pattern} = $widget->get_text;
1223             $this->save_excludes;
1224             }
1225             );
1226            
1227             return $widget;
1228             }
1229            
1230             sub save_excludes {
1231             my ($this) = @_;
1232             return unless -e $this->get_store_folder . "/$CurrentDat";
1233             my $folder = abs_path $this->folder->get_filename;
1234             my $excludes = $this->get_excludes_filename;
1235            
1236             #printf "save_excludes\n" if $DEBUG > 3;
1237            
1238             open( EXCLUDES, ">", $excludes ) || die $!;
1239             printf EXCLUDES "%s\n", join "\n", map { s/^\Q$folder\E\/?//; $_ }
1240             grep { $_ }
1241             map { ( $_->get_children )[1]->{pattern} } $this->exclude_box->get_children;
1242             close EXCLUDES;
1243             return;
1244             }
1245            
1246             sub get_excludes_filename {
1247             my ($this) = @_;
1248             my $store = $this->get_store_folder;
1249             my $excludes = "$store/$ExcludesFile";
1250             return $excludes;
1251             }
1252            
1253             #restore
1254             #
1255            
1256             sub on_restore_folder_changed {
1257             my ($this) = @_;
1258             printf "on_restore_folder_changed %s\n", abs_path $this->restore_folder->get_filename if $DEBUG > 3;
1259             $this->gconf( "restore-folder", abs_path $this->restore_folder->get_filename );
1260             return;
1261             }
1262            
1263             sub on_restore_extract_modification_time_changed {
1264             my ($this) = @_;
1265             printf "on_restore_extract_modification_time_changed %s\n", $this->restore_extract_modifiction_time->get_active ? 1 : 0
1266             if $DEBUG > 3;
1267             $this->gconf( "restore-extract-modification-time", $this->restore_extract_modifiction_time->get_active ? 1 : 0 );
1268             return;
1269             }
1270            
1271             sub on_restore_button_clicked {
1272             my ($this) = @_;
1273             print "on_restore_button_clicked $this\n" if $DEBUG > 3;
1274            
1275             my $selected = $this->tree_view->get_selection->get_selected;
1276             my ( $hdate, $time ) = $this->tree_view->get_model->get( $selected, COL_HDATE, COL_TIME );
1277            
1278             $this->restore_backup_from_label->set_text("$hdate $time");
1279            
1280             $this->window->set_sensitive(FALSE);
1281             $this->restore_dialog->show;
1282             return;
1283             }
1284            
1285             sub on_restore_dialog_cancel {
1286             my ( $this, $widget ) = @_;
1287             print "on_restore_folder_dialog_cancel $this\n" if $DEBUG > 3;
1288             $this->restore_dialog->hide;
1289             $this->window->set_sensitive(TRUE);
1290             return 1;
1291             }
1292            
1293             sub on_restore_dialog_ok {
1294             my ( $this, $widget ) = @_;
1295            
1296             $this->restore_dialog->hide;
1297             $this->restore_notification->show;
1298            
1299             $this->restore_backup;
1300            
1301             $this->fill_tree;
1302            
1303             $this->restore_notification->hide;
1304             $this->window->set_sensitive(TRUE);
1305             return;
1306             }
1307            
1308             sub restore_backup {
1309             my ($this) = @_;
1310            
1311             my $restore_to_folder = abs_path $this->restore_folder->get_filename;
1312             my @files = $this->get_files_to_restore;
1313            
1314             $this->log_add_text( sprintf "\n%s\n", "*" x 42 );
1315             $this->log_add_text( sprintf __("%s Starting restore . . .\n"), strftime( "%F %X", localtime ) );
1316            
1317             $this->restore_progress(0);
1318             $this->restore_notification->{startTime} = time;
1319            
1320             my $store = $this->get_store_folder;
1321             my $utf8 = Unicode::UTF8simple->new;
1322            
1323             my $backup = 0;
1324             my $counter = 0;
1325             my $numFiles = 0;
1326             my $size = 0;
1327             my $elapsedSize = 0;
1328             my $totalSize = 0;
1329             foreach my $filename (@files) {
1330             my $infofile = "$store/" . basename( $filename, ".tar.bz2" ) . ".info.txt";
1331             my $info = $this->get_backup_info( $filename, $infofile );
1332             $numFiles += @{ $info->{files} };
1333             $totalSize += $info->{size};
1334            
1335             $this->log_add_text( sprintf "%s %d\n", $infofile, $info->{size} );
1336             }
1337            
1338             $this->log_add_text( sprintf "total size %d\n", $totalSize );
1339            
1340             printf "***restore_backup to folder: %s %d\n", $restore_to_folder, $totalSize
1341             if $DEBUG > 3;
1342            
1343             foreach my $filename (@files) {
1344             printf "file: %s\n", $filename if $DEBUG > 3;
1345            
1346             ##########################################################################
1347             my $infofile = "$store/" . basename( $filename, ".tar.bz2" ) . ".info.txt";
1348             my $info = $this->get_backup_info( $filename, $infofile );
1349             ##########################################################################
1350            
1351             $this->log_add_text( sprintf __("restoring backup from %s\n"), basename( $filename, ".tar.bz2" ) );
1352             $this->restore_process_backup_label->set_text( sprintf __("%d / %d"), ++$backup, scalar @files );
1353             Gtk2->main_iteration while Gtk2->events_pending;
1354            
1355             my $touch =
1356             $this->restore_extract_modifiction_time->get_active
1357             ? ""
1358             : "--touch";
1359            
1360             my $cmd = qq{ env LANG=en_GB.utf8 nice --adjustment=17 \\
1361             env LANG=en_GB.utf8 tar --extract \\
1362             --verbose \\
1363             --directory "$restore_to_folder" \\
1364             --file "$filename" \\
1365             --preserve-permissions \\
1366             $touch \\
1367             --listed-incremental /dev/null \\
1368             ./ 2>&1 \\
1369             };
1370            
1371             printf "cmd %s\n", $cmd if $DEBUG > 4;
1372             die $! unless $this->{tarpid} = open TAR, "-|", $cmd;
1373             while () {
1374            
1375             print $_ if $DEBUG > 0;
1376            
1377             chomp;
1378             $_ = $utf8->fromUTF8( "iso-8859-1", $_ );
1379             my $path = "$restore_to_folder/$_";
1380            
1381             if ( -d $path ) {
1382             }
1383             elsif ( -f $path ) {
1384             $size = -s $path;
1385             $elapsedSize += $size;
1386            
1387             my @times =
1388             map { strtime($_) } estimated_time( $this->restore_notification->{startTime}, $elapsedSize, $totalSize );
1389            
1390             $this->restore_elapsed_time_label->set_text( sprintf "%s", $times[0] );
1391             $this->restore_estimated_time_label->set_text( sprintf "%s / %s", @times[ 1, 2 ] );
1392            
1393             $this->restore_file_label->set_text( sprintf "%s [%sB]", $_, format_bytes($size) );
1394            
1395             $this->restore_progress( $elapsedSize / $totalSize );
1396            
1397             Gtk2->main_iteration while Gtk2->events_pending;
1398             }
1399             }
1400             close TAR;
1401             printf "tar returned %s\n", $? if $DEBUG > 3;
1402            
1403             if ($?) { # cancel / error ...
1404            
1405             if ( $? == 512 ) {
1406            
1407             #$this->log_add_text();
1408             $? = FALSE;
1409             }
1410             else {
1411             $this->log_add_text( sprintf __("Tar exited with status %s\n"), $? );
1412             last;
1413             }
1414             }
1415             else { # everything is fine
1416             }
1417            
1418             }
1419            
1420             #store date
1421             $this->store_restore_date( $files[$#files] );
1422            
1423             #finish
1424             $this->{tarpid} = 0;
1425             $this->log_add_text( sprintf __("%s Restore done . . .\n"), strftime( "%F %X", localtime ) );
1426             }
1427            
1428             sub get_files_to_restore {
1429             my ($this) = @_;
1430            
1431             my @files = ();
1432            
1433             my $selected = $this->tree_view->get_selection->get_selected;
1434             my $file = $this->tree_view->get_model->get( $selected, COL_PATH );
1435             my $folder = dirname $file;
1436            
1437             #printf "***get_files_to_restore file: %s\n", $file if $DEBUG > 3;
1438             #printf "***get_files_to_restore folder %s\n", $folder if $DEBUG > 3;
1439            
1440             foreach my $filename ( grep { m/\.tar\.bz2$/ } get_files($folder) ) {
1441             push @files, $filename;
1442             last if $filename eq $file;
1443             }
1444            
1445             return @files;
1446             }
1447            
1448             sub on_cancel_restore {
1449             my ($this) = @_;
1450             printf "on_cancel_restore %s\n", $this->{tarpid} if $DEBUG > 3;
1451             system "pkill", "-P", $this->{tarpid};
1452            
1453             $this->restore_notification->{startTime} = 0;
1454             $this->restore_progressbar->set_fraction(1);
1455             $this->restore_progressbar->set_text( __ "Canceling Restore ..." );
1456            
1457             $this->log_add_text( sprintf __("%s Restore canceled.\n"), strftime( "%F %X", localtime ) );
1458             return 1;
1459             }
1460            
1461             sub restore_progress {
1462             my ( $this, $fraction ) = @_;
1463            
1464             $this->restore_progressbar->set_fraction($fraction);
1465             $this->restore_progressbar->set_text( sprintf "%.2f %%", $fraction * 100 );
1466            
1467             #$this->backup_notification->set_title( sprintf "Backup in progress %.2f %%", $fraction * 100 );
1468            
1469             return;
1470             }
1471            
1472             # remove backup
1473             #
1474            
1475             sub on_backup_remove_button_clicked {
1476             my ($this) = @_;
1477             print "on_backup_remove_button_clicked $this\n" if $DEBUG > 3;
1478            
1479             my $selected = $this->tree_view->get_selection->get_selected;
1480             my ( $hdate, $time ) = $this->tree_view->get_model->get( $selected, COL_HDATE, COL_TIME );
1481            
1482             $this->backup_remove_from_label->set_text("$hdate $time");
1483            
1484             $this->window->set_sensitive(FALSE);
1485             $this->remove_dialog->show;
1486             return;
1487             }
1488            
1489             sub on_backup_remove_dialog_cancel {
1490             my ( $this, $widget ) = @_;
1491             print "on_restore_folder_dialog_cancel $this\n" if $DEBUG > 3;
1492             $this->remove_dialog->hide;
1493             $this->window->set_sensitive(TRUE);
1494             return 1;
1495             }
1496            
1497             sub on_backup_remove_dialog_ok {
1498             my ( $this, $widget ) = @_;
1499             $this->remove_dialog->hide;
1500             $this->remove_backup;
1501             $this->fill_tree;
1502             $this->window->set_sensitive(TRUE);
1503             $this->backup_remove_button(FALSE);
1504             return;
1505             }
1506            
1507             sub remove_backup {
1508             my ($this) = @_;
1509            
1510             my $restore_to_folder = abs_path $this->restore_folder->get_filename;
1511             my @files = $this->get_files_to_restore;
1512            
1513             my $selected = $this->tree_view->get_selection->get_selected;
1514             my ( $hdate, $date, $time, $file ) = $this->tree_view->get_model->get( $selected, COL_HDATE, COL_DATE, COL_TIME, COL_PATH );
1515             my $folder = dirname $file;
1516             my $store = $this->get_store_folder;
1517            
1518             $this->log_add_text( sprintf "\n%s\n", "*" x 42 );
1519             $this->log_add_text( sprintf __("%s removing backup from %s %s\n"), strftime( "%F %X", localtime ), $hdate, $time );
1520            
1521             {
1522             my $archive = "$store/$date $time.tar.bz2";
1523             my $tardat = "$store/$date $time.dat.bz2";
1524             my $infofile = "$folder/$date $time.info.txt";
1525            
1526             print "$archive\n";
1527             print "$tardat\n";
1528             print "$infofile\n";
1529            
1530             unlink( $archive, $tardat, $infofile );
1531             pop @files;
1532             }
1533            
1534             {
1535             my $current_dat = "$store/$CurrentDat";
1536             if (@files) {
1537             my $date = basename( $files[$#files], ".tar.bz2" );
1538             my $tardat = "$store/$date.dat.bz2";
1539            
1540             # print "bzip2 -c -d '$tardat' >'$current_dat'\n";
1541             system "bzip2 -c -d '$tardat' >'$current_dat'";
1542            
1543             $this->store_restore_date( $files[$#files] );
1544             }
1545             else {
1546             my $excludes = "$store/$ExcludesFile";
1547             my $dateTxt = "$store/$DateTxt";
1548            
1549             unlink $current_dat, $excludes, $dateTxt;
1550             rmdir_p $store;
1551             }
1552             }
1553            
1554             $this->log_add_text( sprintf __("%s remove done . . .\n"), strftime( "%F %X", localtime ) );
1555             }
1556            
1557             # schedule
1558             #
1559            
1560             sub on_schedule_enabled_button_toggled {
1561             my ( $this, $widget ) = @_;
1562             print "on_schedule_enabled_button_toggled $this\n" if $DEBUG > 3;
1563             $this->time_hbox->set_sensitive( $widget->get_active );
1564             $this->wdays_hbox->set_sensitive( $widget->get_active );
1565             return;
1566             }
1567            
1568             #store
1569             #
1570            
1571             sub on_store_folder_changed {
1572             my ($this) = @_;
1573             printf "on_store_folder_changed %s\n", $this->get_main_store_folder
1574             if $DEBUG > 3;
1575            
1576             my $store = abs_path $this->get_main_store_folder;
1577            
1578             #system "mkdir", "-p", $store;
1579             $this->gconf( 'store-folder', abs_path $this->store_folder->get_filename );
1580             $this->gconf( 'store-folder-name', $this->store_folder_name->get_text );
1581            
1582             my @folders = $this->get_store_folders;
1583             $this->{folder_recycle_button}->add_filename($_) foreach @folders;
1584            
1585             $this->fill_tree;
1586             return;
1587             }
1588            
1589             sub get_main_store_folder {
1590             my ($this) = @_;
1591             return abs_path sprintf( "%s/%s", $this->store_folder->get_filename, $this->store_folder_name->get_text );
1592             }
1593            
1594             sub get_store_folders {
1595             my ($this) = @_;
1596             my $store = $this->get_main_store_folder;
1597             return map { s/^$store//; $_; }
1598             grep { -e "$_/$CurrentDat" } get_all_sub_folders($store);
1599             }
1600            
1601             sub on_store_folder_name_key_release_event {
1602             my ( $this, $widget, $event ) = @_;
1603            
1604             # if ( $event->keyval == $Gtk2::Gdk::Keysyms{KP_Enter}
1605             # or $event->keyval == $Gtk2::Gdk::Keysyms{Return} )
1606             {
1607             printf "on_store_folder_name_changed %s\n", $event->keyval if $DEBUG > 3;
1608             $this->on_store_folder_changed;
1609             }
1610             }
1611            
1612             #expander
1613             #
1614            
1615             sub configure_expander {
1616             my ($this) = @_;
1617             printf "*** configure_expander\n" if $DEBUG > 3;
1618            
1619             $this->exclude_expander->set_expanded( $this->gconf('exclude_expander') )
1620             if defined $this->gconf('exclude_expander');
1621            
1622             $this->schedule_expander->set_expanded( $this->gconf('schedule_expander') )
1623             if defined $this->gconf('schedule_expander');
1624            
1625             $this->store_expander->set_expanded( $this->gconf('store_expander') )
1626             if defined $this->gconf('store_expander');
1627            
1628             $this->log_expander->set_expanded( $this->gconf('log_expander') )
1629             if defined $this->gconf('log_expander');
1630            
1631             return;
1632             }
1633            
1634             sub on_expander_activate {
1635             my ( $this, $widget ) = @_;
1636             printf "%s, %s\n", $widget->get_name, not $widget->get_expanded ? 1 : 0
1637             if $DEBUG > 3;
1638             $this->gconf( $widget->get_name, not $widget->get_expanded ? 1 : 0 );
1639             return;
1640             }
1641            
1642             #expander nop
1643             # disables expander
1644             #
1645            
1646             sub expander_nop {
1647             my ( $this, $expander ) = @_;
1648             $expander->set_expanded(FALSE);
1649             return;
1650             }
1651            
1652             #log
1653             #
1654            
1655             sub log_init {
1656             my ($this) = @_;
1657             my $tview = $this->log_textview;
1658             my $buffer = $tview->get_buffer();
1659             $this->{log_end_mark} = $buffer->create_mark( 'end', $buffer->get_end_iter, FALSE );
1660             $buffer->signal_connect( insert_text => \&on_log_insert_text, $this );
1661             }
1662            
1663             sub log_add_text {
1664             my ( $this, @text ) = @_;
1665             my $tview = $this->log_textview;
1666             my $content = join "", @text;
1667             my $buffer = $tview->get_buffer();
1668             $buffer->insert( $buffer->get_end_iter, $content );
1669             Gtk2->main_iteration while Gtk2->events_pending;
1670             }
1671            
1672             sub log_clear {
1673             my ($this) = @_;
1674             my $tview = $this->log_textview;
1675             my $buffer = $tview->get_buffer();
1676             $buffer->set_text("");
1677             }
1678            
1679             sub on_log_insert_text {
1680             my $this = pop @_;
1681             my $tview = $this->log_textview;
1682             $tview->scroll_mark_onscreen( $this->{log_end_mark} );
1683             }
1684            
1685             sub get_tar_version {
1686             my ($this) = @_;
1687             my $cmd = qq{ tar --version };
1688             return `$cmd`;
1689             }
1690            
1691             sub store_restore_date {
1692             my ( $this, $file ) = @_;
1693             my $store = dirname($file);
1694             my $restore_date = basename( $file, ".tar.bz2" );
1695             my $date_txt = "$store/$DateTxt";
1696             system "echo '$restore_date' > '$date_txt'";
1697            
1698             #printf "%s\n", "echo '$restore_date' > '$date_txt'";
1699            
1700             }
1701            
1702             sub fetch_restore_date {
1703             my ( $this, $store ) = @_;
1704             my $date_txt = "$store/$DateTxt";
1705             return "" unless -e $date_txt;
1706             my $date = `cat '$date_txt'`;
1707             chomp $date;
1708             printf "*** %s\n", $date;
1709            
1710             return $date;
1711             }
1712            
1713             # quit
1714            
1715             sub gtk_main_quit {
1716             my ($this) = @_;
1717             print "gtk_main_quit\n" if $DEBUG > 3;
1718             $this->gconf( "pid", 0 );
1719             Gtk2->main_quit;
1720             return;
1721             }
1722            
1723             sub DESTROY {
1724             my ($this) = @_;
1725             return;
1726             }
1727            
1728             1;
1729             __END__