1# ClamTk, copyright (C) 2004-2020 Dave M
2#
3# This file is part of ClamTk
4# (https://gitlab.com/dave_m/clamtk-gtk3/).
5#
6# ClamTk is free software; you can redistribute it and/or modify it
7# under the terms of either:
8#
9# a) the GNU General Public License as published by the Free Software
10# Foundation; either version 1, or (at your option) any later version, or
11#
12# b) the "Artistic License".
13package ClamTk::History;
14
15# use strict;
16# use warnings;
17
18use File::Basename 'basename';
19use Locale::gettext;
20use Encode 'decode';
21# use Gtk3::Gdk::Keysyms;
22
23use Glib 'TRUE', 'FALSE';
24
25sub show_window {
26    my $box = Gtk3::Box->new( vertical, 5 );
27    $box->set_homogeneous( FALSE );
28
29    my $sort = 0;    # 0 = asc, 1 = desc
30
31    my $swin = Gtk3::ScrolledWindow->new( undef, undef );
32    $swin->set_policy( 'automatic', 'automatic' );
33    $swin->set_vexpand( TRUE );
34    $box->pack_start( $swin, TRUE, TRUE, 0 );
35
36    my $store = create_model();
37
38    my $view = Gtk3::TreeView->new_with_model( $store );
39    $view->set_rules_hint( TRUE );
40    my $column = Gtk3::TreeViewColumn->new_with_attributes(
41        _( 'History' ),
42        Gtk3::CellRendererText->new,
43        text => 0,
44    );
45    $column->set_sort_column_id( 0 );
46    $view->append_column( $column );
47    $swin->add( $view );
48
49    # Add delete signals
50    $box->signal_connect(
51        key_press_event => sub {
52            my ( $widget, $event ) = @_;
53            if ( $event->keyval == $Gtk3::Gdk::Delete ) {
54                del_history( undef, $view );
55            }
56            return TRUE;
57        }
58    );
59
60    # Jump to a row to make keyboard use easier
61    my $first_iter = $store->get_iter_first;
62    # Make sure they *have* a history
63    if ( $first_iter ) {
64        if ( $store->iter_is_valid( $first_iter ) ) {
65            my $viewpath = $store->get_path( $store->get_iter_first );
66            $view->set_cursor( $viewpath, $column, FALSE ) if ( $viewpath );
67        }
68    }
69
70    $box->pack_start( Gtk3::Separator->new( 'vertical' ), FALSE, FALSE, 0 );
71
72    my $viewbar = Gtk3::Toolbar->new;
73    $box->pack_start( $viewbar, FALSE, FALSE, 5 );
74    $viewbar->set_style( 'both-horiz' );
75
76    my $button = Gtk3::ToolButton->new();
77    # my $use_image = ClamTk::Icons->get_image('text-x-preview');
78    my $use_image = ClamTk::Icons->get_image( 'document-new' );
79    $button->set_icon_name( $use_image );
80    $button->set_label( _( 'View' ) );
81    $viewbar->insert( $button, -1 );
82    $button->set_is_important( TRUE );
83    $button->signal_connect( clicked => \&view_history, $view );
84
85    $button    = Gtk3::ToolButton->new();
86    $use_image = ClamTk::Icons->get_image( 'document-print' );
87    $button->set_icon_name( $use_image );
88    $button->set_label( _( 'Print' ) );
89    $viewbar->insert( $button, -1 );
90    $button->set_is_important( TRUE );
91    $button->signal_connect(
92        clicked => sub {
93            my $p = Gtk3::PrintOperation->new();
94        }
95    );
96
97    my $v_sep = Gtk3::SeparatorToolItem->new;
98    $v_sep->set_draw( FALSE );
99    $v_sep->set_expand( TRUE );
100    $viewbar->insert( $v_sep, -1 );
101
102    $button    = Gtk3::ToolButton->new();
103    $use_image = ClamTk::Icons->get_image( 'edit-delete' );
104    $button->set_icon_name( $use_image );
105    $button->set_label( _( 'Delete' ) );
106    $viewbar->insert( $button, -1 );
107    $button->set_is_important( TRUE );
108    $button->signal_connect( clicked => \&del_history, $view );
109
110    $box->show_all;
111    return $box;
112}
113
114sub history_sort {
115    my %orcish;
116    return
117        #<<<
118        sort {
119        ( $orcish{ $a } ||= -M $a )
120                <=> ( $orcish{ $b } ||= -M $b ) }
121                        @_;
122        #>>>
123}
124
125sub view_history {
126    my ( $button, $view ) = @_;
127    my $select = $view->get_selection;
128    return unless ( $select );
129
130    my ( $model, $iter ) = $select->get_selected;
131    return unless $iter;
132
133    my $basename = '';
134    $select->selected_foreach(
135        sub {
136            my ( $model, $path, $iter ) = @_;
137            $basename = $model->get( $iter, 0 );
138        }
139    );
140
141    #<<<
142    my $full_path
143        = ClamTk::App->get_path( 'history' )
144        . '/'
145        . $basename
146        . '.log';
147    #>>>
148
149    my $win = Gtk3::Dialog->new(
150        sprintf( _( 'Viewing %s' ), $basename ),
151        undef, [ qw| modal destroy-with-parent no-separator | ],
152    );
153    $win->signal_connect( destroy => sub { $win->destroy; 1 } );
154    $win->set_default_size( 800, 350 );
155
156    my $textview = Gtk3::TextView->new;
157    $textview->set( editable       => FALSE );
158    $textview->set( cursor_visible => FALSE );
159
160    my $text;
161    $text = do {
162        my $FILE;    # filehandle for histories log
163        #<<<
164        unless ( open( $FILE, '<:encoding(UTF-8)', $full_path ) ) {
165            my $notice
166                = sprintf _( 'Problems opening %s...' ), $full_path;
167                return;
168        }
169        #>>>
170        local $/ = undef;
171        <$FILE>;
172    };
173    #close( $FILE )
174    #    or warn sprintf( "Unable to close FILE %s! %s\n" ),
175    #    $full_path;
176
177    my $textbuffer = $textview->get_buffer;
178    # I hate setting a font here, but it makes the printf stuff
179    # look MUCH better.
180    $textbuffer->create_tag( 'mono', family => 'Monospace' );
181    $textbuffer->insert_with_tags_by_name( $textbuffer->get_start_iter,
182        $text, 'mono' );
183
184    my $scroll_win = Gtk3::ScrolledWindow->new( undef, undef );
185    $scroll_win->set_vexpand( TRUE );
186    $scroll_win->set_border_width( 5 );
187    $scroll_win->set_shadow_type( 'none' );
188    $scroll_win->set_policy( 'automatic', 'automatic' );
189
190    my $scrollbox = Gtk3::Box->new( 'vertical', 5 );
191    $scrollbox->set_homogeneous( FALSE );
192    $win->get_content_area->add( $scrollbox );
193
194    $scrollbox->pack_start( $scroll_win, TRUE, TRUE, 0 );
195    $scroll_win->add( $textview );
196
197    my $viewbar = Gtk3::Toolbar->new;
198    $scrollbox->pack_start( $viewbar, FALSE, FALSE, 0 );
199    $viewbar->set_style( 'both-horiz' );
200
201    my $v_sep = Gtk3::SeparatorToolItem->new;
202    $v_sep->set_draw( FALSE );
203    $v_sep->set_expand( TRUE );
204    $viewbar->insert( $v_sep, -1 );
205
206    my $close_btn = Gtk3::ToolButton->new();
207    my $use_image = ClamTk::Icons->get_image( 'window-close' );
208    $close_btn->set_icon_name( $use_image );
209    $close_btn->set_label( _( 'Close' ) );
210    $close_btn->set_is_important( TRUE );
211    $viewbar->insert( $close_btn, -1 );
212    $close_btn->signal_connect( clicked => sub { $win->destroy } );
213
214    $win->show_all();
215    $win->run;
216    $win->destroy;
217    return;
218}
219
220sub del_history {
221    my ( $button, $tree ) = @_;
222    my $sel = $tree->get_selection;
223
224    my ( $model, $iter ) = $sel->get_selected;
225    return unless $iter;
226
227    my $row        = $model->get( $iter, 0 );
228    my $first_iter = $model->get_iter_first;
229    # my $next_iter  = $model->iter_next($iter);
230    my $new_path = $model->get_path( $iter );
231
232    my $paths     = ClamTk::App->get_path( 'history' );
233    my $top_dir   = $paths . '/';
234    my $full_path = $top_dir . $row . '.log';
235    # $row = undef;
236    return FALSE unless ( -e $full_path );
237    unlink( $full_path ) or warn "couldn't delete $full_path: $!\n";
238
239    $model->remove( $iter );
240    if ( $model->iter_is_valid( $iter ) ) {
241        $sel->select_iter( $iter );
242    } else {
243        return;
244    }
245    # $sel->select_path( $new_path );
246    return TRUE;
247}
248
249sub create_model {
250    my $paths   = ClamTk::App->get_path( 'history' );
251    my @h_files = glob "$paths/*.log";
252    for ( @h_files ) {
253        $_ = decode( 'utf8', $_ );
254    }
255    if ( @h_files > 1 ) {
256        @h_files = history_sort( @h_files );
257    }
258
259    my $liststore = Gtk3::ListStore->new( 'Glib::String' );
260    for my $log ( @h_files ) {
261        my $iter     = $liststore->append;
262        my $basename = basename( $log );
263        $basename =~ s/(.*?)\.log/$1/;
264        $liststore->set( $iter, 0, $basename );
265    }
266    return $liststore;
267}
268
2691;
270