1 /* view_newest_files_op.cc
2 * This file belongs to Worker, a file manager for UN*X/X11.
3 * Copyright (C) 2016-2021 Ralf Hoffmann.
4 * You can contact me at: ralf@boomerangsworld.de
5 * or http://www.boomerangsworld.de/worker
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include "view_newest_files_op.hh"
23 #include "aguix/aguix.h"
24 #include "aguix/awindow.h"
25 #include "aguix/text.h"
26 #include "aguix/fieldlistview.h"
27 #include "aguix/button.h"
28 #include "aguix/textstorage.h"
29 #include "aguix/textview.h"
30 #include "listermode.h"
31 #include "nwc_fsentry.hh"
32 #include "nmspecialsourceext.hh"
33 #include "worker_locale.h"
34 #include "worker.h"
35 #include "get_files_thread.hh"
36 #include "nwc_path.hh"
37 #include "virtualdirmode.hh"
38 #include "wconfig.h"
39
40 int ViewNewestFilesOp::s_vdir_number = 1;
41
42 const char *ViewNewestFilesOp::name = "ViewNewestFilesOp";
43
ViewNewestFilesOp()44 ViewNewestFilesOp::ViewNewestFilesOp() : FunctionProto()
45 {
46 m_past_days = 0;
47 }
48
~ViewNewestFilesOp()49 ViewNewestFilesOp::~ViewNewestFilesOp()
50 {
51 }
52
duplicate() const53 ViewNewestFilesOp *ViewNewestFilesOp::duplicate() const
54 {
55 ViewNewestFilesOp *ta = new ViewNewestFilesOp();
56 return ta;
57 }
58
isName(const char * str)59 bool ViewNewestFilesOp::isName(const char *str)
60 {
61 if ( strcmp( str, name ) == 0 ) {
62 return true;
63 } else {
64 return false;
65 }
66 }
67
getName()68 const char *ViewNewestFilesOp::getName()
69 {
70 return name;
71 }
72
run(std::shared_ptr<WPUContext> wpu,ActionMessage * msg)73 int ViewNewestFilesOp::run( std::shared_ptr< WPUContext > wpu, ActionMessage *msg )
74 {
75 Lister *l1;
76 ListerMode *lm1;
77
78 m_aguix = msg->getWorker()->getAGUIX();
79
80 l1 = msg->getWorker()->getActiveLister();
81 if ( l1 == NULL ) {
82 return 1;
83 }
84
85 msg->getWorker()->setWaitCursor();
86
87 lm1 = l1->getActiveMode();
88
89 std::string dirname;
90
91 if ( lm1 ) {
92 dirname = lm1->getCurrentDirectory();
93 }
94
95 if ( dirname.empty() ) {
96 msg->getWorker()->unsetWaitCursor();
97 return 0;
98 }
99
100 std::unique_ptr< DirFilterSettings > dir_filter;
101 VirtualDirMode *vdm = dynamic_cast< VirtualDirMode *>( lm1 );
102 std::unique_ptr< NWC::Dir > use_dir;
103
104 if ( vdm ) {
105 dir_filter = vdm->getDirFilterSettings();
106 use_dir = vdm->getCurrentDir();
107 } else {
108 use_dir = std::make_unique< NWC::Dir >( dirname );
109 use_dir->readDir( false );
110 }
111
112 m_base_dir = dirname;
113 m_past_days = 0;
114
115 std::unique_ptr< GetFilesThread > sth( new GetFilesThread( std::move( use_dir ) ) );
116
117 time_t newest_mod_time = 0;
118 std::list< std::pair< std::string, time_t > > files;
119 time_t now = time( NULL );
120
121 sth->setVisitCB( [ &newest_mod_time,
122 &files,
123 &dir_filter,
124 &dirname,
125 &now ]( NWC::File &file )
126 {
127 if ( ! dir_filter ||
128 dir_filter->check( dirname, &file ) == true ) {
129
130 // ignore files newer than the current time
131 if ( file.stat_lastmod() <= now ) {
132 files.push_back( std::make_pair( file.getFullname(),
133 file.stat_lastmod() ) );
134
135 if ( file.stat_lastmod() > newest_mod_time ) {
136 newest_mod_time = file.stat_lastmod();
137 }
138 }
139 }
140 }
141 );
142 sth->setVisitEnterDirCB( [ &dir_filter,
143 &dirname ]( NWC::Dir &dir )
144 {
145 if ( ! dir_filter ||
146 dir_filter->check( dirname, &dir ) == true ) {
147 return true;
148 }
149
150 return false;
151 }
152 );
153
154 sth->start();
155
156 openWindow();
157
158 m_win->show();
159
160 AGMessage *agmsg;
161 int endmode = 0;
162
163 for ( ; endmode == 0 || endmode == 1; ) {
164 bool wait_some_time = false;
165
166 agmsg = NULL;
167
168 if ( sth && sth->running() ) {
169 agmsg = m_aguix->GetMessage( NULL );
170 wait_some_time = true;
171 } else {
172 if ( sth ) {
173 sth->join();
174 sth = NULL;
175
176 m_infotext->setText( catalog.getLocale( 1163 ) );
177
178 sortResults( files );
179
180 time_t l = calculateModLimit( newest_mod_time );
181
182 updateResults( files, l );
183 }
184
185 if ( endmode == 1 ) {
186 endmode = 2;
187 } else {
188 agmsg = m_aguix->WaitMessage( NULL );
189 }
190 }
191 if ( agmsg != NULL ) {
192 switch ( agmsg->type ) {
193 case AG_CLOSEWINDOW:
194 endmode = -1;
195 if ( sth ) {
196 sth->signalCancel();
197 }
198 break;
199 case AG_BUTTONCLICKED:
200 if ( agmsg->button.button == m_okb && endmode == 0 ) {
201 endmode = 1;
202
203 if ( sth ) {
204 m_okb->setText( 0, catalog.getLocale( 1165 ) );
205 m_okb->resize( m_okb->getMaximumWidth(),
206 m_okb->getHeight() );
207 }
208 } else if ( agmsg->button.button == m_closeb ) {
209 endmode = -1;
210 if ( sth ) {
211 sth->signalCancel();
212 }
213 } else if ( agmsg->button.button == m_more_days_b && endmode == 0 ) {
214 m_past_days++;
215 updateTimeWindowText();
216
217 if ( ! sth ) {
218 time_t l = calculateModLimit( newest_mod_time );
219
220 updateResults( files, l );
221 }
222 } else if ( agmsg->button.button == m_less_days_b && endmode == 0 ) {
223 if ( m_past_days > 0 ) m_past_days--;
224 updateTimeWindowText();
225
226 if ( ! sth ) {
227 time_t l = calculateModLimit( newest_mod_time );
228
229 updateResults( files, l );
230 }
231 }
232 break;
233 case AG_KEYPRESSED:
234 if ( agmsg->key.key == XK_Return && endmode == 0 ) {
235 endmode = 1;
236
237 if ( sth ) {
238 m_okb->setText( 0, catalog.getLocale( 1165 ) );
239 m_okb->resize( m_okb->getMaximumWidth(),
240 m_okb->getHeight() );
241 }
242 } else if ( agmsg->key.key == XK_Escape ) {
243 endmode = -1;
244 if ( sth ) {
245 sth->signalCancel();
246 }
247 } else if ( agmsg->key.key == XK_Up && endmode == 0 ) {
248 m_past_days++;
249 updateTimeWindowText();
250
251 if ( ! sth ) {
252 time_t l = calculateModLimit( newest_mod_time );
253
254 updateResults( files, l );
255 }
256 } else if ( agmsg->key.key == XK_Down && endmode == 0 ) {
257 if ( m_past_days > 0 ) m_past_days--;
258 updateTimeWindowText();
259
260 if ( ! sth ) {
261 time_t l = calculateModLimit( newest_mod_time );
262
263 updateResults( files, l );
264 }
265 }
266 break;
267 }
268 } else if ( wait_some_time ) {
269 waittime( 10 );
270 }
271
272 m_aguix->ReplyMessage( agmsg );
273 }
274
275 while ( sth && sth->running() ) {
276 waittime( 10 );
277 }
278 if ( sth ) {
279 sth->join();
280 sth = NULL;
281
282 // just to be consistent with the panelize output
283 sortResults( files );
284 }
285
286 if ( endmode > 0 ) {
287 panelizeResults( msg->getWorker(), files, newest_mod_time );
288 }
289
290 m_win.reset();
291
292 msg->getWorker()->unsetWaitCursor();
293 return 0;
294 }
295
getDescription()296 const char *ViewNewestFilesOp::getDescription()
297 {
298 return catalog.getLocale( 1306 );
299 }
300
openWindow()301 void ViewNewestFilesOp::openWindow()
302 {
303 m_win = std::unique_ptr<AWindow>( new AWindow( m_aguix,
304 0, 0,
305 500, 400,
306 getDescription() ) );
307 m_win->create();
308
309 AContainer *cont0 = m_win->setContainer( new AContainer( m_win.get(), 1, 5 ), true );
310 cont0->setMaxSpace( 5 );
311
312 RefCount<AFontWidth> lencalc( new AFontWidth( m_aguix, NULL ) );
313 m_help_ts = std::unique_ptr< TextStorageString >( new TextStorageString( catalog.getLocale( 1168 ), lencalc ) );
314 TextView *help_tv = cont0->addWidget( new TextView( m_aguix,
315 0, 0, 50, 80, "", *m_help_ts ),
316 0, 0, AContainer::CO_INCW );
317 help_tv->setLineWrap( true );
318 help_tv->maximizeYLines( 10, 500 );
319 help_tv->showFrame( false );
320 cont0->readLimits();
321 help_tv->show();
322 help_tv->setAcceptFocus( false );
323
324 TextView::ColorDef tv_cd = help_tv->getColors();
325 tv_cd.setBackground( 0 );
326 tv_cd.setTextColor( 1 );
327 help_tv->setColors( tv_cd );
328
329 m_infotext = cont0->addWidget( new Text( m_aguix, 0, 0, catalog.getLocale( 1164 ) ),
330 0, 1, AContainer::CO_INCW );
331
332 m_lv = dynamic_cast<FieldListView*>( cont0->add( new FieldListView( m_aguix, 0, 0,
333 500, 200, 0 ),
334 0, 2, AContainer::CO_MIN ) );
335 m_lv->setNrOfFields( 2 );
336 m_lv->setShowHeader( true );
337 m_lv->setFieldText( 0, catalog.getLocale( 176 ) );
338 m_lv->setFieldText( 1, catalog.getLocale( 163 ) );
339 m_lv->setHBarState( 2 );
340 m_lv->setVBarState( 2 );
341 m_lv->setGlobalFieldSpace( 5 );
342 m_lv->setDefaultColorMode( FieldListView::PRECOLOR_ONLYSELECT );
343
344 AContainer *cont1 = cont0->add( new AContainer( m_win.get(), 4, 1 ), 0, 3 );
345 cont1->setBorderWidth( 0 );
346 cont1->setMinSpace( 5 );
347 cont1->setMaxSpace( 5 );
348
349 cont1->add( new Text( m_aguix, 0, 0, catalog.getLocale( 1160 ) ),
350 0, 0, AContainer::CO_FIX );
351 m_time_window_text = cont1->addWidget( new Text( m_aguix, 0, 0, "" ),
352 1, 0, AContainer::CO_INCW );
353 m_more_days_b = dynamic_cast<Button*>( cont1->add( new Button( m_aguix, 0, 0, catalog.getLocale( 1161 ), 0 ),
354 2, 0, AContainer::CO_FIX ) );
355 m_less_days_b = dynamic_cast<Button*>( cont1->add( new Button( m_aguix, 0, 0, catalog.getLocale( 1162 ), 0 ),
356 3, 0, AContainer::CO_FIX ) );
357
358 m_more_days_b->setBubbleHelpText( catalog.getLocale( 1166 ) );
359 m_less_days_b->setBubbleHelpText( catalog.getLocale( 1167 ) );
360
361 AContainer *cont2 = cont0->add( new AContainer( m_win.get(), 2, 1 ), 0, 4 );
362 cont2->setBorderWidth( 0 );
363 cont2->setMinSpace( 5 );
364 cont2->setMaxSpace( -1 );
365
366 m_okb = dynamic_cast<Button*>( cont2->add( new Button( m_aguix, 0, 0,
367 catalog.getLocale( 1034 ),
368 0 ),
369 0, 0, AContainer::CO_FIX ) );
370 m_closeb = dynamic_cast<Button*>( cont2->add( new Button( m_aguix, 0, 0,
371 catalog.getLocale( 8 ),
372 0 ),
373 1, 0, AContainer::CO_FIX ) );
374
375 m_win->contMaximize( true );
376 m_win->setDoTabCycling( true );
377
378 updateTimeWindowText();
379 }
380
updateResults(const std::list<std::pair<std::string,time_t>> & files,time_t mod_limit)381 void ViewNewestFilesOp::updateResults( const std::list< std::pair< std::string, time_t > > &files,
382 time_t mod_limit )
383 {
384 m_lv->setSize( 0 );
385
386 for ( auto p: files ) {
387 if ( p.second >= mod_limit ) {
388 int row = m_lv->addRow();
389
390 std::string s = NWC::Path::get_extended_basename( m_base_dir, p.first );
391
392 m_lv->setText( row, 0, s );
393
394 s.clear();
395
396 struct tm *timeptr = localtime( &p.second );
397 wconfig->writeDateToString( s, timeptr );
398
399 m_lv->setText( row, 1, s );
400 }
401 }
402
403 m_lv->redraw();
404 m_aguix->Flush();
405 }
406
calculateModLimit(time_t newest_time)407 time_t ViewNewestFilesOp::calculateModLimit( time_t newest_time )
408 {
409 struct tm newest_date;
410
411 localtime_r( &newest_time, &newest_date );
412
413 newest_date.tm_sec = 0;
414 newest_date.tm_min = 0;
415 newest_date.tm_hour = 0;
416
417 newest_date.tm_mday -= m_past_days;
418
419 return mktime( &newest_date );
420 }
421
panelizeResults(Worker * w,const std::list<std::pair<std::string,time_t>> & files,time_t newest_time)422 void ViewNewestFilesOp::panelizeResults( Worker *w,
423 const std::list< std::pair< std::string, time_t > > &files,
424 time_t newest_time )
425 {
426 Lister *l1;
427
428 time_t l = calculateModLimit( newest_time );
429
430 l1 = w->getActiveLister();
431 if ( l1 != NULL ) {
432 l1->switch2Mode( 0 );
433
434 VirtualDirMode *vdm = dynamic_cast< VirtualDirMode* >( l1->getActiveMode() );
435 if ( vdm != NULL ) {
436 vdm->newTab();
437
438 std::string name = AGUIXUtils::formatStringToString( "viewnewest%d", s_vdir_number++ );
439
440 if ( s_vdir_number < 0 ) {
441 // avoid negative and zero number
442 s_vdir_number = 1;
443 }
444
445 std::unique_ptr< NWC::Dir > d( new NWC::VirtualDir( name ) );
446
447 const int rows = m_lv->getElements();
448 int row = 0;
449 bool some_selected = false;
450
451 for ( row = 0; row < rows; row++ ) {
452 if ( m_lv->getSelect( row ) ) {
453 some_selected = true;
454 break;
455 }
456 }
457
458 row = 0;
459 for ( auto p: files ) {
460 if ( p.second >= l ) {
461
462 if ( ! some_selected ||
463 m_lv->getSelect( row ) ) {
464 NWC::FSEntry fse( p.first );
465 if ( fse.entryExists() ) {
466 d->add( fse );
467 }
468 }
469 row++;
470 }
471 }
472
473 vdm->showDir( d );
474 }
475 }
476 }
477
updateTimeWindowText()478 void ViewNewestFilesOp::updateTimeWindowText()
479 {
480 if ( m_past_days == 0 ) {
481 m_time_window_text->setText( catalog.getLocale( 1159 ) );
482 } else if ( m_past_days == 1 ) {
483 std::string t = AGUIXUtils::formatStringToString( catalog.getLocale( 1107 ), 1 );
484 m_time_window_text->setText( t.c_str() );
485 } else {
486 std::string t = AGUIXUtils::formatStringToString( catalog.getLocale( 1108 ), m_past_days );
487 m_time_window_text->setText( t.c_str() );
488 }
489 }
490
sortResults(std::list<std::pair<std::string,time_t>> & files)491 void ViewNewestFilesOp::sortResults( std::list< std::pair< std::string, time_t > > &files )
492 {
493 files.sort( []( std::pair< std::string, time_t > &lhs,
494 std::pair< std::string, time_t > &rhs ) -> bool {
495 return lhs.first < rhs.first;
496 });
497 }
498