Adding a log window with restore save options.
This commit is contained in:
parent
72103913bb
commit
0b0bb7a7b5
1
Build.PL
1
Build.PL
@ -17,6 +17,7 @@ my $build = Module::Build->new(
|
|||||||
'File::HomeDir' => 0,
|
'File::HomeDir' => 0,
|
||||||
'Moo' => 0,
|
'Moo' => 0,
|
||||||
'namespace::clean' => 0,
|
'namespace::clean' => 0,
|
||||||
|
'UUID::URandom' => 0,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
$build->create_build_script;
|
$build->create_build_script;
|
||||||
|
@ -13,7 +13,16 @@ sub MIGRATIONS {
|
|||||||
'CREATE TABLE options (
|
'CREATE TABLE options (
|
||||||
name TEXT PRIMARY KEY,
|
name TEXT PRIMARY KEY,
|
||||||
value TEXT
|
value TEXT
|
||||||
)',
|
);',
|
||||||
|
'CREATE TABLE logs (
|
||||||
|
uuid TEXT PRIMARY KEY,
|
||||||
|
date datetime NOT NULL,
|
||||||
|
message TEXT NOT NULL,
|
||||||
|
input_file TEXT,
|
||||||
|
output_file TEXT,
|
||||||
|
backup_input_file TEXT,
|
||||||
|
backup_output_file TEXT
|
||||||
|
);',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
1;
|
1;
|
||||||
|
92
lib/GEmeTool/Log.pm
Normal file
92
lib/GEmeTool/Log.pm
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package GEmeTool::Log;
|
||||||
|
|
||||||
|
use v5.16.3;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use Moo;
|
||||||
|
use parent 'Exporter';
|
||||||
|
|
||||||
|
use Digest::SHA qw/sha256_hex/;
|
||||||
|
|
||||||
|
use Path::Tiny;
|
||||||
|
use UUID::URandom qw/create_uuid_string/;
|
||||||
|
|
||||||
|
use GEmeTool::DB;
|
||||||
|
use GEmeTool::Config;
|
||||||
|
|
||||||
|
our @EXPORT_OK = qw/logger/;
|
||||||
|
|
||||||
|
sub logger {
|
||||||
|
return GEmeTool::Log->new;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub msg {
|
||||||
|
my $self = shift;
|
||||||
|
my $msg = shift;
|
||||||
|
my $input_file = shift;
|
||||||
|
my $output_file = shift;
|
||||||
|
my ( $input_file_backup, $output_file_backup ) =
|
||||||
|
$self->backup_files( $input_file, $output_file );
|
||||||
|
my $db = GEmeTool::DB->connect;
|
||||||
|
my $uuid = create_uuid_string();
|
||||||
|
my $insert = <<'EOF';
|
||||||
|
INSERT INTO logs
|
||||||
|
(uuid, date, message, input_file,
|
||||||
|
output_file, backup_input_file,
|
||||||
|
backup_output_file)
|
||||||
|
VALUES (?, datetime('now'), ?, ?, ?, ?, ?);');
|
||||||
|
EOF
|
||||||
|
return $db->do( $insert, {}, $uuid, $msg, $input_file, $output_file,
|
||||||
|
$input_file_backup, $output_file_backup );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub backup_files {
|
||||||
|
my $self = shift;
|
||||||
|
my @files = @_;
|
||||||
|
my $data_dir = GEmeTool::Config->new->data_dir;
|
||||||
|
my $backup_dir = $data_dir->child('backups');
|
||||||
|
$backup_dir->mkpath;
|
||||||
|
@files = map { sub {return undef if !defined $_; return path($_)->absolute}->() } @files;
|
||||||
|
my @result_files;
|
||||||
|
for my $file (@files) {
|
||||||
|
if (!defined $file || !-f $file) {
|
||||||
|
push @result_files, undef;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
my $contents = $file->slurp_raw;
|
||||||
|
my $digest = sha256_hex($contents);
|
||||||
|
my ($possible_output) =
|
||||||
|
grep { $_->basename =~ /^$digest-/ } $backup_dir->children;
|
||||||
|
if ( defined $possible_output ) {
|
||||||
|
|
||||||
|
# We should probably check that the backup
|
||||||
|
# is unmodified, but I cannot
|
||||||
|
# figure a user friendly way to recover from
|
||||||
|
# that error.
|
||||||
|
push @result_files, $possible_output;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
my $basename = $file->basename;
|
||||||
|
my $output_file = $backup_dir->child("$digest-$basename");
|
||||||
|
$output_file->spew_raw($contents);
|
||||||
|
push @result_files, $output_file;
|
||||||
|
}
|
||||||
|
return @result_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_logs {
|
||||||
|
my $self = shift;
|
||||||
|
my $db = GEmeTool::DB->connect;
|
||||||
|
my $query = <<'EOF';
|
||||||
|
SELECT message, date, input_file,
|
||||||
|
output_file, backup_input_file,
|
||||||
|
backup_output_file
|
||||||
|
FROM logs
|
||||||
|
ORDER BY date;
|
||||||
|
EOF
|
||||||
|
my $results = $db->selectall_arrayref($query, {Slice => {}});
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
1;
|
@ -14,6 +14,7 @@ use Rsaves::Constants::Emerald::Rematches;
|
|||||||
|
|
||||||
use Moo;
|
use Moo;
|
||||||
|
|
||||||
|
use GEmeTool::Log qw/logger/;
|
||||||
use namespace::clean;
|
use namespace::clean;
|
||||||
|
|
||||||
has __saves => ( is => 'rw' );
|
has __saves => ( is => 'rw' );
|
||||||
@ -22,6 +23,10 @@ has __extra => ( is => 'rw' );
|
|||||||
|
|
||||||
has input_file => ( is => 'rw', required => 1 );
|
has input_file => ( is => 'rw', required => 1 );
|
||||||
|
|
||||||
|
has _initial_rematches => ( is => 'rw' );
|
||||||
|
|
||||||
|
has _initial_flags => ( is => 'rw' );
|
||||||
|
|
||||||
sub instance {
|
sub instance {
|
||||||
my $class = shift;
|
my $class = shift;
|
||||||
my $input_file = shift;
|
my $input_file = shift;
|
||||||
@ -29,6 +34,11 @@ sub instance {
|
|||||||
return $save;
|
return $save;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub BUILD {
|
||||||
|
my $self = shift;
|
||||||
|
logger()->msg( 'Opening save...', $self->input_file );
|
||||||
|
}
|
||||||
|
|
||||||
sub get_flags_save {
|
sub get_flags_save {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my %flags_by_id = $self->_get_flags_hash_by_id;
|
my %flags_by_id = $self->_get_flags_hash_by_id;
|
||||||
@ -56,13 +66,41 @@ sub get_rematches_save {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub save {
|
sub save {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $file = shift;
|
my $file = shift;
|
||||||
my $extra = $self->__extra;
|
my $extra = $self->__extra;
|
||||||
my $saves = $self->__saves;
|
my $saves = $self->__saves;
|
||||||
|
my $ini_flags = $self->_initial_flags;
|
||||||
|
my $end_flags = $self->get_flags_save;
|
||||||
|
my $ini_rematches = $self->_initial_rematches;
|
||||||
|
my $end_rematches = $self->get_rematches_save;
|
||||||
|
logger()->msg(
|
||||||
|
"Saving, edited data:\n"
|
||||||
|
. $self->diff_boolean_flag_like( $ini_flags, $end_flags )
|
||||||
|
. $self->diff_boolean_flag_like( $ini_rematches, $end_rematches ),
|
||||||
|
$self->input_file,
|
||||||
|
$file,
|
||||||
|
);
|
||||||
Rsaves::save_changes( @$saves, $extra, $file );
|
Rsaves::save_changes( @$saves, $extra, $file );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub diff_boolean_flag_like {
|
||||||
|
my $self = shift;
|
||||||
|
my $initial = shift;
|
||||||
|
my $final = shift;
|
||||||
|
my $result_string = '';
|
||||||
|
for ( my $i = 0 ; $i < scalar @$initial ; $i++ ) {
|
||||||
|
my $flag_ini = $initial->[$i];
|
||||||
|
my $flag_end = $final->[$i];
|
||||||
|
if ( ( !!$flag_ini->{value} ) == ( !!$flag_end->{value} ) ) {
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
$result_string .=
|
||||||
|
"@{[$flag_end->{name}]} => @{[$flag_end->{value} ? 'ON' : 'OFF']}\n";
|
||||||
|
}
|
||||||
|
return $result_string;
|
||||||
|
}
|
||||||
|
|
||||||
sub has_rematch {
|
sub has_rematch {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $flag_id = shift;
|
my $flag_id = shift;
|
||||||
@ -137,6 +175,13 @@ sub _build_saves_and_extra {
|
|||||||
my @saves = get_saves( @saves_raw, $EMERALD_VERSION );
|
my @saves = get_saves( @saves_raw, $EMERALD_VERSION );
|
||||||
$self->__saves( \@saves );
|
$self->__saves( \@saves );
|
||||||
$self->__extra($extra);
|
$self->__extra($extra);
|
||||||
|
$self->store_initial_flags_and_rematch;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub store_initial_flags_and_rematch {
|
||||||
|
my $self = shift;
|
||||||
|
$self->_initial_flags( [@{$self->get_flags_save}] );
|
||||||
|
$self->_initial_rematches( [@{$self->get_rematches_save}] );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _get_rematches_constant_list {
|
sub _get_rematches_constant_list {
|
||||||
|
130
lib/GEmeTool/View/LogWindow.pm
Normal file
130
lib/GEmeTool/View/LogWindow.pm
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
package GEmeTool::View::LogWindow;
|
||||||
|
|
||||||
|
use v5.16.3;
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use utf8;
|
||||||
|
|
||||||
|
use Glib::IO;
|
||||||
|
use Glib::Object::Introspection;
|
||||||
|
use Path::Tiny;
|
||||||
|
|
||||||
|
use GEmeTool::Log qw/logger/;
|
||||||
|
use Moo;
|
||||||
|
|
||||||
|
use namespace::clean;
|
||||||
|
|
||||||
|
Glib::Object::Introspection->setup(
|
||||||
|
basename => 'Gtk',
|
||||||
|
version => '4.0',
|
||||||
|
package => 'Gtk4',
|
||||||
|
);
|
||||||
|
|
||||||
|
Glib::Object::Introspection->setup(
|
||||||
|
basename => 'Gdk',
|
||||||
|
version => '4.0',
|
||||||
|
package => 'Gdk',
|
||||||
|
);
|
||||||
|
|
||||||
|
use namespace::clean;
|
||||||
|
|
||||||
|
has app => (
|
||||||
|
is => 'ro',
|
||||||
|
required => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
has _win => ( is => 'rw', );
|
||||||
|
|
||||||
|
sub start {
|
||||||
|
my $self = shift;
|
||||||
|
$self->_win( Gtk4::Window->new );
|
||||||
|
my $win = $self->_win;
|
||||||
|
$win->set_title('GEmeTool Log Viewer');
|
||||||
|
$win->set_default_size( 600, 600 );
|
||||||
|
$self->fill_logs_win;
|
||||||
|
$win->present;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub fill_logs_win {
|
||||||
|
my $self = shift;
|
||||||
|
my $win = $self->_win;
|
||||||
|
my $logs = GEmeTool::Log->new->get_logs;
|
||||||
|
my $grid_log = Gtk4::Grid->new;
|
||||||
|
my $i = 0;
|
||||||
|
for my $log_message (@$logs) {
|
||||||
|
my $date = $log_message->{date};
|
||||||
|
my $message = $log_message->{message};
|
||||||
|
my $input_file = $log_message->{input_file};
|
||||||
|
my $output_file = $log_message->{output_file};
|
||||||
|
my $backup_input_file = $log_message->{backup_input_file};
|
||||||
|
my $backup_output_file = $log_message->{backup_output_file};
|
||||||
|
$message = "$date $message";
|
||||||
|
|
||||||
|
if ( defined $input_file ) {
|
||||||
|
$message .= "\n with input file $input_file";
|
||||||
|
}
|
||||||
|
if ( defined $output_file ) {
|
||||||
|
$message .= "\n with output file $output_file";
|
||||||
|
}
|
||||||
|
my $label = Gtk4::Label->new($message);
|
||||||
|
$grid_log->attach($label, 0, $i, 10, 1);
|
||||||
|
$self->create_button_restore( $grid_log, 11, $i, 'Restore input file',
|
||||||
|
$backup_input_file, $input_file );
|
||||||
|
$self->create_button_restore( $grid_log, 12, $i, 'Restore output file',
|
||||||
|
$backup_output_file, $output_file );
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
$win->set_child($grid_log);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub open_save {
|
||||||
|
my $self = shift;
|
||||||
|
my $file = shift;
|
||||||
|
my $dest_file = shift;
|
||||||
|
my $file_dialog = Gtk4::FileDialog->new;
|
||||||
|
if ( defined $dest_file ) {
|
||||||
|
$file_dialog->set_initial_folder( Glib::IO::File::new_for_path($dest_file->parent ));
|
||||||
|
$file_dialog->set_initial_name( $dest_file->basename );
|
||||||
|
}
|
||||||
|
$file_dialog->save(
|
||||||
|
$self->_win,
|
||||||
|
undef,
|
||||||
|
sub {
|
||||||
|
my ( $dialog, $res ) = @_;
|
||||||
|
if ( $res->had_error ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
my $dest_file = $dialog->save_finish($res);
|
||||||
|
return if !defined $dest_file;
|
||||||
|
$dest_file = path( $dest_file->get_path );
|
||||||
|
logger()->msg('Restoring backup...', $file, $dest_file);
|
||||||
|
$dest_file->spew_raw( $file->slurp_raw );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub create_button_restore {
|
||||||
|
my $self = shift;
|
||||||
|
my $grid_log = shift;
|
||||||
|
my $column = shift;
|
||||||
|
my $row = shift;
|
||||||
|
my $restore_label = shift;
|
||||||
|
my $file = shift;
|
||||||
|
my $dest_file = shift;
|
||||||
|
if ( !defined $file ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( defined $dest_file ) {
|
||||||
|
$dest_file = path($dest_file);
|
||||||
|
}
|
||||||
|
$file = path($file);
|
||||||
|
return if !-e $file;
|
||||||
|
my $button_restore_backup = Gtk4::Button->new_with_label($restore_label);
|
||||||
|
$button_restore_backup->signal_connect(
|
||||||
|
clicked => sub {
|
||||||
|
$self->open_save( $file, $dest_file );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$grid_log->attach($button_restore_backup, $column, $row, 1, 1);
|
||||||
|
}
|
||||||
|
1;
|
@ -13,6 +13,7 @@ use Data::Dumper;
|
|||||||
use Path::Tiny;
|
use Path::Tiny;
|
||||||
use GEmeTool::Options;
|
use GEmeTool::Options;
|
||||||
use GEmeTool::Save;
|
use GEmeTool::Save;
|
||||||
|
use GEmeTool::View::LogWindow;
|
||||||
|
|
||||||
use Moo;
|
use Moo;
|
||||||
|
|
||||||
@ -79,11 +80,13 @@ sub activate {
|
|||||||
my $menu = Glib::IO::Menu->new;
|
my $menu = Glib::IO::Menu->new;
|
||||||
my $about = Glib::IO::SimpleAction->new( 'about', undef );
|
my $about = Glib::IO::SimpleAction->new( 'about', undef );
|
||||||
my $open = Glib::IO::SimpleAction->new( 'open', undef );
|
my $open = Glib::IO::SimpleAction->new( 'open', undef );
|
||||||
|
my $logs = Glib::IO::SimpleAction->new( 'view_logs', undef );
|
||||||
my $save_as = Glib::IO::SimpleAction->new( 'save_as', undef );
|
my $save_as = Glib::IO::SimpleAction->new( 'save_as', undef );
|
||||||
$save_as->set_enabled(0);
|
$save_as->set_enabled(0);
|
||||||
my $app = $self->_app;
|
my $app = $self->_app;
|
||||||
$app->add_action($about);
|
$app->add_action($about);
|
||||||
$app->add_action($open);
|
$app->add_action($open);
|
||||||
|
$app->add_action($logs);
|
||||||
$app->add_action($save_as);
|
$app->add_action($save_as);
|
||||||
$self->_save_as($save_as);
|
$self->_save_as($save_as);
|
||||||
my $save;
|
my $save;
|
||||||
@ -99,16 +102,25 @@ sub activate {
|
|||||||
$self->activate_save;
|
$self->activate_save;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
$logs->signal_connect(
|
||||||
|
activate => sub {
|
||||||
|
GEmeTool::View::LogWindow->new(app => $self->_app)->start;
|
||||||
|
}
|
||||||
|
);
|
||||||
$about->signal_connect( activate => \&activate_about, );
|
$about->signal_connect( activate => \&activate_about, );
|
||||||
|
|
||||||
my $about_menu_item = Glib::IO::MenuItem->new( 'About', 'app.about' );
|
my $about_menu_item = Glib::IO::MenuItem->new( 'About', 'app.about' );
|
||||||
my $open_menu_item = Glib::IO::MenuItem->new( 'Open', 'app.open' );
|
my $open_menu_item = Glib::IO::MenuItem->new( 'Open', 'app.open' );
|
||||||
|
my $logs_menu_item = Glib::IO::MenuItem->new( 'View logs', 'app.view_logs' );
|
||||||
my $submenu_file = Glib::IO::Menu->new;
|
my $submenu_file = Glib::IO::Menu->new;
|
||||||
|
my $submenu_view = Glib::IO::Menu->new;
|
||||||
my $submenu_help = Glib::IO::Menu->new;
|
my $submenu_help = Glib::IO::Menu->new;
|
||||||
$submenu_help->append_item($about_menu_item);
|
$submenu_help->append_item($about_menu_item);
|
||||||
|
$submenu_view->append_item($logs_menu_item);
|
||||||
$submenu_file->append_item($open_menu_item);
|
$submenu_file->append_item($open_menu_item);
|
||||||
$submenu_file->append_item($save_menu_item);
|
$submenu_file->append_item($save_menu_item);
|
||||||
$menu->append_submenu( 'File', $submenu_file );
|
$menu->append_submenu( 'File', $submenu_file );
|
||||||
|
$menu->append_submenu( 'View', $submenu_view );
|
||||||
$menu->append_submenu( 'Help', $submenu_help );
|
$menu->append_submenu( 'Help', $submenu_help );
|
||||||
$app->set_menubar($menu);
|
$app->set_menubar($menu);
|
||||||
my $box = Gtk4::Box->new( 'vertical', 0 );
|
my $box = Gtk4::Box->new( 'vertical', 0 );
|
||||||
@ -158,10 +170,11 @@ sub activate_save {
|
|||||||
my $win = $self->_win;
|
my $win = $self->_win;
|
||||||
my $dialog = Gtk4::FileDialog->new;
|
my $dialog = Gtk4::FileDialog->new;
|
||||||
my $options = $self->_options;
|
my $options = $self->_options;
|
||||||
my $last_dir = $options->get_last_dir_save;
|
my $last_dir = $options->get_last_dir_open;
|
||||||
if (defined $last_dir && -d $last_dir) {
|
if (defined $last_dir && -d $last_dir) {
|
||||||
my $curdir = Glib::IO::File::new_for_path($last_dir);
|
my $curdir = Glib::IO::File::new_for_path($last_dir);
|
||||||
$dialog->set_initial_folder($curdir);
|
$dialog->set_initial_folder($curdir);
|
||||||
|
$dialog->set_initial_name( path($self->_file)->basename );
|
||||||
}
|
}
|
||||||
$dialog->save(
|
$dialog->save(
|
||||||
$win, undef,
|
$win, undef,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user