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