/* pathjumpui.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2012-2024 Ralf Hoffmann.
 * You can contact me at: ralf@boomerangsworld.de
 *   or http://www.boomerangsworld.de/worker
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "pathjumpui.hh"
#include "aguix/aguix.h"
#include "aguix/awindow.h"
#include "aguix/text.h"
#include "aguix/fieldlistview.h"
#include "aguix/button.h"
#include "aguix/solidbutton.h"
#include "aguix/util.h"
#include "aguix/choosebutton.h"
#include "aguix/cyclebutton.h"
#include "aguix/acontainerbb.h"
#include "aguix/kartei.h"
#include "aguix/karteibutton.h"
#include "aguix/stringgadget.h"
#include "aguix/textview.h"
#include "existence_test.hh"
#include "nwc_fsentry.hh"
#include "worker_locale.h"
#include "nwc_path.hh"
#include <algorithm>
#include "worker.h"
#include "deeppathstore.hh"
#include "bookmarkdbproxy.hh"
#include "persdeeppathstore.hh"
#include "prefixdb.hh"
#include "wconfig.h"
#include "pers_kvp.hh"
#include "async_job_limiter.hh"
#include <iostream>
#include <functional>
#include "worker_time.hh"

int PathJumpUI::s_pathjump_number = 1;

static time_t get_newest_time( const std::list< std::pair< std::string, time_t > > &paths )
{
    time_t v = 0;

    for ( auto &e : paths ) {
        if ( e.second > v ) {
            v = e.second;
        }
    }

    return v;
}

PathJumpUI::PathJumpUI( AGUIX &aguix, DeepPathStore &path_store, Worker &worker )
  : m_aguix( aguix ),
    m_worker( worker ),
    m_path_store( path_store ),
    m_current_depth( -1 ),
    m_show_hidden_entries( false ),
    m_entry_filter_mode( SHOW_ALL ),
    m_lv_dirty( false),
    m_byfilter_lv_dirty( false ),
    m_byprog_lv_dirty( false ),
    m_current_time( 0 ),
    m_ignore_date_for_sorting( false ),
    m_oldest_time( 0 )
{
    int max_path_components = 0;
    auto temp_prog_store = worker.getPathProgramsPers();
    loff_t store_size = worker.getSizeOfPersPaths();
    loff_t store_archive_size = worker.getSizeOfPersPathsArchive();
    loff_t prog_store_size = worker.getSizeOfPersPathPrograms();

    m_bookmarks = Worker::getBookmarkDBInstance().getNamesOfEntries( "" );

    auto best_hits = worker.getDirHistPrefixDB().getAllBestHits();
    loff_t prefix_size = worker.getDirHistPrefixDB().getLastSize();
    std::map< std::string, size_t > best_hits_pos;
    size_t pos = 0;

    for ( auto &e : best_hits ) {
        m_best_hits.push_back( { .filter = e.first,
                                 .path = std::get<0>( e.second ),
                                 .value = std::get<1>( e.second ),
                                 .visible = true,
                                 .last_access = std::get<2>( e.second ),
                                 .exists = { .exists = ExistenceTest::EXISTS_UNKNOWN,
                                             .path_length = 0 } } );
        best_hits_pos[ std::get<0>( e.second ) ] = pos++;
    }

    time_t now = time( NULL );

    m_current_time = now;

    std::sort( m_best_hits.begin(),
               m_best_hits.end(),
               []( const best_hit &lhs,
                   const best_hit &rhs ) {
                   if ( lhs.last_access > 0 && rhs.last_access == 0 ) {
                       return true;
                   } else if ( lhs.last_access == 0 && rhs.last_access > 0 ) {
                       return false;
                   } else if ( lhs.last_access > 0 && rhs.last_access > 0 ) {
                       return lhs.last_access > rhs.last_access;
                   } else {
                       return lhs.value > rhs.value;
                   }
               } );

    for ( auto &e : temp_prog_store ) {
        auto paths = e.second.getPaths();

        time_t last_access = get_newest_time( paths );

        m_used_programs.push_back( { .program = e.first,
                                     .last_access = last_access,
                                     .visible = true } );

        std::vector< program_path_entry > path_list;

        for ( auto &path : paths ) {
            path_list.push_back( { .path = path.first,
                                   .last_access = path.second,
                                   .exists = { .exists = ExistenceTest::EXISTS_UNKNOWN,
                                               .path_length = 0 } } );
        }

        std::sort( path_list.begin(),
                   path_list.end(),
                   []( auto &lhs, auto &rhs ) {
                       return lhs.last_access > rhs.last_access;
                   } );

        m_program_paths[ e.first ] = path_list;
    }

    m_used_programs.sort( []( auto &lhs, auto &rhs ) {
                              return lhs.last_access > rhs.last_access;
                          } );

    // without leading slash there could be this many components,
    // add one for the initial slash
    max_path_components++;

    m_win = std::unique_ptr<AWindow>( new AWindow( &m_aguix,
                                                   10, 10,
                                                   400, 400,
                                                   catalog.getLocale( 1301 ),
                                                   AWindow::AWINDOW_NORMAL ) );
    m_win->create();

    m_mainco = m_win->setContainer( new AContainer( m_win.get(), 1, 1 ), true );
    m_mainco->setMaxSpace( 5 );

    m_k1 = new Kartei( &m_aguix, 10, 10, 10, 10, "" );
    m_mainco->add( m_k1, 0, 0, AContainer::CO_MIN );
    m_k1->create();

    /*************
     * Main view *
     *************/
    m_subwin1 = new AWindow( &m_aguix, 0, 0, 10, 10, "" );
    m_k1->add( m_subwin1 );
    m_subwin1->create();

    m_co1 = m_subwin1->setContainer( new AContainer( m_subwin1, 1, 6 ), true );
    m_co1->setMaxSpace( 5 );

    AContainer *co1_2 = m_co1->add( new AContainerBB( m_subwin1, 1, 3 ), 0, 0 );
    co1_2->setMaxSpace( 5 );

    AContainer *co1_2_1 = co1_2->add( new AContainer( m_subwin1, 2, 1 ), 0, 0 );
    co1_2_1->setMaxSpace( 5 );
    co1_2_1->setBorderWidth( 0 );
    co1_2_1->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 758 ) ),
                  0, 0, AContainer::CO_FIX );
    m_infixtext = co1_2_1->addWidget( new Text( &m_aguix, 0, 0, "" ),
                                      1, 0, AContainer::CO_INCW );


    m_hold_co = co1_2->add( new AContainer( m_subwin1, 4, 1 ), 0, 1 );
    m_hold_co->setMaxSpace( 5 );
    m_hold_co->setBorderWidth( 0 );
    m_hold_co->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 1029 ) ),
                0, 0, AContainer::CO_FIX );
    m_hold_status_b = m_hold_co->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1027 ), 0 ),
                                            1, 0, AContainer::CO_FIX );
    m_hold_co->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 1030 ) ),
                2, 0, AContainer::CO_FIX );
    m_hold_filter_text = m_hold_co->addWidget( new Text( &m_aguix, 0, 0, "" ),
                                           3, 0, AContainer::CO_INCW );

    m_lock_dir_co = co1_2->add( new AContainer( m_subwin1, 4, 1 ), 0, 2 );
    m_lock_dir_co->setMaxSpace( 5 );
    m_lock_dir_co->setBorderWidth( 0 );
    m_lock_dir_co->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 1436 ) ),
                        0, 0, AContainer::CO_FIX );
    m_lock_dir_status_b = m_lock_dir_co->addWidget( new Button( &m_aguix, 0, 0,
                                                                is_locked_on_directory() ? catalog.getLocale( 1437 ) : catalog.getLocale( 1027 ),
                                                                0 ),
                                                    1, 0, AContainer::CO_FIX );
    m_lock_dir_co->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 1438 ) ),
                        2, 0, AContainer::CO_FIX );
    m_lock_dir_text = m_lock_dir_co->addWidget( new Text( &m_aguix, 0, 0, m_lock_base_dir.c_str() ),
                                                3, 0, AContainer::CO_INCW );

    AContainer *co1_4 = m_co1->add( new AContainerBB( m_subwin1, 1, 2 ), 0, 1 );
    co1_4->setMaxSpace( 5 );
    AContainer *co1_4_1 = co1_4->add( new AContainer( m_subwin1, 2, 1 ), 0, 0 );
    co1_4_1->setMaxSpace( 5 );
    co1_4_1->setBorderWidth( 0 );
    co1_4_1->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 980 ) ),
                  0, 0, AContainer::CO_FIX );
    m_filter_mode_cyb = co1_4_1->addWidget( new CycleButton( &m_aguix, 0, 0, 50, 0 ),
                                            1, 0, AContainer::CO_INCWNR );

    m_filter_mode_cyb->addOption( catalog.getLocale( 981 ) );
    m_filter_mode_cyb->addOption( catalog.getLocale( 982 ) );
    m_filter_mode_cyb->addOption( catalog.getLocale( 983 ) );

    m_filter_mode_cyb->resize( m_filter_mode_cyb->getMaxSize(),
                               m_filter_mode_cyb->getHeight() );
    co1_4_1->readLimits();

    AContainer *co1_op = co1_4->add( new AContainer( m_subwin1, 4, 1 ), 0, 1 );
    co1_op->setMaxSpace( 5 );
    co1_op->setBorderWidth( 0 );

    m_show_all_cb = (ChooseButton*)co1_op->add( new ChooseButton( &m_aguix, 0, 0,
                                                                  m_show_hidden_entries,
                                                                  catalog.getLocale( 977 ),
                                                                  LABEL_RIGHT, 0 ),
                                                0, 0, AContainer::CO_FIXNR );

    m_ignore_date_cb = (ChooseButton*)co1_op->add( new ChooseButton( &m_aguix, 0, 0,
                                                                     m_ignore_date_for_sorting,
                                                                     catalog.getLocale( 1111 ),
                                                                     LABEL_RIGHT, 0 ),
                                                   1, 0, AContainer::CO_INCWNR );

    m_show_relative_paths_cb = co1_op->addWidget( new ChooseButton( &m_aguix, 0, 0,
                                                                    m_relative_view_mode,
                                                                    catalog.getLocale( 1386 ),
                                                                    LABEL_RIGHT, 0 ),
                                                  2, 0, AContainer::CO_INCWNR );

    m_include_archive_cb = co1_op->addWidget( new ChooseButton( &m_aguix, 0, 0,
                                                                m_include_archive,
                                                                catalog.getLocale( 1434 ),
                                                                LABEL_RIGHT, 0 ),
                                              3, 0, AContainer::CO_FIXNR );


    AContainer *co_age = m_co1->add( new AContainerBB( m_subwin1, 7, 1 ), 0, 2 );
    co_age->setMaxSpace( 5 );

    co_age->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 1154 ) ),
                 0, 0, AContainer::CO_FIX );
    m_age_text = co_age->addWidget( new Text( &m_aguix, 0, 0, "----" ),
                                    1, 0, AContainer::CO_FIX );

    {
        int textw = 0;
        for ( int k = 1105; k <= 1110; k++ ) {
            int tw = m_aguix.getTextWidth( catalog.getLocale( k ) );
            if ( tw > textw ) {
                textw = tw;
            }
        }
        co_age->setMinWidth( textw, 1, 0 );
        co_age->setMaxWidth( textw, 1, 0 );
    }
    
    m_plus1h_b = co_age->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1155 ), 0 ),
                                    2, 0, AContainer::CO_FIX );
    m_plus1d_b = co_age->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1156 ), 0 ),
                                    3, 0, AContainer::CO_FIX );
    m_plus1w_b = co_age->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1157 ), 0 ),
                                    4, 0, AContainer::CO_FIX );
    m_age_reset_b = co_age->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1158 ), 0 ),
                                       5, 0, AContainer::CO_FIX );

    m_breadcrumb_co = m_co1->add( new AContainer( m_subwin1, max_path_components + 1, 1 ), 0, 3 );
    m_breadcrumb_co->setMaxSpace( 5 );

    for ( int i = 0; i < max_path_components; i++ ) {
        Button *b = (Button*)m_breadcrumb_co->add( new Button( &m_aguix,
                                                               0,
                                                               0,
                                                               "",
                                                               0 ), i, 0, AContainer::CO_INCW );
        m_breadcrumb_buttons.push_back( b );
        b->setAcceptFocus( false );
        b->hide();
    }

    AContainer *co1_5 = m_co1->add( new AContainer( m_subwin1, 2, 1 ), 0, 4 );
    co1_5->setMaxSpace( 5 );
    co1_5->setBorderWidth( 0 );

    m_lv = co1_5->addWidget( new FieldListView( &m_aguix, 
                                                0, 0, 
                                                400, 300, 0 ),
                             0, 0, AContainer::CO_MIN );

    m_lv->setNrOfFields( 4 );
    m_lv->setShowHeader( true );
    m_lv->setFieldText( 1, catalog.getLocale( 1112 ) );
    m_lv->setFieldText( 2, catalog.getLocale( 809 ) );
    m_lv->setFieldText( 3, catalog.getLocale( 809 ) );
    m_lv->setHBarState( 2 );
    m_lv->setVBarState( 2 );
    m_lv->setAcceptFocus( true );
    m_lv->setDisplayFocus( true );
    m_lv->setFieldText( 0, catalog.getLocale( 978 ) );
    m_lv->setGlobalFieldSpace( 5 );
    m_lv->setFieldSpace( 2, 0 );
    m_lv->setFieldSpace( 3, 0 );
    m_lv->setFieldTextMerged( 2, true );
    m_lv->setFieldWidth( 2, m_aguix.getTextWidth( "?" ) );

    m_lv->setOnDemandCallback( [this]( FieldListView *lv,
                                       int row,
                                       int field,
                                       struct FieldListView::on_demand_data &data )
                               {
                                   updateLVData( row, field, data );
                               } );

    m_class_counts_lv = co1_5->addWidget( new FieldListView( &m_aguix, 
                                                             0, 0, 
                                                             100, 100, 0 ),
                                          1, 0, AContainer::CO_INCH );
    m_class_counts_lv->setNrOfFields( 3 );
    m_class_counts_lv->setShowHeader( true );
    m_class_counts_lv->setFieldText( 0, catalog.getLocale( 978 ) );
    m_class_counts_lv->setFieldText( 1, catalog.getLocale( 1031 ) );
    m_class_counts_lv->setFieldText( 2, catalog.getLocale( 1032 ) );

    m_class_counts_lv->setHBarState( 2 );
    m_class_counts_lv->setVBarState( 2 );
    m_class_counts_lv->setAcceptFocus( true );
    m_class_counts_lv->setDisplayFocus( true );
    m_class_counts_lv->setGlobalFieldSpace( 5 );
    m_class_counts_lv->setConsiderHeaderWidthForDynamicWidth( true );

    m_class_counts_lv->maximizeX();

    co1_5->readLimits();

    AContainer *co1_3 = m_co1->add( new AContainer( m_subwin1, 6, 1 ), 0, 5 );
    co1_3->setMinSpace( 5 );
    co1_3->setMaxSpace( -1 );
    co1_3->setBorderWidth( 0 );
    m_okb = (Button*)co1_3->add( new Button( &m_aguix,
                                             0,
                                             0,
                                             catalog.getLocale( 761 ),
                                             catalog.getLocale( 1551 ),
                                             0 ), 0, 0, AContainer::CO_FIX );
    m_okb->setBubbleHelpText( catalog.getLocale( 1552 ) );
    m_panelize_b = co1_3->addWidget( new Button( &m_aguix,
                                                 0,
                                                 0,
                                                 catalog.getLocale( 1034 ),
                                                 0 ), 1, 0, AContainer::CO_FIX );

    m_hide_non_existing_cb = co1_3->addWidget( new ChooseButton( &m_aguix, 0, 0,
                                                                 m_hide_non_existing,
                                                                 catalog.getLocale( 1408 ),
                                                                 LABEL_RIGHT, 0 ),
                                               2, 0, AContainer::CO_FIXNR );

    std::string cleanupstr = AGUIXUtils::formatStringToString( "%s (%s)",
                                                               catalog.getLocale( 984 ),
                                                               AGUIXUtils::bytes_to_human_readable_f( store_size ).c_str() );
    std::string cleanuparchivestr = AGUIXUtils::formatStringToString( "%s (%s)",
                                                                      catalog.getLocale( 1430 ),
                                                                      AGUIXUtils::bytes_to_human_readable_f( store_archive_size ).c_str() );
    m_cleanupb = (Button*)co1_3->add( new Button( &m_aguix,
                                                  0,
                                                  0,
                                                  cleanupstr.c_str(),
                                                  0 ), 3, 0, AContainer::CO_FIX );
    m_cleanup_archiveb = (Button*)co1_3->add( new Button( &m_aguix,
                                                          0,
                                                          0,
                                                          cleanuparchivestr.c_str(),
                                                          0 ), 4, 0, AContainer::CO_FIX );
    m_cancelb = (Button*)co1_3->add( new Button( &m_aguix,
                                                 0,
                                                 0,
                                                 catalog.getLocale( 633 ),
                                                 0 ), 5, 0, AContainer::CO_FIX );

    /******************
     * By-filter view *
     ******************/
    AWindow *subwin2 = new AWindow( &m_aguix, 0, 0, 10, 10, "" );
    m_k1->add( subwin2 );
    subwin2->create();

    auto sw1_co1 = subwin2->setContainer( new AContainer( subwin2, 1, 3 ), true );
    sw1_co1->setMaxSpace( 5 );

    AContainer *sw1_co1_2 = sw1_co1->add( new AContainer( subwin2, 3, 1 ), 0, 0 );
    sw1_co1_2->setMaxSpace( 5 );
    sw1_co1_2->setBorderWidth( 0 );
    sw1_co1_2->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 793 ) ),
                    0, 0, AContainer::CO_FIX );
    m_byfilter_filter_t = sw1_co1_2->addWidget( new Text( &m_aguix, 0, 0, "" ),
                                                1, 0, AContainer::CO_INCW );
    std::string msg;

    if ( wconfig->getPathJumpAllowDirs().empty() ) {
        msg = catalog.getLocale( 1221 );
    }

    sw1_co1_2->add( new Text( &m_aguix, 0, 0, msg.c_str() ),
                    2, 0, AContainer::CO_FIX );

    m_byfilter_lv = sw1_co1->addWidget( new FieldListView( &m_aguix, 
                                                           0, 0, 
                                                           400, 300, 0 ),
                                        0, 1, AContainer::CO_MIN );

    m_byfilter_lv->setNrOfFields( 4 );
    m_byfilter_lv->setShowHeader( true );
    m_byfilter_lv->setFieldText( 0, catalog.getLocale( 159 ) );
    m_byfilter_lv->setFieldText( 1, catalog.getLocale( 1112 ) );
    m_byfilter_lv->setFieldText( 2, catalog.getLocale( 809 ) );
    m_byfilter_lv->setHBarState( 2 );
    m_byfilter_lv->setVBarState( 2 );
    m_byfilter_lv->setAcceptFocus( true );
    m_byfilter_lv->setDisplayFocus( true );
    m_byfilter_lv->setGlobalFieldSpace( 5 );
    m_byfilter_lv->setFieldSpace( 2, 0 );
    m_byfilter_lv->setFieldSpace( 3, 0 );
    m_byfilter_lv->setFieldTextMerged( 2, true );
    m_byfilter_lv->setFieldWidth( 2, m_aguix.getTextWidth( "?" ) );

    m_byfilter_lv->setOnDemandCallback( [this]( FieldListView *lv,
                                                int row,
                                                int field,
                                                struct FieldListView::on_demand_data &data )
                                        {
                                            updateFilterLVData( row, field, data );
                                        } );


    AContainer *sw1_co1_3 = sw1_co1->add( new AContainer( subwin2, 4, 1 ), 0, 2 );
    sw1_co1_3->setMinSpace( 5 );
    sw1_co1_3->setMaxSpace( -1 );
    sw1_co1_3->setBorderWidth( 0 );

    m_byfilter_jump_b = sw1_co1_3->addWidget( new Button( &m_aguix,
                                                          0,
                                                          0,
                                                          catalog.getLocale( 761 ),
                                                          0 ), 0, 0, AContainer::CO_FIX );
    std::string cleanupstr2 = AGUIXUtils::formatStringToString( "%s (%s)",
                                                                catalog.getLocale( 1235 ),
                                                                AGUIXUtils::bytes_to_human_readable_f( prefix_size ).c_str() );
    m_prefixcleanupb = sw1_co1_3->addWidget( new Button( &m_aguix,
                                                         0,
                                                         0,
                                                         cleanupstr2.c_str(),
                                                         0 ), 2, 0, AContainer::CO_FIX );
    m_byfilter_close_b = sw1_co1_3->addWidget( new Button( &m_aguix,
                                                           0,
                                                           0,
                                                           catalog.getLocale( 633 ),
                                                           0 ), 3, 0, AContainer::CO_FIX );

    /****************************
     * setup window per program *
     ****************************/

    AWindow *subwin3 = new AWindow( &m_aguix, 0, 0, 10, 10, "" );
    m_k1->add( subwin3 );
    subwin3->create();

    auto co3 = subwin3->setContainer( new AContainer( subwin3, 1, 3 ), true );
    co3->setMaxSpace( 5 );

    AContainer *co3_1 = co3->add( new AContainer( subwin3, 3, 1 ), 0, 0 );
    co3_1->setMaxSpace( 5 );
    co3_1->setBorderWidth( 0 );
    co3_1->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 793 ) ),
                0, 0, AContainer::CO_FIX );
    m_byprog_filter_t = co3_1->addWidget( new Text( &m_aguix, 0, 0, "" ),
                                          1, 0, AContainer::CO_INCW );

    msg = "";
    if ( wconfig->getPathJumpAllowDirs().empty() ) {
        msg = catalog.getLocale( 1221 );
    }

    co3_1->add( new Text( &m_aguix, 0, 0, msg.c_str() ),
                2, 0, AContainer::CO_FIX );

    AContainer *co3_5 = co3->add( new AContainer( subwin3, 2, 1 ), 0, 1 );
    co3_5->setMaxSpace( 5 );
    co3_5->setBorderWidth( 0 );

    m_lv_progs = co3_5->addWidget( new FieldListView( &m_aguix, 
                                                      0, 0, 
                                                      100, 300, 0 ),
                                   0, 0, AContainer::CO_MIN );

    m_lv_progs->setNrOfFields( 2 );
    m_lv_progs->setShowHeader( true );
    m_lv_progs->setFieldText( 0, catalog.getLocale( 1325 ) );
    m_lv_progs->setFieldText( 1, catalog.getLocale( 1112 ) );
    m_lv_progs->setHBarState( 2 );
    m_lv_progs->setVBarState( 2 );
    m_lv_progs->setAcceptFocus( true );
    m_lv_progs->setDisplayFocus( true );
    m_lv_progs->setGlobalFieldSpace( 5 );

    m_lv_prog_files = co3_5->addWidget( new FieldListView( &m_aguix, 
                                                           0, 0, 
                                                           400, 100, 0 ),
                                        1, 0, AContainer::CO_MIN );
    m_lv_prog_files->setNrOfFields( 3 );
    m_lv_prog_files->setShowHeader( true );
    m_lv_prog_files->setFieldText( 0, catalog.getLocale( 809 ) );
    m_lv_prog_files->setFieldText( 1, catalog.getLocale( 809 ) );
    m_lv_prog_files->setFieldText( 2, catalog.getLocale( 1112 ) );

    m_lv_prog_files->setHBarState( 2 );
    m_lv_prog_files->setVBarState( 2 );
    m_lv_prog_files->setAcceptFocus( true );
    m_lv_prog_files->setDisplayFocus( true );
    m_lv_prog_files->setGlobalFieldSpace( 5 );
    m_lv_prog_files->setFieldSpace( 0, 0 );
    m_lv_prog_files->setFieldSpace( 1, 0 );
    m_lv_prog_files->setFieldTextMerged( 0, true );
    m_lv_prog_files->setFieldWidth( 0, m_aguix.getTextWidth( "?" ) );

    m_lv_prog_files->setOnDemandCallback( [this]( FieldListView *lv,
                                                  int row,
                                                  int field,
                                                  struct FieldListView::on_demand_data &data )
                                          {
                                              updateProgFileLVData( row, field, data );
                                          } );

    m_lv_prog_files->maximizeX();

    AContainer *co3_3 = co3->add( new AContainer( subwin3, 5, 1 ), 0, 2 );
    co3_3->setMinSpace( 5 );
    co3_3->setMaxSpace( -1 );
    co3_3->setBorderWidth( 0 );

    m_byprog_jump_b = co3_3->addWidget( new Button( &m_aguix,
                                                    0,
                                                    0,
                                                    catalog.getLocale( 761 ),
                                                    0 ), 0, 0, AContainer::CO_FIX );

    m_byprog_hide_non_existing_cb = co3_3->addWidget( new ChooseButton( &m_aguix, 0, 0,
                                                                        m_hide_non_existing,
                                                                        catalog.getLocale( 1408 ),
                                                                        LABEL_RIGHT, 0 ),
                                                      2, 0, AContainer::CO_FIXNR );


    std::string cleanupstr3 = AGUIXUtils::formatStringToString( "%s (%s)",
                                                                catalog.getLocale( 1324 ),
                                                                AGUIXUtils::bytes_to_human_readable_f( prog_store_size ).c_str() );
    m_byprog_clean_b = co3_3->addWidget( new Button( &m_aguix,
                                                     0,
                                                     0,
                                                     cleanupstr3.c_str(),
                                                     0 ), 3, 0, AContainer::CO_FIX );
    m_byprog_close_b = co3_3->addWidget( new Button( &m_aguix,
                                                     0,
                                                     0,
                                                     catalog.getLocale( 633 ),
                                                     0 ), 4, 0, AContainer::CO_FIX );

    co3_5->readLimits();

    /*********************
     * setup main window *
     *********************/

    m_k1->setOption( m_subwin1, 0, catalog.getLocale( 1222 ) );
    m_k1->setOption( subwin2, 1, catalog.getLocale( 1223 ) );
    m_k1->setOption( subwin3, 2, catalog.getLocale( 1326 ) );
    m_k1->maximize();
    m_k1->contMaximize();
    m_k1->show();

    m_mainco->readLimits();

    m_win->contMaximize( true );
    m_win->setDoTabCycling( true );

    m_entry_filter_age = 0;

    if ( m_entry_filter_mode == SHOW_ALL ) {
        m_show_relative_paths_cb->hide();
    }
}

PathJumpUI::~PathJumpUI()
{
}

void PathJumpUI::checkExistResults()
{
    if ( ! m_apply_state.done ||
         ( m_existence_tests.empty() &&
           m_existence_tests_prog_path.empty() &&
           m_existence_tests_filter_path.empty() ) ) {
        return;
    }

    bool update_lv = false;
    bool update_lv_prog_files = false;
    bool update_lv_filter = false;
    bool update_match_classes_lv = false;
    bool update_breadcrumb_state = false;

    for ( auto it = m_existence_tests.begin();
          it != m_existence_tests.end();
          ) {
        std::future_status status = std::get<0>( *it ).wait_for(std::chrono::duration<int>::zero());

        if ( status == std::future_status::ready ) {
            ExistenceTest::existence_state_t res = std::get<0>( *it ).get();

            if ( std::get<2>( *it ) == true ) {
                int row = std::get<1>( *it );

                try {
                    auto &entry = m_entries.at( std::get<3>( m_entry_fullnames.at( row ) ) );
                    entry.m_exists = res;

                    if ( std::get<3>( m_entry_fullnames.at( row ) ) == m_current_entry_pos ) {
                        update_breadcrumb_state = true;
                    }

                    if ( res.exists == ExistenceTest::EXISTS_YES ) {
                        std::get<2>( m_entry_fullnames.at( row ) ).exists = ExistenceTest::EXISTS_YES;
                        m_lv->clearOnDemandDataAvailableFlag( row );
                    } else {
                        if ( m_hide_non_existing ) {
                            bool restore_active_row = false;

                            if ( row == m_lv->getActiveRow() ) {
                                restore_active_row = true;
                            }

                            m_lv->deleteRow( row ) ;

                            // first adjust row in other pending tests
                            for ( auto it2 = m_existence_tests.begin();
                                  it2 != m_existence_tests.end();
                                  it2++ ) {
                                if ( std::get<1>( *it2 ) > row ) {
                                    std::get<1>( *it2 )--;
                                }
                            }

                            // now correct the match count
                            for ( auto &m : m_match_classes ) {
                                if ( m.first == entry.m_blockcount ) {
                                    m.second--;
                                }
                            }

                            // remove row from full name list
                            auto e = m_entry_fullnames.begin() + row;
                            m_entry_fullnames.erase( e );

                            // and finally restore active row
                            if ( restore_active_row ) {
                                m_lv->setActiveRow( row );
                                handleMainLVRowChange( m_lv->getActiveRow() );

                                update_breadcrumb_state = false;
                            }

                            update_match_classes_lv = true;
                        } else {
                            std::get<2>( m_entry_fullnames.at( row ) ).exists = ExistenceTest::EXISTS_NO;
                            std::get<2>( m_entry_fullnames.at( row ) ).path_length = res.path_length;

                            m_lv->clearOnDemandDataAvailableFlag( row );
                        }
                    }

                    update_lv = true;
                } catch (...) {
                }
            }

            auto next_it = it;
            next_it++;

            m_existence_tests.erase( it );

            it = next_it;
        } else {
            it++;
        }
    }

    int prog_row = m_lv_progs->getActiveRow();

    if ( m_lv_progs->isValidRow( prog_row ) ) {
        auto current_program = m_lv_progs->getText( prog_row, 0 );

        if ( m_program_paths.count( current_program ) > 0 ) {
            auto &path_list = m_program_paths[ current_program ];

            for ( auto it = m_existence_tests_prog_path.begin();
                  it != m_existence_tests_prog_path.end();
                  ) {
                std::future_status status = std::get<0>( *it ).wait_for(std::chrono::duration<int>::zero());

                if ( status == std::future_status::ready ) {
                    ExistenceTest::existence_state_t res = std::get<0>( *it ).get();

                    if ( std::get<2>( *it ) == true ) {
                        int lvrow = std::get<1>( *it );
                        int path_list_pos = m_lv_prog_files->getData( lvrow, -1 );

                        if ( path_list_pos >= 0 &&
                             path_list_pos < (int)path_list.size() ) {

                            try {
                                if ( res.exists == ExistenceTest::EXISTS_YES ) {
                                    path_list.at( path_list_pos ).exists.exists = ExistenceTest::EXISTS_YES;
                                    m_lv_prog_files->clearOnDemandDataAvailableFlag( lvrow );
                                } else {
                                    path_list.at( path_list_pos ).exists.exists = ExistenceTest::EXISTS_NO;
                                    path_list.at( path_list_pos ).exists.path_length = res.path_length;

                                    if ( m_hide_non_existing ) {
                                        bool restore_active_row = false;

                                        if ( lvrow == m_lv_prog_files->getActiveRow() ) {
                                            restore_active_row = true;
                                        }

                                        // first adjust row in other pending tests
                                        for ( auto it2 = m_existence_tests_prog_path.begin();
                                              it2 != m_existence_tests_prog_path.end();
                                              it2++ ) {
                                            if ( std::get<1>( *it2 ) > lvrow ) {
                                                std::get<1>( *it2 )--;
                                            }
                                        }

                                        // and finally restore active row
                                        if ( restore_active_row ) {
                                            m_lv_prog_files->setActiveRow( lvrow );
                                        }
                                    } else {
                                        m_lv_prog_files->clearOnDemandDataAvailableFlag( lvrow );
                                    }
                                }

                                update_lv_prog_files = true;
                            } catch (...) {
                            }
                        }
                    }

                    auto next_it = it;
                    next_it++;

                    m_existence_tests_prog_path.erase( it );

                    it = next_it;
                } else {
                    it++;
                }
            }
        }
    }

    for ( auto it = m_existence_tests_filter_path.begin();
          it != m_existence_tests_filter_path.end();
          ) {
        std::future_status status = std::get<0>( *it ).wait_for(std::chrono::duration<int>::zero());

        if ( status == std::future_status::ready ) {
            ExistenceTest::existence_state_t res = std::get<0>( *it ).get();

            if ( std::get<2>( *it ) == true ) {
                int row = std::get<1>( *it );

                try {
                    auto &e = m_best_hits.at( m_byfilter_row_to_element.at( row ) );

                    if ( res.exists == ExistenceTest::EXISTS_YES ) {
                        e.exists.exists = ExistenceTest::EXISTS_YES;
                    } else {
                        e.exists.exists = ExistenceTest::EXISTS_NO;
                        e.exists.path_length = res.path_length;
                    }

                    m_byfilter_lv->clearOnDemandDataAvailableFlag( row );
                    update_lv_filter = true;
                } catch (...) {
                }
            }

            auto next_it = it;
            next_it++;

            m_existence_tests_filter_path.erase( it );

            it = next_it;
        } else {
            it++;
        }
    }

    if ( update_lv ) {
        m_lv->redraw();
    }

    if ( update_lv_prog_files ) {
        m_lv_prog_files->redraw();
    }

    if ( update_lv_filter ) {
        m_byfilter_lv->redraw();
    }

    if ( update_match_classes_lv ) {
        buildMatchClassesLV();
    }

    if ( update_breadcrumb_state ) {
        build_current_path_components();
        update_breadcrumb();
    }
}

int PathJumpUI::mainLoop( initial_tab_t show_tab )
{
    maximizeWin();

    int tw = Worker::getPersKVPStore().getIntValue( "pathjumpwin-width",
                                                    m_win->getWidth(),
                                                    10000 );
    int th = Worker::getPersKVPStore().getIntValue( "pathjumpwin-height",
                                                    m_win->getHeight(),
                                                    10000 );
    m_win->resize( tw, th );

    m_win->show();

    m_lv->takeFocus();

    switch ( show_tab ) {
        case SHOW_BY_FILTER:
            m_k1->optionChange( 1 );
            m_byfilter_lv->takeFocus();
            break;
        case SHOW_BY_PROGRAM:
            m_k1->optionChange( 2 );
            m_lv_progs->takeFocus();
            break;
        default:
            break;
    }

    update_entries();

    m_lv_dirty = true;
    m_byfilter_lv_dirty = true;
    m_byprog_lv_dirty = true;

    apply_filter( m_filter, NULL );

    // do run2completion for the first call to have everything
    // initialized
    while ( ! m_apply_state.done ) {
        apply_filter_next();
    }

    highlight_best_hit( m_dirname );

    showData();

    AGMessage *msg;
    enum pathjump_endmode {
        RUNNING = 0,
        JUMP_CURRENT_PATH,
        JUMP_FULL_PATH,
        PANELIZE,
        CLOSE
    } endmode = RUNNING;
    int persist_width = -1;
    int persist_height = -1;

    for ( ; endmode == RUNNING; ) {
        if ( m_apply_state.done &&
             m_existence_tests.empty() &&
             m_existence_tests_prog_path.empty() &&
             m_existence_tests_filter_path.empty() ) {
            msg = m_aguix.WaitMessage( NULL );
        } else {
            msg = m_aguix.GetMessage( NULL );
        }

        checkExistResults();

        if ( msg != NULL ) {
            switch ( msg->type ) {
              case AG_CLOSEWINDOW:
                  endmode = CLOSE;
                  break;
              case AG_BUTTONCLICKED:
                  if ( msg->button.button == m_okb ) {
                      if ( msg->button.state == 2 ) {
                          endmode = JUMP_FULL_PATH;
                      } else {
                          endmode = JUMP_CURRENT_PATH;
                      }
                  } else if ( msg->button.button == m_byfilter_jump_b ) {
                      endmode = JUMP_CURRENT_PATH;
                  } else if ( msg->button.button == m_byprog_jump_b ) {
                      endmode = JUMP_CURRENT_PATH;
                  } else if ( msg->button.button == m_cleanupb ) {
                      if ( storeCleanUp() == 0 ) {
                          endmode = CLOSE;
                      }
                  } else if ( msg->button.button == m_cleanup_archiveb ) {
                      if ( storeCleanUpArchive() == 0 ) {
                          endmode = CLOSE;
                      }
                  } else if ( msg->button.button == m_prefixcleanupb ) {
                      if ( prefixDBCleanUp() == 0 ) {
                          endmode = CLOSE;
                      }
                  } else if ( msg->button.button == m_byprog_clean_b ) {
                      if ( programDBCleanUp() == 0 ) {
                          endmode = CLOSE;
                      }
                  } else if ( msg->button.button == m_panelize_b ) {
                      endmode = PANELIZE;
                  } else if ( msg->button.button == m_cancelb ) {
                      endmode = CLOSE;
                  } else if ( msg->button.button == m_byfilter_close_b ) {
                      endmode = CLOSE;
                  } else if ( msg->button.button == m_byprog_close_b ) {
                      endmode = CLOSE;
                  } else if ( msg->button.button == m_plus1h_b ) {
                      m_entry_filter_age += 1 * 60 * 60;

                      apply_filter( m_filter, [this]( int matches ) {
                              showData();
                              updateAgeText();
                          });
                  } else if ( msg->button.button == m_plus1d_b ) {
                      m_entry_filter_age += 1 * 24 * 60 * 60;

                      apply_filter( m_filter, [this]( int matches ) {
                              showData();
                              updateAgeText();
                          });
                  } else if ( msg->button.button == m_plus1w_b ) {
                      m_entry_filter_age += 7 * 24 * 60 * 60;

                      apply_filter( m_filter, [this]( int matches ) {
                              showData();
                              updateAgeText();
                          });
                  } else if ( msg->button.button == m_age_reset_b ) {
                      m_entry_filter_age = 0;

                      apply_filter( m_filter, [this]( int matches ) {
                              showData();
                              updateAgeText();
                          });
                  } else if ( msg->button.button == m_hold_status_b ) {
                      if ( m_hold_status.active ) {
                          resetHeldEntriesAndUpdateUI();
                      } else {
                          showHoldHelp();
                      }
                  } else if ( msg->button.button == m_lock_dir_status_b ) {
                      if ( is_locked_on_directory() ) {
                          unlock_directory();
                      } else {
                          lock_to_current_path();
                      }
                  } else {
                      for ( unsigned int i = 0; i < m_breadcrumb_buttons.size(); i++ ) {
                          if ( m_breadcrumb_buttons[i] == msg->button.button ) {
                              m_current_depth = i;
                              endmode = JUMP_CURRENT_PATH;
                          }
                      }
                  }
                  break;
              case AG_KEYPRESSED:
                  if ( msg->key.key == XK_BackSpace ) {
                      if ( KEYSTATEMASK( msg->key.keystate ) == ShiftMask ) {
                          std::string *f;

                          switch( m_k1->getCurOption() ) {
                              case 1:
                                  f = &m_byfilter_filter;
                                  break;
                              case 2:
                                  f = &m_byprog_filter;
                                  break;
                              default:
                                  f = &m_filter;
                                  break;
                          };
                          
                          *f = "";
                          apply_filter( *f, [this]( int matches ) {
                                  showData();
                              });
                      } else {
                          std::string *f;

                          switch( m_k1->getCurOption() ) {
                              case 1:
                                  f = &m_byfilter_filter;
                                  break;
                              case 2:
                                  f = &m_byprog_filter;
                                  break;
                              default:
                                  f = &m_filter;
                                  break;
                          };

                          std::string cur_infix = *f;
                          
                          if ( cur_infix.length() > 0 ) {
                              cur_infix.resize( cur_infix.length() - 1 );
                              *f = cur_infix;
                              apply_filter( *f, [this]( int matches ) {
                                      showData();
                                  });
                          }
                      }
                  } else if ( msg->key.key == XK_Return ) {
                      if ( KEYSTATEMASK( msg->key.keystate ) == ControlMask ) {
                          endmode = JUMP_FULL_PATH;
                      } else {
                          endmode = JUMP_CURRENT_PATH;
                      }
                  } else if ( msg->key.key == XK_Escape ) {
                      endmode = CLOSE;
                  } else if ( msg->key.key == XK_Left &&
                              m_k1->getCurOption() == 0 ) {
                      if ( ! m_current_components.empty() ) {
                          if ( m_current_depth < 0 ) {
                              m_current_depth = m_current_components.size();
                          } else if ( m_current_depth > 0 ) {
                              if ( m_current_depth > (int)m_current_components.size() &&
                                   m_current_components.size() >= 1 ) {
                                  m_current_depth = m_current_components.size();
                              }

                              if ( is_locked_on_directory() &&
                                   NWC::Path::is_prefix_dir( m_lock_base_dir, NWC::Path::dirname( getCurrentPath() ) ) ) {
                                  m_current_depth--;
                              } else if ( ! is_locked_on_directory() ) {
                                  m_current_depth--;
                              }
                          }

                          if ( m_entry_filter_mode != SHOW_ALL ) {
                              post_filtering();
                              showData();
                          } else {
                              update_breadcrumb();
                          }
                      }
                  } else if ( msg->key.key == XK_Left &&
                              m_k1->getCurOption() == 2 ) {
                      m_lv_progs->takeFocus();
                  } else if ( msg->key.key == XK_Right &&
                              m_k1->getCurOption() == 0 ) {
                      if ( ! m_current_components.empty() ) {
                          if ( KEYSTATEMASK( msg->key.keystate ) == ShiftMask ) {
                              m_current_depth = (int)( m_current_components.size() );
                          } else {
                              if ( m_current_depth < 0 ) {
                                  m_current_depth = 0;
                              } else if ( m_current_depth <= (int)( m_current_components.size() - 1 ) ) {
                                  m_current_depth++;
                              }
                          }

                          if ( m_entry_filter_mode != SHOW_ALL ) {
                              post_filtering();
                              showData();
                          } else {
                              update_breadcrumb();
                          }
                      }
                  } else if ( msg->key.key == XK_Right &&
                              m_k1->getCurOption() == 2 ) {
                      m_lv_prog_files->takeFocus();
                  } else if ( msg->key.key == XK_Up ) {
                      if ( m_k1->getCurOption() == 0 ) {
                          m_lv->takeFocus();
                      } else if ( m_k1->getCurOption() == 1 ) {
                          m_byfilter_lv->takeFocus();
                      } else if ( m_k1->getCurOption() == 2 ) {
                          m_lv_progs->takeFocus();
                      }
                  } else if ( msg->key.key == XK_Down ) {
                      if ( m_k1->getCurOption() == 0 ) {
                          m_lv->takeFocus();
                      } else if ( m_k1->getCurOption() == 1 ) {
                          m_byfilter_lv->takeFocus();
                      } else if ( m_k1->getCurOption() == 2 ) {
                          m_lv_progs->takeFocus();
                      }
                  } else if ( msg->key.key == XK_Next ) {
                  } else if ( msg->key.key == XK_Prior ) {
                  } else if ( msg->key.key == XK_Home &&
                              m_k1->getCurOption() == 0 ) {
                      // key is grabbed by listview so it has no effect at the moment
                      if ( ! m_current_components.empty() ) {
                          m_current_depth = 0;
                          update_breadcrumb();
                      }
                  } else if ( msg->key.key == XK_End &&
                              m_k1->getCurOption() == 0 ) {
                      // key is grabbed by listview so it has no effect at the moment
                      if ( ! m_current_components.empty() ) {
                          m_current_depth = m_current_components.size();
                          update_breadcrumb();
                      }
                  } else if ( msg->key.key == XK_F1 ) {
                      m_k1->optionChange( 0 );
                  } else if ( msg->key.key == XK_F2 ) {
                      m_k1->optionChange( 1 );
                      m_byfilter_lv->takeFocus();
                  } else if ( msg->key.key == XK_F3 ) {
                      m_k1->optionChange( 2 );
                      m_lv_progs->takeFocus();
                  } else if ( IsModifierKey( msg->key.key ) ||
                              IsCursorKey( msg->key.key ) ||
                              IsPFKey( msg->key.key ) ||
                              IsFunctionKey( msg->key.key ) ||
                              IsMiscFunctionKey( msg->key.key ) ||
                              ( ( msg->key.key >= XK_BackSpace ) && ( msg->key.key <= XK_Escape ) ) ) {
                  } else if ( msg->key.key >= XK_0 && msg->key.key <= XK_9 &&
                              KEYSTATEMASK( msg->key.keystate ) == ControlMask &&
                              m_k1->getCurOption() == 0) {
                      if ( msg->key.key == XK_0 ) {
                          resetHeldEntriesAndUpdateUI();
                      } else {
                          holdCurrentEntriesAndUpdateUI( msg->key.key - XK_0 );
                      }
                  } else if ( msg->key.key == XK_l &&
                              KEYSTATEMASK( msg->key.keystate ) == ControlMask &&
                              m_k1->getCurOption() == 0) {
                      if ( is_locked_on_directory() ) {
                          unlock_directory();
                      } else {
                          lock_to_current_path();
                      }
                  } else if ( strlen( msg->key.keybuf ) > 0 ) {
                      if ( msg->key.key == XK_a &&
                           KEYSTATEMASK( msg->key.keystate ) == ControlMask &&
                           m_k1->getCurOption() == 0 ) {
                          if ( ! m_hold_status.active ) {
                              m_show_hidden_entries = ! m_show_hidden_entries;
                              m_show_all_cb->setState( m_show_hidden_entries );
                              apply_filter( m_filter, [this]( int matches ) {
                                      showData();
                                  });
                          }
                      } else if ( msg->key.key == XK_A &&
                                  KEYSTATEMASK( msg->key.keystate ) == ( ControlMask | ShiftMask ) &&
                                  m_k1->getCurOption() == 0 ) {
                          if ( ! m_hold_status.active ) {
                              m_include_archive = ! m_include_archive;
                              m_include_archive_cb->setState( m_include_archive );

                              update_entries();
                          }
                      } else if ( msg->key.key == XK_t &&
                                  KEYSTATEMASK( msg->key.keystate ) == ControlMask &&
                                  m_k1->getCurOption() == 0 ) {
                          m_ignore_date_for_sorting = ! m_ignore_date_for_sorting;
                          m_ignore_date_cb->setState( m_ignore_date_for_sorting );
                          apply_filter( m_filter, [this]( int matches ) {
                                  showData();
                              });
                      } else if ( msg->key.key == XK_r &&
                                  KEYSTATEMASK( msg->key.keystate ) == ControlMask &&
                                  m_k1->getCurOption() == 0 ) {
                          m_relative_view_mode = ! m_relative_view_mode;
                          m_show_relative_paths_cb->setState( m_relative_view_mode );
                          m_lv_dirty = true;
                          showData();
                      } else if ( msg->key.key == XK_f &&
                                  KEYSTATEMASK( msg->key.keystate ) == ControlMask &&
                                  m_k1->getCurOption() == 0 ) {
                          switch ( m_entry_filter_mode ) {
                              case SHOW_ONLY_SUBDIRS:
                                  m_entry_filter_mode = SHOW_ONLY_DIRECT_SUBDIRS;
                                  m_filter_mode_cyb->setOption( 2 );
                                  m_show_relative_paths_cb->show();
                                  break;
                              case SHOW_ONLY_DIRECT_SUBDIRS:
                                  m_entry_filter_mode = SHOW_ALL;
                                  m_filter_mode_cyb->setOption( 0 );
                                  m_show_relative_paths_cb->hide();
                                  break;
                              default:
                                  m_entry_filter_mode = SHOW_ONLY_SUBDIRS;
                                  m_filter_mode_cyb->setOption( 1 );
                                  m_show_relative_paths_cb->show();
                                  break;
                          }
                          apply_filter( m_filter, [this]( int matches ) {
                                  showData();
                              });
                      } else if ( msg->key.key == XK_h &&
                                  KEYSTATEMASK( msg->key.keystate ) == ControlMask &&
                                  m_k1->getCurOption() == 0 ) {
                          m_entry_filter_age += 1 * 60 * 60;

                          apply_filter( m_filter, [this]( int matches ) {
                                  showData();
                                  updateAgeText();
                              });
                      } else if ( msg->key.key == XK_d &&
                                  KEYSTATEMASK( msg->key.keystate ) == ControlMask &&
                                  m_k1->getCurOption() == 0 ) {
                          m_entry_filter_age += 1 * 24 * 60 * 60;

                          apply_filter( m_filter, [this]( int matches ) {
                                  showData();
                                  updateAgeText();
                              });
                      } else if ( msg->key.key == XK_w &&
                                  KEYSTATEMASK( msg->key.keystate ) == ControlMask &&
                                  m_k1->getCurOption() == 0 ) {
                          m_entry_filter_age += 7 * 24 * 60 * 60;

                          apply_filter( m_filter, [this]( int matches ) {
                                  showData();
                                  updateAgeText();
                              });
                      } else if ( msg->key.key == XK_e &&
                                  KEYSTATEMASK( msg->key.keystate ) == ControlMask &&
                                  ( m_k1->getCurOption() == 0 ||
                                    m_k1->getCurOption() == 2 ) ) {
                          m_hide_non_existing = !m_hide_non_existing;
                          m_hide_non_existing_cb->setState( m_hide_non_existing );
                          m_byprog_hide_non_existing_cb->setState( m_hide_non_existing );


                          m_lv_dirty = true;
                          m_byprog_lv_dirty = true;
                          showData();
                      } else {
                          std::string *f;

                          switch( m_k1->getCurOption() ) {
                              case 1:
                                  f = &m_byfilter_filter;
                                  break;
                              case 2:
                                  f = &m_byprog_filter;
                                  break;
                              default:
                                  f = &m_filter;
                                  break;
                          };

                          std::string cur_infix = *f;
                          cur_infix += msg->key.keybuf;

                          apply_filter( cur_infix, [this]( int matches ) {
                                                       showData();
                                                   });
                      }
                  }
                  break;
              case AG_FIELDLV_DOUBLECLICK:
                  if ( msg->fieldlv.lv == m_lv ||
                       msg->fieldlv.lv == m_byfilter_lv ||
                       msg->fieldlv.lv == m_lv_progs ) {
                      // double click in lv, actual element is unimportant here
                      endmode = JUMP_CURRENT_PATH;
                  } else if ( msg->fieldlv.lv == m_class_counts_lv ) {
                      holdCurrentEntriesAndUpdateUI( msg->fieldlv.row + 1 );
                  }
                  break;
                case AG_FIELDLV_ONESELECT:
                case AG_FIELDLV_MULTISELECT:
                    if ( msg->fieldlv.lv == m_lv ) {
                        handleMainLVRowChange( m_lv->getActiveRow() );
                    } else if ( msg->fieldlv.lv == m_byfilter_lv ) {
                        handleByFilterLVRowChange( m_byfilter_lv->getActiveRow() );
                    } else if ( msg->fieldlv.lv == m_lv_progs ) {
                        showPathsForProgram( m_lv_progs->getActiveRow() );
                    } else if ( msg->fieldlv.lv == m_class_counts_lv ) {
                        handleMatchClassRow( msg->fieldlv.row );
                    }
                    break;
                case AG_CHOOSECLICKED:
                    if ( msg->choose.button == m_show_all_cb ) {
                        m_show_hidden_entries = msg->choose.state;
                        apply_filter( m_filter, [this]( int matches ) {
                                showData();
                            });
                    } else if ( msg->choose.button == m_include_archive_cb ) {
                        m_include_archive = msg->choose.state;
                        update_entries();
                    } else if ( msg->choose.button == m_ignore_date_cb ) {
                        m_ignore_date_for_sorting = m_ignore_date_cb->getState();
                        apply_filter( m_filter, [this]( int matches ) {
                                showData();
                            });
                    } else if ( msg->choose.button == m_show_relative_paths_cb ) {
                        m_relative_view_mode = m_show_relative_paths_cb->getState();
                        m_lv_dirty = true;
                        showData();
                    } else if ( msg->choose.button == m_hide_non_existing_cb ||
                                msg->choose.button == m_byprog_hide_non_existing_cb ) {
                        m_hide_non_existing = msg->choose.state;

                        m_hide_non_existing_cb->setState( m_hide_non_existing );
                        m_byprog_hide_non_existing_cb->setState( m_hide_non_existing );

                        m_lv_dirty = true;
                        m_byprog_lv_dirty = true;
                        showData();
                    }
                    break;
                case AG_CYCLEBUTTONCLICKED:
                    if ( msg->cyclebutton.cyclebutton == m_filter_mode_cyb ) {
                        switch ( msg->cyclebutton.option ) {
                            case 1:
                                m_entry_filter_mode = SHOW_ONLY_SUBDIRS;
                                m_show_relative_paths_cb->show();
                                break;
                            case 2:
                                m_entry_filter_mode = SHOW_ONLY_DIRECT_SUBDIRS;
                                m_show_relative_paths_cb->show();
                                break;
                            default:
                                m_entry_filter_mode = SHOW_ALL;
                                m_show_relative_paths_cb->hide();
                                break;
                        }
                        apply_filter( m_filter, [this]( int matches ) {
                                showData();
                            });
                    }
                    break;
                case AG_SIZECHANGED:
                    if ( msg->size.window == m_win->getWindow() &&
                         ! msg->size.explicit_resize ) {
                        persist_width = msg->size.neww;
                        persist_height = msg->size.newh;
                    }
                    break;
            }
            m_aguix.ReplyMessage( msg );
        }

        if ( ! m_apply_state.done ) {
            apply_filter_next();
        }

        if ( endmode == JUMP_CURRENT_PATH ||
             endmode == JUMP_FULL_PATH ) {
            int prev_current_depth = m_current_depth;

            if ( endmode == JUMP_FULL_PATH ) {
                m_current_depth = (int)( m_current_components.size() );
            }

            if ( ! NWC::FSEntry( getCurrentPath() ).entryExists() ) {
                endmode = RUNNING;
                m_current_depth = prev_current_depth;
            }
        }
    }

    m_selected_path = "";
    m_selected_path_stored = false;

    if ( endmode == PANELIZE ) {
    } else if ( endmode == JUMP_CURRENT_PATH ||
                endmode == JUMP_FULL_PATH ) {
        std::string res = "";

        if ( endmode == JUMP_FULL_PATH ) {
            m_current_depth = (int)( m_current_components.size() );
        }

        // finalize running filter
        while ( ! m_apply_state.done ) {
            apply_filter_next();
        }

        if ( m_k1->getCurOption() == 0 ) {
            if ( m_current_depth <= 0 ) {
                res = "/";
            } else {
                for ( unsigned int i = 0; i < m_current_components.size(); i++ ) {
                    if ( (int)i >= m_current_depth ) break;

                    res += "/";
                    res += m_current_components[i].first;
                }
            }
        } else if ( m_k1->getCurOption() == 1 ) {
            res = m_current_entry_byfilter;
        } else if ( m_k1->getCurOption() == 2 ) {
            int row = m_lv_prog_files->getActiveRow();

            if ( m_lv_prog_files->isValidRow( row ) ) {
                res = m_lv_prog_files->getText( row, 1 );
            }
        }

        m_selected_path = res;

        std::string filter;

        if ( m_k1->getCurOption() == 1 ) {
            if ( m_byfilter_lv->isValidRow( m_byfilter_lv->getActiveRow() ) ) {
                filter = m_byfilter_lv->getText( m_byfilter_lv->getActiveRow(), 0 );
            }
        } else if ( m_k1->getCurOption() == 0 ) {
            filter = m_filter;
        }

        if ( filter.empty() ) {
            m_worker.setPathJumpInProgress( true );
        } else {
            m_worker.setPathJumpInProgress( filter );
        }

        // check if this path is allowed to be tracked, than put its
        // access into the prefixdb
        if ( ! wconfig->getPathJumpAllowDirs().empty() ) {
            const std::list< std::string > &l = wconfig->getPathJumpAllowDirs();

            for ( std::list< std::string >::const_iterator it1 = l.begin();
                  it1 != l.end();
                  it1++ ) {
                if ( AGUIXUtils::starts_with( m_selected_path, *it1 ) ) {
                    m_selected_path_stored = m_worker.storePathPersIfInProgress( m_selected_path );
                    break;
                }
            }
        }
    }

    if ( persist_height > 0 ) {
        Worker::getPersKVPStore().setIntValue( "pathjumpwin-width",
                                               persist_width );
    }
    if ( persist_height > 0 ) {
        Worker::getPersKVPStore().setIntValue( "pathjumpwin-height",
                                               persist_height );
    }

    m_win->hide();

    moveTests();

    if ( endmode == JUMP_CURRENT_PATH ||
         endmode == JUMP_FULL_PATH ) {
        return 1;
    }

    if ( endmode == PANELIZE ) {
        return 2;
    }

    return -1;
}

void PathJumpUI::buildMatchClassesLV()
{
    m_class_counts_lv->setSize( 0 );

    for ( auto &e : m_match_classes ) {
        int row = m_class_counts_lv->addRow();

        std::string t1;

        if ( e.first >= 0 ) {
            t1 = AGUIXUtils::formatStringToString( "%3.0f%%",
                                                   100.0 / (double)( e.first + 1 ) );

            m_class_counts_lv->setText( row, 0, t1 );
        } else {
            m_class_counts_lv->setText( row, 0, "100%" );
        }

        if ( row == 0 ) {
            t1 = AGUIXUtils::formatStringToString( "%d",
                                                   e.second );
        } else {
            t1 = AGUIXUtils::formatStringToString( "+%d",
                                                   e.second );
        }

        m_class_counts_lv->setText( row, 1, t1 );

        if ( row < 9 ) {
            t1 = AGUIXUtils::formatStringToString( "Ctrl-%d",
                                                   row + 1 );

            m_class_counts_lv->setText( row, 2, t1 );
        }

        m_class_counts_lv->setPreColors( row, FieldListView::PRECOLOR_ONLYACTIVE );
    }

    m_class_counts_lv->redraw();
}

void PathJumpUI::showData()
{
    m_ondemand_info.matcher.setMatchString( m_filter );

    if ( m_lv_dirty ) {

        m_lv->setSize( 0 );
        m_entry_fullnames.resize( 0 );

        // invalidate running futures
        for ( auto &e : m_existence_tests ) {
            std::get<2>( e ) = false;
        }
    
        int size = 0;
        size_t pos = 0;

        std::string prefix_filter;

        if ( m_entry_filter_mode == SHOW_ONLY_SUBDIRS ||
             m_entry_filter_mode == SHOW_ONLY_DIRECT_SUBDIRS ) {
            int i = 0;

            if ( m_current_depth > 0 ) {
                for ( auto it1 = m_current_components.begin();
                      it1 != m_current_components.end();
                      it1++, i++ ) {
                    prefix_filter += "/";
                    prefix_filter += it1->first;
                    if ( i + 1 >= m_current_depth ) break;
                }
            }
        }

        size_t l = prefix_filter.length();
        bool current_entry_found = false;

        m_ondemand_info.prefix_filter_length = l;

        for ( auto it1 = m_entries.begin();
              it1 != m_entries.end();
              it1++, pos++ ) {
            if ( ( it1->m_type == PATH_SHOW ||
                   it1->m_type == BOOKMARK_SHOW ) &&
                 it1->m_post_filtering &&
                 it1->m_always_ignore == false ) {
                int row = m_lv->addRow();

                m_entry_fullnames.push_back( std::make_tuple( it1->m_path,
                                                              it1->m_last_access,
                                                              (ExistenceTest::existence_state_t){ .exists = ExistenceTest::EXISTS_UNKNOWN,
                                                                  .path_length = 0 },
                                                              pos ) );

                if ( it1->m_blockcount >= 0 && ! m_filter.empty() ) {
                    std::string qual = AGUIXUtils::formatStringToString( "%3.0f%%",
                                                                         100.0 / (double)( it1->m_blockcount + 1 ) );
                    m_lv->setText( row, 0, qual );
                }

                m_lv->setPreColors( row, FieldListView::PRECOLOR_ONLYACTIVE );

                if ( get_current_entry() == it1->m_path ) {
                    m_lv->setActiveRow( row );
                    current_entry_found = true;
                }

                size++;
            }
        }

        if ( ! current_entry_found ) {
            for ( int row = 0; row < m_lv->getElements(); row++ ) {
                if ( AGUIXUtils::starts_with( get_current_entry(),
                                              m_lv->getText( row, 3 ) ) ) {
                    m_lv->setActiveRow( row );
                    break;
                }
            }
        }

        calculateMatchClasses();
        buildMatchClassesLV();

        m_lv->showActive();
        m_lv->redraw();
    }

    update_breadcrumb();

    m_lv_dirty = false;

    if ( m_byfilter_lv_dirty ) {
        m_byfilter_lv->setSize( 0 );
        m_byfilter_row_to_element.clear();

        // invalidate running futures
        for ( auto &e : m_existence_tests_filter_path ) {
            std::get<2>( e ) = false;
        }
    
        int pos = 0;

        bool current_entry_found = false;

        for ( auto it1 = m_best_hits.begin();
              it1 != m_best_hits.end();
              it1++, pos++ ) {
            if ( it1->visible == true ) {
                int row = m_byfilter_lv->addRow();
                m_byfilter_row_to_element.push_back( pos );

                m_byfilter_lv->setText( row, 0, it1->filter );
                m_byfilter_lv->setText( row, 3, it1->path );
                m_byfilter_lv->setPreColors( row, FieldListView::PRECOLOR_ONLYACTIVE );

                if ( m_current_entry_byfilter == it1->path &&
                     ! current_entry_found ) {
                    m_byfilter_lv->setActiveRow( row );
                    current_entry_found = true;
                }
            }
        }

        if ( ! current_entry_found ) {
            for ( int row = 0; row < m_byfilter_lv->getElements(); row++ ) {
                if ( AGUIXUtils::starts_with( m_current_entry_byfilter,
                                              m_byfilter_lv->getText( row, 3 ) ) ) {
                    m_byfilter_lv->setActiveRow( row );
                    current_entry_found = true;
                    break;
                }
            }
        }

        if ( ! current_entry_found ) {
            m_byfilter_lv->setActiveRow( 0 );
            handleByFilterLVRowChange( m_byfilter_lv->getActiveRow() );
        }

        m_byfilter_lv_dirty = false;

        m_byfilter_lv->showActive();
        m_byfilter_lv->redraw();
    }

    showProgramData();
}

void PathJumpUI::showProgramData()
{
    if ( ! m_byprog_lv_dirty ) {
        return;
    }

    // invalidate running futures
    for ( auto &e : m_existence_tests_prog_path ) {
        std::get<2>( e ) = false;
    }
    
    time_t now = time( NULL );

    std::string current_program;

    if ( m_lv_progs->isValidRow( m_lv_progs->getActiveRow() ) ) {
        current_program = m_lv_progs->getText( m_lv_progs->getActiveRow(), 0 );
    }

    m_lv_progs->setSize( 0 );

    int pos = 0;
    int new_active_row = -1;

    for ( auto it1 = m_used_programs.begin();
          it1 != m_used_programs.end();
          it1++, pos++ ) {
        if ( it1->visible == true ) {
            int row = m_lv_progs->addRow();

            m_lv_progs->setText( row, 0, it1->program );
            m_lv_progs->setText( row, 1, WorkerTime::convert_time_diff_to_string( now - it1->last_access ) );
            m_lv_progs->setPreColors( row, FieldListView::PRECOLOR_ONLYACTIVE );

            if ( it1->program == current_program ) {
                new_active_row = row;
            }
        }
    }

    if ( new_active_row >= 0 ) {
        m_lv_progs->setActiveRow( new_active_row );
    } else {
        m_lv_progs->setActiveRow( 0 );
    }
    showPathsForProgram( m_lv_progs->getActiveRow() );

    m_byprog_lv_dirty = false;

    m_lv_progs->showActive();
    m_lv_progs->redraw();
}

void PathJumpUI::showPathsForProgram( int program_row )
{
    time_t now = time( NULL );

    std::string current_file;

    if ( m_lv_prog_files->isValidRow( m_lv_prog_files->getActiveRow() ) ) {
        current_file = m_lv_prog_files->getText( m_lv_prog_files->getActiveRow(), 1 );
    }

    m_lv_prog_files->setSize( 0 );

    std::string program = m_lv_progs->getText( program_row, 0 );

    if ( m_program_paths.count( program ) == 0 ) {
        m_lv_prog_files->redraw();
        return;
    }

    auto &path_list = m_program_paths[ program ];

    size_t pos = 0;
    int new_active_row = -1;
    auto it = path_list.begin();
    for ( ;
          it != path_list.end();
          pos++, it++ ) {
        auto &e = *it;

        if ( m_hide_non_existing ) {
            if ( e.exists.exists == ExistenceTest::EXISTS_NO ) {
                continue;
            }
        }

        int row = m_lv_prog_files->addRow();

        m_lv_prog_files->setText( row, 1, e.path );
        m_lv_prog_files->setText( row, 2, WorkerTime::convert_time_diff_to_string( now - e.last_access ) );
        m_lv_prog_files->setPreColors( row, FieldListView::PRECOLOR_ONLYACTIVE );
        m_lv_prog_files->setData( row, (int)pos );

        if ( e.path == current_file ) {
            new_active_row = row;
        }
    }

    if ( new_active_row >= 0 ) {
        m_lv_prog_files->setActiveRow( new_active_row );
    } else {
        m_lv_prog_files->setActiveRow( 0 );
    }

    m_lv_prog_files->showActive();
    m_lv_prog_files->redraw();
}

std::string PathJumpUI::getSelectedPath( bool &stored )
{
    stored = m_selected_path_stored;
    return m_selected_path;
}

void PathJumpUI::setCurrentDirname( const std::string &dirname )
{
    m_dirname = dirname;
}

void PathJumpUI::setCurrentBasename( const std::string &basename )
{
    m_basename = basename;
}

void PathJumpUI::maximizeWin()
{
    m_lv->maximizeX();
    int my_w = m_lv->getWidth() + 10;
    int my_h = m_lv->getHeight() + 10;

    if ( my_w < 400 ) my_w = 400;
    if ( my_h < 300 ) my_h = 300;

    int rx, ry, rw, rh;

    m_aguix.getLargestDimensionOfCurrentScreen( &rx, &ry,
                                                &rw, &rh );

    int mw = rw * 80 / 100;
    int mh = rh * 80 / 100;
    m_mainco->resize( mw, mh );
    m_mainco->rearrange();

    if ( my_w < m_lv->getWidth() ) {
        m_co1->setMinWidth( my_w, 0, 4 );
    } else {
        m_co1->setMinWidth( m_lv->getWidth(), 0, 4 );
    }
    m_win->contMaximize( true );
}

void PathJumpUI::highlight_best_hit( const std::string &filter )
{
    find_entry( filter, "" );

    post_filtering();

    showData();
}

void PathJumpUI::apply_filter( const std::string filter,
                               std::function< void( int ) > finalize )
{
    m_apply_state = apply_filter_state();
    m_apply_state.it1 = m_entries.begin();

    if ( m_k1->getCurOption() == 1 ) {
        m_byfilter_filter = filter;

        StringMatcherFNMatch matcher;
        matcher.setMatchString( "*" + filter + "*" );
        matcher.setMatchCaseSensitive( false );

        time_t t1;
        bool first_found = false;
        auto best_hit = m_worker.getDirHistPrefixDB().getBestHit( filter, t1 );

        for ( auto it1 = m_best_hits.begin();
              it1 != m_best_hits.end();
              it1++ ) {
            if ( matcher.match( it1->path ) ) {
                it1->visible = true;
            } else if ( matcher.match( it1->filter ) ) {
                it1->visible = true;
            } else {
                it1->visible = false;
            }

            if ( it1->visible ) {
                if ( AGUIXUtils::starts_with( it1->path,
                                              best_hit ) ||
                     first_found == false ) {
                    m_current_entry_byfilter = it1->path;

                    if ( first_found ) {
                        break;
                    } else {
                        first_found = true;
                    }
                }
            }
        }

        m_byfilter_lv_dirty = true;
        m_byfilter_filter_t->setText( filter.c_str() );
        if ( finalize ) {
            finalize( 0 );
        }
        return;
    }

    if ( m_k1->getCurOption() == 2 ) {
        m_byprog_filter = filter;

        StringMatcherFNMatch matcher;
        matcher.setMatchString( "*" + filter + "*" );
        matcher.setMatchCaseSensitive( false );

        for ( auto &e : m_used_programs ) {
            if ( matcher.match( e.program ) ) {
                e.visible = true;
            } else {
                e.visible = false;
            }
        }

        m_byprog_lv_dirty = true;
        m_byprog_filter_t->setText( filter.c_str() );
        if ( finalize ) {
            finalize( 0 );
        }
        return;
    }

    m_filter = filter;

    std::string visible_filter = m_filter;
    visible_filter += "...";
    m_infixtext->setText( visible_filter.c_str() );

    m_apply_state.flmatch.setMatchString( filter );
    m_apply_state.flmatch.setMatchCaseSensitive( false );

    m_apply_state.flexible_string_filter = "*" + StringMatcherFNMatch::convertMatchStringToFlexibleMatch( filter ) + "*";

    m_apply_state.flprematcher.setMatchString( m_apply_state.flexible_string_filter );
    m_apply_state.flprematcher.setMatchCaseSensitive( false );

    m_apply_state.finalize = finalize;

    if ( filter.find( "*" ) != std::string::npos ) {
        std::string real_filter = "*";
        real_filter += filter;
        real_filter += "*";

        m_apply_state.match.setMatchString( real_filter );
        m_apply_state.match.setMatchCaseSensitive( false );

        m_apply_state.use_flexible = false;
    }

    m_apply_state.filter = filter;
}

void PathJumpUI::apply_filter_next()
{
    if ( m_apply_state.done == true ) {
        return;
    }
    
    if ( m_apply_state.it1 != m_entries.end() ) {
        if ( m_apply_state.it1->m_always_ignore ) {
            m_apply_state.it1++;

            return;
        }

        if ( m_apply_state.use_flexible ) {
            // check with fnmatch first which is faster than the flexiblematcher but can't return the blockcount
            if ( m_apply_state.flprematcher.match( m_apply_state.it1->m_path ) ) {
                m_apply_state.it1->m_blockcount = m_apply_state.flmatch.countNonMatchingBlocks<false>( m_apply_state.it1->m_path, NULL );
            } else {
                m_apply_state.it1->m_blockcount = -1;
            }
        } else {
            m_apply_state.it1->m_blockcount = m_apply_state.match.match( m_apply_state.it1->m_path ) ? 0 : -1;
        }

        if ( m_apply_state.it1->m_blockcount == 0 ) {
            if ( m_apply_state.it1->m_type == PATH_SHOW ||
                 m_apply_state.it1->m_type == PATH_HIDE ) {
                m_apply_state.exact_path_matches++;
                m_apply_state.path_matches++;
            } else {
                m_apply_state.exact_bookmark_matches++;
                m_apply_state.bookmark_matches++;
            }
        } else if ( m_apply_state.it1->m_blockcount > 0 ) {
            if ( m_apply_state.it1->m_type == PATH_SHOW ||
                 m_apply_state.it1->m_type == PATH_HIDE ) {
                m_apply_state.path_matches++;
            } else {
                m_apply_state.bookmark_matches++;
            }
        }

        m_apply_state.it1++;

        return;
    }

    auto current_path = get_current_entry();

    std::sort( m_entries.begin(),
               m_entries.end(),
               std::bind( pathjump_entry::compare,
                          std::placeholders::_1,
                          std::placeholders::_2,
                          m_ignore_date_for_sorting ) );

    m_current_entry_pos = m_entries.size();

    size_t pos = 0;
    for ( auto it1 = m_entries.begin();
          it1 != m_entries.end();
          it1++, pos++ ) {
        if ( it1->m_always_ignore ) continue;

        if ( it1->m_type == PATH_SHOW ||
             it1->m_type == PATH_HIDE ) {
            if ( it1->m_blockcount == 0 ||
                 ( it1->m_blockcount > 0 &&
                   ( ! m_apply_state.prefer_exact_matches || ( m_apply_state.prefer_exact_matches && m_apply_state.exact_path_matches == 0 ) ) ) ) {
                it1->m_type = PATH_SHOW;
                m_apply_state.matches++;
            } else {
                it1->m_type = PATH_HIDE;
            }
        } else {
            if ( ( m_apply_state.path_matches == 0 && ! m_apply_state.filter.empty() ) ||
                 m_show_hidden_entries == true ||
                 m_hold_status.active == true ) {
                if ( it1->m_blockcount == 0 ||
                     ( it1->m_blockcount > 0 &&
                       ( ! m_apply_state.prefer_exact_matches || ( m_apply_state.prefer_exact_matches && m_apply_state.exact_bookmark_matches == 0 ) ) ) ) {
                    it1->m_type = BOOKMARK_SHOW;
                    m_apply_state.matches++;
                } else {
                    it1->m_type = BOOKMARK_HIDE;
                }
            } else {
                    it1->m_type = BOOKMARK_HIDE;
            }
        }

        if ( m_entry_filter_age > 0 && m_current_time > m_entry_filter_age ) {
            if ( it1->m_last_access + m_entry_filter_age < m_current_time ) {
                if ( it1->m_type == BOOKMARK_SHOW ) {
                    it1->m_type = BOOKMARK_HIDE;
                } else {
                    it1->m_type = PATH_HIDE;
                }
            }
        }

        if ( it1->m_path == current_path ) {
            m_current_entry_pos = pos;
        }
    }

    // avoid calling find_entry when the filter hasn't been changed.
    // that way the current entry and depth does not change
    if ( m_apply_state.filter != m_previous_apply_filter ) {
        find_entry( get_current_entry(), m_apply_state.filter );
        m_previous_apply_filter = m_apply_state.filter;
    }

    post_filtering();

    m_lv_dirty = true;

    m_apply_state.done = true;

    m_infixtext->setText( m_filter.c_str() );

    if ( m_apply_state.finalize ) {
        m_apply_state.finalize( m_apply_state.matches );
    }
}

int PathJumpUI::find_entry( const std::string &name, const std::string &filter )
{
    std::string entry_to_find;
    bool first_found = false;
    std::string best_hit;

    if ( ! filter.empty() ) {
        time_t t1;
        best_hit = entry_to_find = m_worker.getDirHistPrefixDB().getBestHit( filter, t1 );
    }

    if ( entry_to_find.empty() ) {
        entry_to_find = name;
    }

    size_t pos = 0;
    for ( auto it1 = m_entries.begin();
          it1 != m_entries.end();
          it1++, pos++ ) {
        if ( it1->m_always_ignore ) continue;

        if ( it1->m_type == PATH_SHOW ||
             it1->m_type == BOOKMARK_SHOW ) {
            if ( ! m_hide_non_existing ||
                 ( m_hide_non_existing && it1->m_exists.exists != ExistenceTest::EXISTS_NO ) ) {
                if ( NWC::Path::is_prefix_dir( it1->m_path,
                                               entry_to_find ) ||
                     first_found == false ) {
                    m_current_entry_pos = pos;

                    if ( first_found ) {
                        break;
                    } else {
                        first_found = true;
                    }
                }
            }
        }
    }

    m_current_depth = -1;

    if ( ! best_hit.empty() ) {
        std::string f = best_hit;
        f += "*";
        
        m_current_depth = find_best_matching_depth( get_current_entry(), f );
    }

    if ( m_current_depth < 0 ) {
        if ( ! filter.empty() ) {
            m_current_depth = find_best_matching_depth( get_current_entry(), filter );
        } else {
            // workaround to adjust the depth to the name argument
            m_current_depth = find_best_matching_depth( get_current_entry(), name + "*" );
        }
    }

    if ( m_entry_filter_mode != SHOW_ALL ) {
        m_lv_dirty = true;
    }

    return 0;
}

int PathJumpUI::set_entry( const std::string &name )
{
    bool found = false;

    size_t pos = 0;
    for ( auto it1 = m_entries.begin();
          it1 != m_entries.end();
          it1++, pos++ ) {
        if ( it1->m_always_ignore ) continue;

        if ( ( it1->m_type == PATH_SHOW ||
               it1->m_type == BOOKMARK_SHOW ) &&
             it1->m_path == name ) {
            if ( ! m_hide_non_existing ||
                 ( m_hide_non_existing && it1->m_exists.exists != ExistenceTest::EXISTS_NO ) ) {
                m_current_entry_pos = pos;
                found = true;
                break;
            }
        }
    }

    if ( found ) {
        if ( ! m_filter.empty() &&
             m_entry_filter_mode == SHOW_ALL ) {
            m_current_depth = find_best_matching_depth( get_current_entry(), m_filter );
        }
    } else {
        find_entry( name, m_filter );
    }

    post_filtering();

    return 0;
}

int PathJumpUI::post_filtering()
{
    std::string prefix_filter;

    build_current_path_components();

    if ( m_entry_filter_mode != SHOW_ONLY_SUBDIRS &&
         m_entry_filter_mode != SHOW_ONLY_DIRECT_SUBDIRS ) {

        for ( auto it1 = m_entries.begin();
              it1 != m_entries.end();
              it1++ ) {
            if ( it1->m_always_ignore ) continue;

            if ( it1->m_type == PATH_SHOW ||
                 it1->m_type == BOOKMARK_SHOW ) {

                if ( ! it1->m_post_filtering ) m_lv_dirty = true;

                it1->m_post_filtering = true;
            } else {

                if ( it1->m_post_filtering ) m_lv_dirty = true;

                it1->m_post_filtering = false;
            }
        }

        return 0;
    }

    if ( m_current_depth > 0 ) {
        int i = 0;
        for ( auto it1 = m_current_components.begin();
              it1 != m_current_components.end();
              it1++, i++ ) {
            prefix_filter += "/";
            prefix_filter += it1->first;
            if ( i + 1 >= m_current_depth ) break;
        }
    }

    size_t l = prefix_filter.length();
    std::map< std::string, bool > entry_visible;

    for ( auto it1 = m_entries.begin();
          it1 != m_entries.end();
          it1++ ) {
        if ( it1->m_always_ignore ) continue;

        bool new_state = false;

        if ( it1->m_type == PATH_SHOW ||
             it1->m_type == BOOKMARK_SHOW ) {
            new_state = true;
        }

        if ( ! prefix_filter.empty() &&
             ! NWC::Path::is_prefix_dir( prefix_filter, it1->m_path ) ) {
            new_state = false;
        }

        if ( m_entry_filter_mode == SHOW_ONLY_DIRECT_SUBDIRS &&
             new_state ) {

            size_t p2 = it1->m_path.find( '/', l + 1 );
            std::string s;
            if ( p2 != std::string::npos ) {
                s = std::string( it1->m_path, 0, p2 );
            } else {
                s = it1->m_path;
            }

            if ( entry_visible[s] == true ) {
                new_state = false;
            } else {
                entry_visible[s] = true;
            }
        }

        if ( it1->m_post_filtering != new_state ) m_lv_dirty = true;

        it1->m_post_filtering = new_state;
    }

    return 0;
}

int PathJumpUI::update_breadcrumb()
{
    unsigned int i = 0;
    unsigned int last_component = m_current_components.size();

    m_breadcrumb_buttons.at(i)->setText( 0, "/" );

    if ( m_current_depth == (int)i ||
         ( i == last_component && m_current_depth > (int)last_component ) ) {
        m_breadcrumb_buttons.at(i)->setFG( 0, 2 );
        m_breadcrumb_buttons.at(i)->setBG( 0, 3 );

        m_breadcrumb_co->setMinWidth( m_breadcrumb_buttons.at(i)->getMaximumWidth(),
                                      i, 0 );
    } else {
        m_breadcrumb_buttons.at(i)->setFG( 0, 1 );
        m_breadcrumb_buttons.at(i)->setBG( 0, 0 );

        m_breadcrumb_co->setMinWidth( 10,
                                      i, 0 );
    }

    m_breadcrumb_buttons.at(i)->show();

    m_breadcrumb_co->setMaxWidth( m_breadcrumb_buttons.at(i)->getMaximumWidth(),
                                  i, 0 );
    i++;

    for ( auto it1 = m_current_components.begin();
          it1 != m_current_components.end();
          it1++ ) {
        if ( ! it1->first.empty() ) {
            m_breadcrumb_buttons.at(i)->setText( 0, it1->first.c_str() );

            if ( m_current_depth == (int)i ||
                 ( i == last_component && m_current_depth > (int)last_component ) ) {
                m_breadcrumb_buttons.at(i)->setFG( 0, 2 );
                m_breadcrumb_buttons.at(i)->setBG( 0, 3 );

                m_breadcrumb_co->setMinWidth( m_breadcrumb_buttons.at(i)->getMaximumWidth(),
                                              i, 0 );
            } else {
                m_breadcrumb_buttons.at(i)->setFG( 0, 1 );
                m_breadcrumb_buttons.at(i)->setBG( 0, 0 );

                m_breadcrumb_co->setMinWidth( 10,
                                              i, 0 );
            }

            if ( it1->second ) {
                m_breadcrumb_buttons.at(i)->setStrikeOut( false );
                m_breadcrumb_buttons.at(i)->setDisabled( false );
            } else {
                m_breadcrumb_buttons.at(i)->setStrikeOut( true );
                m_breadcrumb_buttons.at(i)->setDisabled( true );
            }

            m_breadcrumb_buttons.at(i)->show();

            m_breadcrumb_co->setMaxWidth( m_breadcrumb_buttons.at(i)->getMaximumWidth(),
                                          i, 0 );
            i++;
        }
    }

    for ( ; i < m_breadcrumb_buttons.size(); i++ ) {
        m_breadcrumb_buttons.at(i)->hide();
        m_breadcrumb_co->setMaxWidth( 0,
                                      i, 0 );
        m_breadcrumb_co->setMinWidth( 0,
                                      i, 0 );
    }

    int w = m_co1->getWidth();
    m_subwin1->updateCont();

    if ( m_co1->getWidth() > w ) {
        m_subwin1->contMaximize();
        m_k1->maximizeX();
        m_win->maximizeX();
    }

    return 0;
}

int PathJumpUI::find_best_matching_depth( const std::string &filter )
{
    if ( m_lv->isValidRow( m_lv->getActiveRow() ) == true ) {
        return find_best_matching_depth( m_lv->getText( m_lv->getActiveRow(), 3 ),
                                         filter );
    }
    return -1;
}

int PathJumpUI::find_best_matching_depth( const std::string &path,
                                          const std::string &filter )
{
    std::vector< std::string > components;
    StringMatcherFNMatch matcher;
    int depth = -1;

    if ( filter.empty() ) return -1;

    AGUIXUtils::split_string( components, path, '/' );

    matcher.setMatchString( filter );
    matcher.setMatchCaseSensitive( false );

    if ( ! matcher.match( path ) ) {
        std::string flexible_filter;

        if ( filter[0] != '*' ) {
            flexible_filter = "*";
        }

        flexible_filter += StringMatcherFNMatch::convertMatchStringToFlexibleMatch( filter ) + "*";

        matcher.setMatchString( flexible_filter );
        matcher.setMatchCaseSensitive( false );
    }

    int i = 0;
    std::string prefix_path = "";

    for ( std::vector< std::string >::const_iterator it1 = components.begin();
          it1 != components.end();
          it1++ ) {
        if ( ! it1->empty() ) {
            prefix_path += "/" + *it1;

            if ( matcher.match( prefix_path ) ) {
                depth = i + 1;
                break;
            }

            i++;
        }
    }

    return depth;
}

int PathJumpUI::build_current_path_components()
{
    std::vector< std::string > components;

    if ( m_current_entry_pos >= m_entries.size() ) {
        return 1;
    }

    const std::string &path = m_entries.at( m_current_entry_pos ).m_path;
    const ExistenceTest::existence_state_t &path_exists = m_entries.at( m_current_entry_pos ).m_exists;

    m_current_components.clear();

    AGUIXUtils::split_string( components, path, '/' );

    if ( components.size() >= m_breadcrumb_buttons.size() ) {
        // something wrong here, shouldn't happen
        return 1;
    }

    std::string temp_path = "";
    bool exists = true;

    for ( std::vector< std::string >::const_iterator it1 = components.begin();
          it1 != components.end();
          it1++ ) {
        if ( ! it1->empty() ) {
            temp_path += "/";
            temp_path += *it1;

            if ( path_exists.exists == ExistenceTest::EXISTS_NO ) {
                if ( temp_path.size() > path_exists.path_length ) {
                    exists = false;
                }
            }

            if ( exists ) {
                m_current_components.push_back( std::make_pair( *it1, true ) );
            } else {
                m_current_components.push_back( std::make_pair( *it1, false ) );
            }
        }
    }

    return 0;
}

int PathJumpUI::storeCleanUp()
{
    auto win = new AWindow( &m_aguix,
                            0, 0,
                            400, 400,
                            catalog.getLocale( 1227 ),
                            AWindow::AWINDOW_DIALOG );
    win->create();

    auto co1 = win->setContainer( new AContainer( win, 1, 4 ), true );
    co1->setMaxSpace( 5 );

    RefCount<AFontWidth> lencalc( new AFontWidth( &m_aguix, NULL ) );
    auto help_ts = std::make_shared< TextStorageString >( catalog.getLocale( 1320 ), lencalc );
    auto help_tv = co1->addWidget( new TextView( &m_aguix,
                                                 0, 0, 400, 50, "", help_ts ),
                                   0, 0, AContainer::CO_INCW );
    help_tv->setLineWrap( true );
    help_tv->showFrame( false );
    help_tv->maximizeYLines( 10, help_tv->getWidth() );
    co1->readLimits();
    help_tv->show();
    help_tv->setAcceptFocus( false );

    TextView::ColorDef tv_cd = help_tv->getColors();
    tv_cd.setBackground( 0 );
    tv_cd.setTextColor( 1 );
    help_tv->setColors( tv_cd );


    auto str1 = AGUIXUtils::formatStringToString( catalog.getLocale( 1228 ), getCurrentPath().c_str() );
    co1->add( new Text( &m_aguix, 0, 0, str1.c_str() ),
              0, 1, AContainer::CO_INCWNR );

    auto co1_1 = co1->add( new AContainer( win, 7, 1 ), 0, 2 );
    co1_1->setMaxSpace( 5 );
    co1_1->setBorderWidth( 0 );

    co1_1->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 1229 ) ),
                0, 0, AContainer::CO_FIXNR + AContainer::ACONT_CENTER );

    auto sg = co1_1->addWidget( new StringGadget( &m_aguix, 0, 0, 5 * m_aguix.getTextWidth( "W" ), "0", 0 ),
                                1, 0, AContainer::CO_FIX );

    co1_1->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 1230 ) ),
                2, 0, AContainer::CO_FIXNR + AContainer::ACONT_CENTER );

    auto weekb = co1_1->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1231 ), 0 ),
                                   3, 0, AContainer::CO_FIX );
    auto monthb = co1_1->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1232 ), 0 ),
                                    4, 0, AContainer::CO_FIX );
    auto yearb = co1_1->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1233 ), 0 ),
                                   5, 0, AContainer::CO_FIX );

    auto co1_2 = co1->add( new AContainer( win, 2, 1 ), 0, 3 );
    co1_2->setMinSpace( 5 );
    co1_2->setMaxSpace( -1 );
    co1_2->setBorderWidth( 0 );
    auto checkb = co1_2->addWidget( new Button( &m_aguix,
                                                0,
                                                0,
                                                catalog.getLocale( 1234 ),
                                                0 ), 0, 0, AContainer::CO_FIX );
    auto cancelb = co1_2->addWidget( new Button( &m_aguix,
                                                 0,
                                                 0,
                                                 catalog.getLocale( 8 ),
                                                 0 ), 1, 0, AContainer::CO_FIX );

    win->contMaximize( true, true );
    win->setDoTabCycling( true );

    win->show();

    AGMessage *msg;
    int endmode = 0;

    for ( ; endmode == 0; ) {
        msg = m_aguix.WaitMessage( NULL );

        if ( msg != NULL ) {
            switch ( msg->type ) {
                case AG_CLOSEWINDOW:
                    if ( msg->closewindow.window == win->getWindow() ) {
                        endmode = -1;
                    }
                    break;
                case AG_BUTTONCLICKED:
                    if ( msg->button.button == weekb ) {
                        auto tstr = AGUIXUtils::formatStringToString( "7" );
                        sg->setText( tstr.c_str() );
                    } else if ( msg->button.button == monthb ) {
                        auto tstr = AGUIXUtils::formatStringToString( "31" );
                        sg->setText( tstr.c_str() );
                    } else if ( msg->button.button == yearb ) {
                        auto tstr = AGUIXUtils::formatStringToString( "365" );
                        sg->setText( tstr.c_str() );
                    } else if ( msg->button.button == checkb ) {
                        endmode = 1;
                    } else if ( msg->button.button == cancelb ) {
                        endmode = -1;
                    }
                    break;
            }
            m_aguix.ReplyMessage( msg );
        }
    }

    if ( endmode == 1 ) {
        char *endptr;
        int t = strtol( sg->getText(), &endptr, 10 );
        if ( *endptr == '\0' && t >= 0 ) {
            if ( cleanup( win, getCurrentPath(), t ) != 0 ) {
                endmode = -1;
            }
        } else {
            endmode = -1;
        }
    }
    
    delete win;

    return endmode == 1 ? 0 : 1;
}

int PathJumpUI::storeCleanUpArchive()
{
    auto win = new AWindow( &m_aguix,
                            0, 0,
                            400, 400,
                            catalog.getLocale( 1227 ),
                            AWindow::AWINDOW_DIALOG );
    win->create();

    auto co1 = win->setContainer( new AContainer( win, 1, 5 ), true );
    co1->setMaxSpace( 5 );

    RefCount<AFontWidth> lencalc( new AFontWidth( &m_aguix, NULL ) );
    auto help_ts = std::make_shared< TextStorageString >( catalog.getLocale( 1431 ), lencalc );
    auto help_tv = co1->addWidget( new TextView( &m_aguix,
                                                 0, 0, 400, 50, "", help_ts ),
                                   0, 0, AContainer::CO_INCW );
    help_tv->setLineWrap( true );
    help_tv->showFrame( false );
    help_tv->maximizeYLines( 10, help_tv->getWidth() );
    co1->readLimits();
    help_tv->show();
    help_tv->setAcceptFocus( false );

    TextView::ColorDef tv_cd = help_tv->getColors();
    tv_cd.setBackground( 0 );
    tv_cd.setTextColor( 1 );
    help_tv->setColors( tv_cd );


    auto str1 = AGUIXUtils::formatStringToString( catalog.getLocale( 1228 ), getCurrentPath().c_str() );
    co1->add( new Text( &m_aguix, 0, 0, str1.c_str() ),
              0, 1, AContainer::CO_INCWNR );

    auto co1_1 = co1->add( new AContainer( win, 7, 1 ), 0, 2 );
    co1_1->setMaxSpace( 5 );
    co1_1->setBorderWidth( 0 );

    co1_1->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 1229 ) ),
                0, 0, AContainer::CO_FIXNR + AContainer::ACONT_CENTER );

    auto sg = co1_1->addWidget( new StringGadget( &m_aguix, 0, 0, 5 * m_aguix.getTextWidth( "W" ), "0", 0 ),
                                1, 0, AContainer::CO_FIX );

    co1_1->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 1230 ) ),
                2, 0, AContainer::CO_FIXNR + AContainer::ACONT_CENTER );

    auto weekb = co1_1->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1231 ), 0 ),
                                   3, 0, AContainer::CO_FIX );
    auto monthb = co1_1->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1232 ), 0 ),
                                    4, 0, AContainer::CO_FIX );
    auto yearb = co1_1->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1233 ), 0 ),
                                   5, 0, AContainer::CO_FIX );

    auto nonexistinb_cb = co1->addWidget( new ChooseButton( &m_aguix, 0, 0,
                                                            false,
                                                            catalog.getLocale( 1432 ),
                                                            LABEL_RIGHT, 0 ), 0, 3, AContainer::CO_INCWNR );

    auto co1_2 = co1->add( new AContainer( win, 2, 1 ), 0, 4 );
    co1_2->setMinSpace( 5 );
    co1_2->setMaxSpace( -1 );
    co1_2->setBorderWidth( 0 );
    auto checkb = co1_2->addWidget( new Button( &m_aguix,
                                                0,
                                                0,
                                                catalog.getLocale( 1234 ),
                                                0 ), 0, 0, AContainer::CO_FIX );
    auto cancelb = co1_2->addWidget( new Button( &m_aguix,
                                                 0,
                                                 0,
                                                 catalog.getLocale( 8 ),
                                                 0 ), 1, 0, AContainer::CO_FIX );

    win->contMaximize( true, true );
    win->setDoTabCycling( true );

    win->show();

    AGMessage *msg;
    int endmode = 0;

    for ( ; endmode == 0; ) {
        msg = m_aguix.WaitMessage( NULL );

        if ( msg != NULL ) {
            switch ( msg->type ) {
                case AG_CLOSEWINDOW:
                    if ( msg->closewindow.window == win->getWindow() ) {
                        endmode = -1;
                    }
                    break;
                case AG_BUTTONCLICKED:
                    if ( msg->button.button == weekb ) {
                        auto tstr = AGUIXUtils::formatStringToString( "7" );
                        sg->setText( tstr.c_str() );
                    } else if ( msg->button.button == monthb ) {
                        auto tstr = AGUIXUtils::formatStringToString( "31" );
                        sg->setText( tstr.c_str() );
                    } else if ( msg->button.button == yearb ) {
                        auto tstr = AGUIXUtils::formatStringToString( "365" );
                        sg->setText( tstr.c_str() );
                    } else if ( msg->button.button == checkb ) {
                        endmode = 1;
                    } else if ( msg->button.button == cancelb ) {
                        endmode = -1;
                    }
                    break;
            }
            m_aguix.ReplyMessage( msg );
        }
    }

    if ( endmode == 1 ) {
        char *endptr;
        int t = strtol( sg->getText(), &endptr, 10 );
        if ( *endptr == '\0' && t >= 0 ) {
            if ( cleanupArchive( win, getCurrentPath(), t, nonexistinb_cb->getState() ) != 0 ) {
                endmode = -1;
            }
        } else {
            endmode = -1;
        }
    }
    
    delete win;

    return endmode == 1 ? 0 : 1;
}

int PathJumpUI::prefixDBCleanUp()
{
    auto win = new AWindow( &m_aguix,
                            0, 0,
                            400, 400,
                            catalog.getLocale( 1235 ),
                            AWindow::AWINDOW_DIALOG );
    win->create();

    auto co1 = win->setContainer( new AContainer( win, 1, 2 ), true );
    co1->setMaxSpace( 5 );

    auto co1_1 = co1->add( new AContainer( win, 8, 1 ), 0, 0 );
    co1_1->setMaxSpace( 5 );
    co1_1->setBorderWidth( 0 );

    co1_1->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 1229 ) ),
                0, 0, AContainer::CO_FIXNR + AContainer::ACONT_CENTER );

    auto sg = co1_1->addWidget( new StringGadget( &m_aguix, 0, 0, 5 * m_aguix.getTextWidth( "W" ), "0", 0 ),
                                1, 0, AContainer::CO_FIX );

    co1_1->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 1230 ) ),
                2, 0, AContainer::CO_FIXNR + AContainer::ACONT_CENTER );

    auto weekb = co1_1->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1231 ), 0 ),
                                   3, 0, AContainer::CO_FIX );
    auto monthb = co1_1->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1232 ), 0 ),
                                    4, 0, AContainer::CO_FIX );
    auto yearb = co1_1->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1233 ), 0 ),
                                   5, 0, AContainer::CO_FIX );
    auto neverb = co1_1->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1236 ), 0 ),
                                    6, 0, AContainer::CO_FIX );

    auto co1_2 = co1->add( new AContainer( win, 2, 1 ), 0, 1 );
    co1_2->setMinSpace( 5 );
    co1_2->setMaxSpace( -1 );
    co1_2->setBorderWidth( 0 );
    auto checkb = co1_2->addWidget( new Button( &m_aguix,
                                                0,
                                                0,
                                                catalog.getLocale( 1234 ),
                                                0 ), 0, 0, AContainer::CO_FIX );
    auto cancelb = co1_2->addWidget( new Button( &m_aguix,
                                                 0,
                                                 0,
                                                 catalog.getLocale( 8 ),
                                                 0 ), 1, 0, AContainer::CO_FIX );

    win->contMaximize( true, true );
    win->setDoTabCycling( true );

    win->show();

    AGMessage *msg;
    int endmode = 0;

    for ( ; endmode == 0; ) {
        msg = m_aguix.WaitMessage( NULL );

        if ( msg != NULL ) {
            switch ( msg->type ) {
                case AG_CLOSEWINDOW:
                    if ( msg->closewindow.window == win->getWindow() ) {
                        endmode = -1;
                    }
                    break;
                case AG_BUTTONCLICKED:
                    if ( msg->button.button == weekb ) {
                        auto tstr = AGUIXUtils::formatStringToString( "7" );
                        sg->setText( tstr.c_str() );
                    } else if ( msg->button.button == monthb ) {
                        auto tstr = AGUIXUtils::formatStringToString( "31" );
                        sg->setText( tstr.c_str() );
                    } else if ( msg->button.button == yearb ) {
                        auto tstr = AGUIXUtils::formatStringToString( "365" );
                        sg->setText( tstr.c_str() );
                    } else if ( msg->button.button == neverb ) {
                        auto tstr = AGUIXUtils::formatStringToString( "-1" );
                        sg->setText( tstr.c_str() );
                    } else if ( msg->button.button == checkb ) {
                        endmode = 1;
                    } else if ( msg->button.button == cancelb ) {
                        endmode = -1;
                    }
                    break;
            }
            m_aguix.ReplyMessage( msg );
        }
    }

    if ( endmode == 1 ) {
        char *endptr;
        int t = strtol( sg->getText(), &endptr, 10 );
        if ( *endptr == '\0' && t >= -1 ) {
            if ( cleanupPrefixDB( win, t ) != 0 ) {
                endmode = -1;
            }
        } else {
            endmode = -1;
        }
    }
    
    delete win;

    return endmode == 1 ? 0 : 1;
}

int PathJumpUI::programDBCleanUp()
{
    auto win = new AWindow( &m_aguix,
                            10, 10,
                            400, 400,
                            catalog.getLocale( 1324 ),
                            AWindow::AWINDOW_DIALOG );
    win->create();

    auto co1 = win->setContainer( new AContainer( win, 1, 2 ), true );
    co1->setMaxSpace( 5 );

    auto co1_1 = co1->add( new AContainer( win, 8, 1 ), 0, 0 );
    co1_1->setMaxSpace( 5 );
    co1_1->setBorderWidth( 0 );

    co1_1->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 1229 ) ),
                0, 0, AContainer::CO_FIXNR + AContainer::ACONT_CENTER );

    auto sg = co1_1->addWidget( new StringGadget( &m_aguix, 0, 0, 5 * m_aguix.getTextWidth( "W" ), "0", 0 ),
                                1, 0, AContainer::CO_FIX );

    co1_1->add( new Text( &m_aguix, 0, 0, catalog.getLocale( 1230 ) ),
                2, 0, AContainer::CO_FIXNR + AContainer::ACONT_CENTER );

    auto weekb = co1_1->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1231 ), 0 ),
                                   3, 0, AContainer::CO_FIX );
    auto monthb = co1_1->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1232 ), 0 ),
                                    4, 0, AContainer::CO_FIX );
    auto yearb = co1_1->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1233 ), 0 ),
                                   5, 0, AContainer::CO_FIX );
    auto neverb = co1_1->addWidget( new Button( &m_aguix, 0, 0, catalog.getLocale( 1236 ), 0 ),
                                    6, 0, AContainer::CO_FIX );

    auto co1_2 = co1->add( new AContainer( win, 2, 1 ), 0, 1 );
    co1_2->setMinSpace( 5 );
    co1_2->setMaxSpace( -1 );
    co1_2->setBorderWidth( 0 );
    auto checkb = co1_2->addWidget( new Button( &m_aguix,
                                                0,
                                                0,
                                                catalog.getLocale( 1234 ),
                                                0 ), 0, 0, AContainer::CO_FIX );
    auto cancelb = co1_2->addWidget( new Button( &m_aguix,
                                                 0,
                                                 0,
                                                 catalog.getLocale( 8 ),
                                                 0 ), 1, 0, AContainer::CO_FIX );

    win->contMaximize( true, true );
    win->setDoTabCycling( true );

    win->show();

    AGMessage *msg;
    int endmode = 0;

    for ( ; endmode == 0; ) {
        msg = m_aguix.WaitMessage( NULL );

        if ( msg != NULL ) {
            switch ( msg->type ) {
                case AG_CLOSEWINDOW:
                    if ( msg->closewindow.window == win->getWindow() ) {
                        endmode = -1;
                    }
                    break;
                case AG_BUTTONCLICKED:
                    if ( msg->button.button == weekb ) {
                        auto tstr = AGUIXUtils::formatStringToString( "7" );
                        sg->setText( tstr.c_str() );
                    } else if ( msg->button.button == monthb ) {
                        auto tstr = AGUIXUtils::formatStringToString( "31" );
                        sg->setText( tstr.c_str() );
                    } else if ( msg->button.button == yearb ) {
                        auto tstr = AGUIXUtils::formatStringToString( "365" );
                        sg->setText( tstr.c_str() );
                    } else if ( msg->button.button == neverb ) {
                        auto tstr = AGUIXUtils::formatStringToString( "-1" );
                        sg->setText( tstr.c_str() );
                    } else if ( msg->button.button == checkb ) {
                        endmode = 1;
                    } else if ( msg->button.button == cancelb ) {
                        endmode = -1;
                    }
                    break;
            }
            m_aguix.ReplyMessage( msg );
        }
    }

    if ( endmode == 1 ) {
        char *endptr;
        int t = strtol( sg->getText(), &endptr, 10 );
        if ( *endptr == '\0' && t >= -1 ) {
            if ( cleanupProgramDB( win, t ) != 0 ) {
                endmode = -1;
            }
        } else {
            endmode = -1;
        }
    }
    
    delete win;

    return endmode == 1 ? 0 : 1;
}

int PathJumpUI::cleanup( AWindow *win, const std::string &base, time_t unused_since_days )
{
    std::string msg, bt;

    std::list< std::pair< std::string, time_t > > temp_paths = m_worker.getPathsPers().getPaths(),
        non_existing_entries;

    int non_existing = 0;
    size_t elements = temp_paths.size();
    size_t processed = 0;
    size_t matching_elements = 0;
    time_t last_update = 0;

    std::string orig_win_title = win->getTitle();

    win->setCursor( AGUIX::WAIT_CURSOR );
    m_aguix.Flush();

    for ( auto it1 = temp_paths.begin();
          it1 != temp_paths.end();
          it1++ ) {
        time_t now = time( NULL );

        processed++;

        if ( ! NWC::Path::is_prefix_dir( base,
                                         it1->first ) ) {
            continue;
        }

        if ( it1->second > now ) {
            continue;
        }

        if ( now - it1->second <= unused_since_days * 24 * 60 * 60 ) {
            continue;
        }

        matching_elements++;

        if ( ! NWC::FSEntry( it1->first ).entryExists() ) {
            non_existing_entries.push_back( *it1 );
            non_existing++;
        }

        if ( now != last_update ) {
            last_update = now;

            std::string title = AGUIXUtils::formatStringToString( catalog.getLocale( 1113 ),
                                                                  ((double)processed) * 100.0 / elements );

            win->setTitle( title );
            m_aguix.Flush();
        }
    }

    win->setTitle( orig_win_title );
    win->unsetCursor();

    if ( non_existing > 0 ) {
        msg = AGUIXUtils::formatStringToString( catalog.getLocale( 1427 ),
                                                non_existing, (int)matching_elements );

        bt = catalog.getLocale( 1429 );
        bt += "|";
        bt += catalog.getLocale( 1428 );
        bt += "|";
        bt += catalog.getLocale( 8 );

        int res = win->request( catalog.getLocale( 123 ),
                                msg.c_str(),
                                bt.c_str() );

        if ( res == 0 ) {
            for ( auto it1 = non_existing_entries.begin();
                  it1 != non_existing_entries.end();
                  it1++ ) {
                m_worker.removePathPers( it1->first );
            }
        } else if ( res == 1 ) {
            for ( auto it1 = non_existing_entries.begin();
                  it1 != non_existing_entries.end();
                  it1++ ) {
                m_worker.removePathPers( it1->first );
                m_worker.storePathPersArchive( it1->first, it1->second );
            }
        } else {
            return 1;
        }
    } else {
        win->request( catalog.getLocale( 123 ),
                      catalog.getLocale( 986 ),
                      catalog.getLocale( 633 ) );
        
        return 1;
    }

    return 0;
}

int PathJumpUI::cleanupArchive( AWindow *win, const std::string &base, time_t unused_since_days, bool also_existing )
{
    std::string msg, bt;

    std::list< std::pair< std::string, time_t > > temp_paths = m_worker.getPathsPersArchive().getPaths(),
        cleanup_entries;

    int cleanup = 0;
    size_t elements = temp_paths.size();
    size_t processed = 0;
    size_t matching_elements = 0;
    time_t last_update = 0;

    std::string orig_win_title = win->getTitle();

    win->setCursor( AGUIX::WAIT_CURSOR );
    m_aguix.Flush();

    for ( auto it1 = temp_paths.begin();
          it1 != temp_paths.end();
          it1++ ) {
        time_t now = time( NULL );

        processed++;

        if ( ! NWC::Path::is_prefix_dir( base,
                                         it1->first ) ) {
            continue;
        }

        if ( it1->second > now ) {
            continue;
        }

        if ( now - it1->second <= unused_since_days * 24 * 60 * 60 ) {
            continue;
        }

        matching_elements++;

        if ( also_existing || ! NWC::FSEntry( it1->first ).entryExists() ) {
            cleanup_entries.push_back( *it1 );
            cleanup++;
        }

        if ( now != last_update ) {
            last_update = now;

            std::string title = AGUIXUtils::formatStringToString( catalog.getLocale( 1113 ),
                                                                  ((double)processed) * 100.0 / elements );

            win->setTitle( title );
            m_aguix.Flush();
        }
    }

    win->setTitle( orig_win_title );
    win->unsetCursor();

    if ( cleanup > 0 ) {
        msg = AGUIXUtils::formatStringToString( catalog.getLocale( 1433 ),
                                                cleanup, (int)matching_elements );

        bt = catalog.getLocale( 1429 );
        bt += "|";
        bt += catalog.getLocale( 8 );

        int res = win->request( catalog.getLocale( 123 ),
                                msg.c_str(),
                                bt.c_str() );

        if ( res == 0 ) {
            for ( auto &e : cleanup_entries ) {
                m_worker.removePathPersArchive( e.first );
            }
        } else {
            return 1;
        }
    } else {
        win->request( catalog.getLocale( 123 ),
                      catalog.getLocale( 986 ),
                      catalog.getLocale( 633 ) );
        
        return 1;
    }

    return 0;
}

int PathJumpUI::cleanupPrefixDB( AWindow *win, int unused_since_days )
{
    std::string msg, bt;

    std::list< std::pair< std::string, std::string > > non_existing_entries;

    size_t processed = 0;
    size_t matching_elements = 0;

    time_t now = time( NULL );

    for ( auto it1 = m_best_hits.begin();
          it1 != m_best_hits.end();
          it1++ ) {
        processed++;

        if ( it1->last_access > now ) {
            continue;
        }

        if ( unused_since_days < -1 ) {
            continue;
        }

        if ( unused_since_days == -1 &&
             it1->last_access != 0 ) {
            continue;
        }

        if ( unused_since_days >= 0 &&
             it1->last_access == 0 ) {
            continue;
        }

        if ( unused_since_days >= 0 &&
             now - it1->last_access <= unused_since_days * 24 * 60 * 60 ) {
            continue;
        }

        matching_elements++;
        non_existing_entries.push_back( std::make_pair( it1->filter, it1->path ) );
    }

    if ( ! non_existing_entries.empty() ) {
        msg = AGUIXUtils::formatStringToString( catalog.getLocale( 1238 ),
                                                (int)matching_elements, (int)processed );

        bt = catalog.getLocale( 11 );
        bt += "|";
        bt += catalog.getLocale( 8 );

        int res = win->request( catalog.getLocale( 123 ),
                                msg.c_str(),
                                bt.c_str() );

        if ( res == 0 ) {
            for ( auto it1 = non_existing_entries.begin();
                  it1 != non_existing_entries.end();
                  it1++ ) {
                m_worker.getDirHistPrefixDB().removeEntry( it1->first );
            }
        } else {
            return 1;
        }
    } else {
        win->request( catalog.getLocale( 123 ),
                      catalog.getLocale( 1237),
                      catalog.getLocale( 633 ) );
        
        return 1;
    }

    return 0;
}

int PathJumpUI::cleanupProgramDB( AWindow *win, int unused_since_days )
{
    std::string msg, bt;

    std::list< std::string > removable_entries;

    size_t processed = 0;
    size_t matching_elements = 0;

    time_t now = time( NULL );

    for ( auto &p : m_used_programs ) {
        processed++;

        if ( p.last_access > now ) {
            continue;
        }

        if ( unused_since_days < -1 ) {
            continue;
        }

        if ( unused_since_days == -1 &&
             p.last_access != 0 ) {
            continue;
        }

        if ( unused_since_days >= 0 &&
             p.last_access == 0 ) {
            continue;
        }

        if ( unused_since_days >= 0 &&
             now - p.last_access <= unused_since_days * 24 * 60 * 60 ) {
            continue;
        }

        matching_elements++;
        removable_entries.push_back( p.program );
    }

    if ( ! removable_entries.empty() ) {
        msg = AGUIXUtils::formatStringToString( catalog.getLocale( 1238 ),
                                                (int)matching_elements, (int)processed );

        bt = catalog.getLocale( 11 );
        bt += "|";
        bt += catalog.getLocale( 8 );

        int res = win->request( catalog.getLocale( 123 ),
                                msg.c_str(),
                                bt.c_str() );

        if ( res == 0 ) {
            for ( auto &program : removable_entries ) {
                m_worker.removePathProgramPers( program );
            }
        } else {
            return 1;
        }
    } else {
        win->request( catalog.getLocale( 123 ),
                      catalog.getLocale( 1237),
                      catalog.getLocale( 633 ) );
        
        return 1;
    }

    return 0;
}

int PathJumpUI::resetHeldEntries()
{
    for ( auto &e : m_entries ) {
        e.m_always_ignore = false;

        if ( is_locked_on_directory() ) {
            if ( ! NWC::Path::is_prefix_dir( m_lock_base_dir, e.m_path ) ) {
                e.m_always_ignore = true;
            }
        }
    }

    return 0;
}

void PathJumpUI::resetHeldEntriesAndUpdateUI()
{
    resetHeldEntries();

    if ( m_hold_status.active ) {
        m_show_all_cb->setState( m_hold_status.base_show_hidden_state );
        m_show_all_cb->show();

        m_show_hidden_entries = m_hold_status.base_show_hidden_state;
        m_hold_status.active = false;
        m_hold_status.current_filter = "";
    }

    m_hold_status_b->setText( 0, catalog.getLocale( 1027 ) );
    m_hold_filter_text->setText( "" );

    m_hold_co->readLimits();
    m_lock_dir_co->readLimits();

    m_win->updateCont();

    apply_filter( m_filter, [this]( int matches ) {
            showData();
        });
}

int PathJumpUI::calculateMatchClasses()
{
    std::set< int > match_counts;
    std::list< int > sorted_counts;
    std::map< int, int > entries_per_match;

    for ( auto &e : m_entries ) {
        if ( ( e.m_type == PATH_SHOW ||
               e.m_type == BOOKMARK_SHOW ) &&
             e.m_post_filtering &&
             e.m_always_ignore == false ) {
            match_counts.insert( e.m_blockcount );
            entries_per_match[e.m_blockcount]++;
        }
    }

    for ( auto &c : match_counts ) {
        sorted_counts.push_back( c );
    }

    sorted_counts.sort();

    m_match_classes.clear();

    for ( auto &c : sorted_counts ) {
        m_match_classes.push_back( std::make_pair( c, entries_per_match[c] ) );
    }

    return 0;
}

int PathJumpUI::holdCurrentEntries( int top_x )
{
    std::list< std::pair< int, int > > sorted_counts;

    if ( top_x < 1 ) return 1;

    sorted_counts = m_match_classes;

    if ( top_x < (int)sorted_counts.size() ) {
        sorted_counts.resize( top_x );
    }

    for ( auto &e : m_entries ) {
        if ( ( e.m_type == PATH_SHOW ||
               e.m_type == BOOKMARK_SHOW ) &&
             e.m_post_filtering &&
             e.m_always_ignore == false ) {
            if ( std::find_if( sorted_counts.begin(),
                               sorted_counts.end(),
                               [&e]( std::pair< int, int > &mc ) {
                                   if ( mc.first == e.m_blockcount ) return true;
                                   return false;
                               } ) != sorted_counts.end() ) {
                // blockcount accepted, continue wiht next entry
                continue;
            }
        }

        e.m_always_ignore = true;
    }

    return 0;
}

int PathJumpUI::holdCurrentEntriesAndUpdateUI( int top_x )
{
    if ( ! m_hold_status.active ) {
        m_hold_status.base_show_hidden_state = m_show_hidden_entries;
        m_hold_status.active = true;

        m_show_all_cb->hide();

        m_hold_status_b->setText( 0, catalog.getLocale( 1028 ) );
        m_hold_status_b->resize( m_hold_status_b->getMaximumWidth(),
                                 m_hold_status_b->getHeight() );
    }

    if ( ! m_filter.empty() ) {
        if ( ! m_hold_status.current_filter.empty() ) {
            m_hold_status.current_filter += "+";
        }

        m_hold_status.current_filter += m_filter;
    }

    m_hold_filter_text->setText( m_hold_status.current_filter.c_str() );

    m_hold_co->readLimits();
    m_lock_dir_co->readLimits();

    m_win->updateCont();

    int res = holdCurrentEntries( top_x );

    m_filter = "";

    apply_filter( m_filter, [this]( int matches ) {
            showData();
        });

    return res;
}

int PathJumpUI::handleMatchClassRow( int row )
{
    if ( row == 0 ) {
        m_lv->setActiveRow( 0 );

        handleMainLVRowChange( 0 );

        m_lv->centerActive();
        m_lv->redraw();

        return 0;
    }

    std::string s1 = m_class_counts_lv->getText( row, 0 );

    for ( int mainrow = 0; mainrow < m_lv->getElements(); mainrow++ ) {
        if ( m_lv->getText( mainrow, 0 ) == s1 ) {
            m_lv->setActiveRow( mainrow );

            handleMainLVRowChange( mainrow );

            m_lv->centerActive();
            m_lv->redraw();
            break;
        }
    }

    return 0;
}

int PathJumpUI::handleMainLVRowChange( int row )
{
    try {
        m_current_entry_pos = std::get<3>( m_entry_fullnames.at( row ) );
        set_entry( get_current_entry() );
        showData();
    } catch (...) {
    }

    return 0;
}

int PathJumpUI::handleByFilterLVRowChange( int row )
{
    try {
        m_current_entry_byfilter = m_best_hits.at( m_byfilter_row_to_element.at( row ) ).path;
    } catch (...) {
    }

    return 0;
}

void PathJumpUI::showHoldHelp()
{
    RefCount<AFontWidth> lencalc( new AFontWidth( Worker::getAGUIX(), NULL ) );

    std::string message = catalog.getLocale( 1033 );

    auto ts = std::make_shared< TextStorageString >( message, lencalc );

    std::string buttons = catalog.getLocale( 11 );

    Worker::getRequester()->request( catalog.getLocale( 124 ), ts, buttons.c_str(), m_win.get(), Requester::REQUEST_NONE );
}

std::unique_ptr< NWC::Dir > PathJumpUI::getResultsAsDir()
{
    std::string name = AGUIXUtils::formatStringToString( "pathjump%d", s_pathjump_number++ );

    if ( s_pathjump_number < 0 ) {
        // avoid negative and zero number
        s_pathjump_number = 1;
    }

    std::unique_ptr< NWC::Dir > d( new NWC::VirtualDir( name ) );

    for ( auto &e : m_entry_fullnames ) {
        NWC::FSEntry fse( std::get<0>( e ) );

        if ( fse.entryExists() ) {
            d->add( fse );
        }
    }

    return d;
}

void PathJumpUI::updateLVData( int row, int field,
                               struct FieldListView::on_demand_data &data )
{
    if ( row < 0 ||
         row >= (int)m_entry_fullnames.size() ) {
        return;
    }
    
    if ( field == 3 ) {
        std::string visible_name;
        ExistenceTest::existence_state_t e = { .exists = ExistenceTest::EXISTS_UNKNOWN,
                                               .path_length = 0 };

        try {
            visible_name = std::get<0>( m_entry_fullnames.at( row ) );
            e = std::get<2>( m_entry_fullnames.at( row ) );
        } catch (...) {
        }

        if ( ! visible_name.empty() ) {
            size_t adjust_strike_out = 0;

            if ( m_entry_filter_mode == SHOW_ONLY_DIRECT_SUBDIRS ) {
                size_t p2 = visible_name.find( '/', m_ondemand_info.prefix_filter_length + 1 );
                if ( p2 != std::string::npos ) {
                    std::string s( visible_name, 0, p2 );

                    visible_name = s;
                }
            }

            if ( m_entry_filter_mode != SHOW_ALL &&
                 m_relative_view_mode ) {
                size_t start_pos = 0;

                for ( int d = 0; d < m_current_depth; d++ ) {
                    start_pos = visible_name.find( '/', start_pos );
                    if ( start_pos != std::string::npos ) {
                        start_pos++;
                    }
                }

                if ( start_pos != std::string::npos ) {
                    adjust_strike_out = start_pos;
                    visible_name = std::string( visible_name, start_pos );
                }
            }

            data.text = visible_name;
            data.text_set = true;

            if ( e.exists == ExistenceTest::EXISTS_NO ) {
                data.strike_out = true;
                data.strike_out_set = true;
                data.strike_out_start = e.path_length + 1 - adjust_strike_out;
                data.strike_out_start_set = true;
            }

            if ( ! m_filter.empty() ) {
                data.segments.clear();

                m_ondemand_info.matcher.countNonMatchingBlocks<true>( visible_name, &data.segments );

                if ( data.segments.size() > 1 ) {
                    data.segments_set = true;
                }
            }

            data.done = true;
        }
    } else if ( field == 2 ) {
        ExistenceTest::existence_state_t e = std::get<2>( m_entry_fullnames.at( row ) );

        if ( e.exists == ExistenceTest::EXISTS_UNKNOWN ) {
            std::string path = std::get<0>( m_entry_fullnames.at( row ) );

            bool async_started = false;
            bool do_check = true;

            if ( ! wconfig->getDisableBGCheckPrefix().empty() &&
                 AGUIXUtils::starts_with( path, wconfig->getDisableBGCheckPrefix() ) ) {
                do_check = false;
            }
            
            if ( do_check && ! AsyncJobLimiter::async_job_limit_reached() ) {
                try {
                    auto f = std::async( std::launch::async,
                                         [path] {
                                             AsyncJobLimiter::inc_async_job();
                                             auto res = ExistenceTest::checkPathExistence( path );
                                             AsyncJobLimiter::dec_async_job();

                                             return res;
                                         } );
                    m_existence_tests.push_back( std::make_tuple( std::move( f ), row, true ) );
                    async_started = true;
                } catch ( std::system_error &e ) {
                    std::cout << e.what() << std::endl;
                }
            }

            if ( ! async_started ) {
                std::promise< ExistenceTest::existence_state_t > promise;
                auto f = promise.get_future();

                m_existence_tests.push_back( std::make_tuple( std::move( f ), row, true ) );

                if ( do_check ) {
                    promise.set_value( ExistenceTest::checkPathExistence( path ) );
                } else {
                    promise.set_value( { .exists = ExistenceTest::EXISTS_YES,
                            .path_length = path.size() } );
                }
            }
        }

        if ( e.exists == ExistenceTest::EXISTS_YES ) {
            data.text = "";
            data.text_set = true;
        } else if ( e.exists == ExistenceTest::EXISTS_NO ) {
            data.text = "";
            data.text_set = true;
        } else {
            data.text = "?";
            data.text_set = true;
        }
    } else if ( field == 1 ) {
        time_t diff = 0;
        bool ignore = false;
        
        try {
            diff = std::get<1>( m_entry_fullnames.at( row ) );

            if ( diff == m_oldest_time ||
                 diff == 0 ) {
                ignore = true;
            } else {
                diff = m_current_time - diff;
            }
        } catch (...) {
        }

        std::string t;

        if ( ! ignore ) {
            t = WorkerTime::convert_time_diff_to_string( diff );
        }

        data.text = t;
        data.text_set = true;
    }
}

void PathJumpUI::updateFilterLVData( int row, int field,
                                     struct FieldListView::on_demand_data &data )
{
    if ( row < 0 ||
         row >= (int)m_byfilter_row_to_element.size() ) {
        return;
    }
    
    try {
        auto &e = m_best_hits.at( m_byfilter_row_to_element.at( row ) );

        if ( field == 1 ) {
            time_t diff = 0;
            bool ignore = false;
        
            diff = e.last_access;

            if ( diff == m_oldest_time ||
                 diff == 0 ) {
                ignore = true;
            } else {
                diff = m_current_time - diff;
            }

            std::string t;

            if ( ! ignore ) {
                if ( diff < 60 ) {
                    t = AGUIXUtils::formatStringToString( catalog.getLocale( 1104 ), (int)( diff / 60 ) );
                } else if ( diff < 2 * 60 ) {
                    t = AGUIXUtils::formatStringToString( catalog.getLocale( 1103 ), (int)( diff / 60 ) );
                } else if ( diff < 1 * 60 * 60 ) {
                    t = AGUIXUtils::formatStringToString( catalog.getLocale( 1104 ), (int)( diff / 60 ) );
                } else if ( diff < 2 * 60 * 60 ) {
                    t = AGUIXUtils::formatStringToString( catalog.getLocale( 1105 ), (int)( diff / 60 / 60 ) );
                } else if ( diff < 24 * 60 * 60 ) {
                    t = AGUIXUtils::formatStringToString( catalog.getLocale( 1106 ), (int)( diff / 60 / 60 ) );
                } else if ( diff < 2 * 24 * 60 * 60 ) {
                    t = AGUIXUtils::formatStringToString( catalog.getLocale( 1107 ), (int)( diff / 60 / 60 / 24 ) );
                } else if ( diff < 7 * 24 * 60 * 60 ) {
                    t = AGUIXUtils::formatStringToString( catalog.getLocale( 1108 ), (int)( diff / 60 / 60 / 24 ) );
                } else if ( diff < 2 * 7 * 24 * 60 * 60 ) {
                    t = AGUIXUtils::formatStringToString( catalog.getLocale( 1109 ), (int)( diff / 60 / 60 / 24 / 7 ) );
                } else {
                    t = AGUIXUtils::formatStringToString( catalog.getLocale( 1110 ), (int)( diff / 60 / 60 / 24 / 7 ) );
                }
            }

            data.text = t;
            data.text_set = true;
        } else if ( field == 2 ) {
            if ( e.exists.exists == ExistenceTest::EXISTS_UNKNOWN ) {
                std::string path = e.path;

                bool async_started = false;
                bool do_check = true;

                if ( ! wconfig->getDisableBGCheckPrefix().empty() &&
                     AGUIXUtils::starts_with( path, wconfig->getDisableBGCheckPrefix() ) ) {
                    do_check = false;
                }
            
                if ( do_check && ! AsyncJobLimiter::async_job_limit_reached() ) {
                    try {
                        auto f = std::async( std::launch::async,
                                             [path] {
                                                 AsyncJobLimiter::inc_async_job();
                                                 auto res =  ExistenceTest::checkPathExistence( path );
                                                 AsyncJobLimiter::dec_async_job();

                                                 return res;
                                             } );
                        m_existence_tests_filter_path.push_back( std::make_tuple( std::move( f ), row, true ) );
                        async_started = true;
                    } catch ( std::system_error &e ) {
                        std::cout << e.what() << std::endl;
                    }
                }

                if ( ! async_started ) {
                    std::promise< ExistenceTest::existence_state_t > promise;
                    auto f = promise.get_future();

                    m_existence_tests_filter_path.push_back( std::make_tuple( std::move( f ), row, true ) );

                    if ( do_check ) {
                        promise.set_value( ExistenceTest::checkPathExistence( path ) );
                    } else {
                        promise.set_value( { .exists = ExistenceTest::EXISTS_YES,
                                .path_length = path.size() } );
                    }
                }
            }

            if ( e.exists.exists == ExistenceTest::EXISTS_YES ) {
                data.text = "";
                data.text_set = true;
            } else if ( e.exists.exists == ExistenceTest::EXISTS_NO ) {
                data.text = "";
                data.text_set = true;
            } else {
                data.text = "?";
                data.text_set = true;
            }
        } else if ( field == 3 ) {
            if ( e.exists.exists == ExistenceTest::EXISTS_NO ) {
                data.strike_out = true;
                data.strike_out_set = true;
                data.strike_out_start = e.exists.path_length + 1;
                data.strike_out_start_set = true;
            }
        }
    } catch (...) {
    }
}

void PathJumpUI::updateProgFileLVData( int row, int field,
                                       struct FieldListView::on_demand_data &data )
{
    if ( field != 0 && field != 1 ) {
        return;
    }

    int prog_row = m_lv_progs->getActiveRow();

    if ( ! m_lv_progs->isValidRow( prog_row ) ) {
        return;
    }

    auto current_program = m_lv_progs->getText( prog_row, 0 );

    if ( m_program_paths.count( current_program ) == 0 ) {
        return;
    }

    auto &path_list = m_program_paths[ current_program ];
    int path_list_pos = m_lv_prog_files->getData( row, -1 );

    if ( path_list_pos < 0 ||
         path_list_pos >= (int)path_list.size() ) {
        return;
    }

    try {
        ExistenceTest::existence_state_t e = path_list.at( path_list_pos ).exists;

        if ( field == 0 ) {
            if ( e.exists == ExistenceTest::EXISTS_UNKNOWN ) {
                std::string path = path_list.at( path_list_pos ).path;

                bool async_started = false;
                bool do_check = true;

                if ( ! wconfig->getDisableBGCheckPrefix().empty() &&
                     AGUIXUtils::starts_with( path, wconfig->getDisableBGCheckPrefix() ) ) {
                    do_check = false;
                }
            
                if ( do_check && ! AsyncJobLimiter::async_job_limit_reached() ) {
                    try {
                        auto f = std::async( std::launch::async,
                                             [path] {
                                                 AsyncJobLimiter::inc_async_job();
                                                 auto res = ExistenceTest::checkPathExistence( path );
                                                 AsyncJobLimiter::dec_async_job();

                                                 return res;
                                             } );
                        m_existence_tests_prog_path.push_back( std::make_tuple( std::move( f ), row, true ) );
                        async_started = true;
                    } catch ( std::system_error &e ) {
                        std::cout << e.what() << std::endl;
                    }
                }

                if ( ! async_started ) {
                    std::promise< ExistenceTest::existence_state_t > promise;
                    auto f = promise.get_future();

                    m_existence_tests_prog_path.push_back( std::make_tuple( std::move( f ), row, true ) );

                    if ( do_check ) {
                        promise.set_value( ExistenceTest::checkPathExistence( path ) );
                    } else {
                        promise.set_value( { .exists = ExistenceTest::EXISTS_YES,
                                .path_length = path.size() } );
                    }
                }
            }

            if ( e.exists == ExistenceTest::EXISTS_YES ) {
                data.text = "";
                data.text_set = true;
            } else if ( e.exists == ExistenceTest::EXISTS_NO ) {
                data.text = "";
                data.text_set = true;
            } else {
                data.text = "?";
                data.text_set = true;
            }
        } else if ( field == 1 ) {
            if ( e.exists == ExistenceTest::EXISTS_NO ) {
                data.strike_out = true;
                data.strike_out_set = true;
                data.strike_out_start = e.path_length + 1;
                data.strike_out_start_set = true;
            }
        }
    } catch (...) {
    }
}

void PathJumpUI::updateAgeText()
{
    if ( m_entry_filter_age == 0 ) {
        // nothing
        m_age_text->setText( "---" );
    } else if ( m_entry_filter_age <= 1 * 60 * 60 ) {
        // 1 hour
        std::string t1 = AGUIXUtils::formatStringToString( catalog.getLocale( 1105 ),
                                                           1 );
        m_age_text->setText( t1.c_str() );
    } else if ( m_entry_filter_age <= 23 * 60 * 60 ) {
        // x hours
        std::string t1 = AGUIXUtils::formatStringToString( catalog.getLocale( 1106 ),
                                                           (int)( ( m_entry_filter_age + 60 * 60 - 1 ) / ( 60 * 60 ) ) );
        m_age_text->setText( t1.c_str() );
    } else if ( m_entry_filter_age <= 1 * 24 * 60 * 60 ) {
        // 1 day
        std::string t1 = AGUIXUtils::formatStringToString( catalog.getLocale( 1107 ),
                                                           1 );
        m_age_text->setText( t1.c_str() );
    } else if ( m_entry_filter_age <= 6 * 24 * 60 * 60 ) {
        // x days
        std::string t1 = AGUIXUtils::formatStringToString( catalog.getLocale( 1108 ),
                                                           (int)( ( m_entry_filter_age + 24 * 60 * 60 - 1 ) / ( 24 * 60 * 60 ) ) );
        m_age_text->setText( t1.c_str() );
    } else if ( m_entry_filter_age <= 7 * 24 * 60 * 60 ) {
        // one week
        std::string t1 = AGUIXUtils::formatStringToString( catalog.getLocale( 1109 ),
                                                           1 );
        m_age_text->setText( t1.c_str() );
    } else {
        // x weeks
        std::string t1 = AGUIXUtils::formatStringToString( catalog.getLocale( 1110 ),
                                                           (int)( ( m_entry_filter_age + 7 * 24 * 60 * 60 - 1 ) / ( 7 * 24 * 60 * 60 ) ) );
        m_age_text->setText( t1.c_str() );
    }
}

std::string PathJumpUI::getCurrentPath()
{
    std::string res = "";

    if ( m_k1->getCurOption() == 0 ) {
        if ( m_current_depth <= 0 ) {
            res = "/";
        } else {
            for ( unsigned int i = 0; i < m_current_components.size(); i++ ) {
                if ( (int)i >= m_current_depth ) break;

                res += "/";
                res += m_current_components[i].first;
            }
        }
    } else {
        res = m_current_entry_byfilter;
    }

    return res;
}

void PathJumpUI::moveTests()
{
    for ( auto it = m_existence_tests.begin();
          it != m_existence_tests.end();
          ) {
        std::future_status status = std::get<0>( *it ).wait_for( std::chrono::duration<int>::zero() );

        auto next_it = it;
        next_it++;

        if ( status != std::future_status::ready ) {
            auto f = std::move( std::get<0>( *it ) );

            m_worker.enqueueFuture( std::move( f ) );
        }

        m_existence_tests.erase( it );
        it = next_it;
    }

    for ( auto it = m_existence_tests_prog_path.begin();
          it != m_existence_tests_prog_path.end();
          ) {
        std::future_status status = std::get<0>( *it ).wait_for( std::chrono::duration<int>::zero() );

        auto next_it = it;
        next_it++;

        if ( status != std::future_status::ready ) {
            auto f = std::move( std::get<0>( *it ) );

            m_worker.enqueueFuture( std::move( f ) );
        }

        m_existence_tests_prog_path.erase( it );
        it = next_it;
    }

    for ( auto it = m_existence_tests_filter_path.begin();
          it != m_existence_tests_filter_path.end();
          ) {
        std::future_status status = std::get<0>( *it ).wait_for( std::chrono::duration<int>::zero() );

        auto next_it = it;
        next_it++;

        if ( status != std::future_status::ready ) {
            auto f = std::move( std::get<0>( *it ) );

            m_worker.enqueueFuture( std::move( f ) );
        }

        m_existence_tests_filter_path.erase( it );
        it = next_it;
    }
}

void PathJumpUI::setEntryFilterMode( entry_filter_mode_t m )
{
    m_entry_filter_mode = m;

    switch ( m_entry_filter_mode ) {
        case SHOW_ONLY_SUBDIRS:
            m_filter_mode_cyb->setOption( 1 );
            m_show_relative_paths_cb->show();
            break;
        case SHOW_ONLY_DIRECT_SUBDIRS:
            m_filter_mode_cyb->setOption( 2 );
            m_show_relative_paths_cb->show();
            break;
        default:
            m_filter_mode_cyb->setOption( 0 );
            m_show_relative_paths_cb->hide();
            break;
    }                
}

void PathJumpUI::setShowAllData( bool v )
{
    m_show_hidden_entries = v;
    m_show_all_cb->setState( v );
}

void PathJumpUI::setHideNonExisting( bool v )
{
    m_hide_non_existing = v;
    m_hide_non_existing_cb->setState( v );
    m_byprog_hide_non_existing_cb->setState( v );
}

void PathJumpUI::setStartLockDirectory( const std::string &dir )
{
    set_lock_dir( dir );
}

void PathJumpUI::prepare_entries( bool include_archive,
                                  int &max_path_components )
{
    std::list< std::pair< std::string, time_t > > paths = m_path_store.getPaths();
    auto temp_store = m_worker.getPathsPers();

    m_entries.clear();

    paths.sort();

    for ( std::list< std::pair< std::string, time_t > >::const_iterator it1 = paths.begin();
          it1 != paths.end();
          it1++ ) {
        m_entries.push_back( pathjump_entry( PATH_SHOW, -1, it1->first, it1->second ) );

        temp_store.storePath( it1->first, it1->second, NULL, DeepPathStore::DPS_TS_UPDATE_ALWAYS );
    }

    if ( include_archive ) {
        auto temp_store_archive = m_worker.getPathsPersArchive();
        temp_store_archive.walkPaths( [ &temp_store ]( const std::string &path, size_t depth, time_t t ) {
            temp_store.storePath( path, t, NULL, DeepPathStore::DPS_TS_UPDATE_ALWAYS );
        } );
    }

    for ( std::list< std::string >::const_iterator it1 = m_bookmarks.begin();
          it1 != m_bookmarks.end();
          it1++ ) {
        //TODO it would be slightly faster to check if path is already
        //     stored in temp_store_paths but there is no
        //     corresponding function at the moment
        DeepPathNode::node_change_t changed = DeepPathNode::NODE_UNCHANGED;

        temp_store.storePath( *it1, 0, &changed, DeepPathStore::DPS_TS_UPDATE_NONZERO );
    }

    temp_store.walkPaths( [this, &max_path_components ]( const std::string &path, size_t depth, time_t t ) {
        m_entries.push_back( pathjump_entry( BOOKMARK_HIDE, -1, path, t ) );

        int slashes = depth;

        if ( slashes + 1 > max_path_components ) {
            max_path_components = slashes + 1;
        }

        if ( t != 0 ) {
            if ( m_oldest_time == 0 ||
                 m_oldest_time > t ) {
                m_oldest_time = t;
            }
        }
    } );

    resetHeldEntries();
}

void PathJumpUI::rebuild_breadcrumb( int max_path_components )
{
    auto breadcrumb_co = m_co1->add( new AContainer( m_subwin1, max_path_components + 1, 1 ), 0, 3 );
    delete m_breadcrumb_co;
    m_breadcrumb_co = breadcrumb_co;
    m_breadcrumb_co->setMaxSpace( 5 );

    for ( size_t i = 0; i < m_breadcrumb_buttons.size() && i < (size_t)max_path_components; i++ ) {
        m_breadcrumb_co->add( m_breadcrumb_buttons.at( i ),
                              (int)i, 0, AContainer::CO_INCW );
    }

    for ( size_t i = m_breadcrumb_buttons.size(); i < (size_t)max_path_components; i++ ) {
        Button *b = (Button*)m_breadcrumb_co->add( new Button( &m_aguix,
                                                               0,
                                                               0,
                                                               "",
                                                               0 ), (int)i, 0, AContainer::CO_INCW );
        m_breadcrumb_buttons.push_back( b );
        b->setAcceptFocus( false );
        b->hide();
    }

    for ( size_t i = (size_t)max_path_components; i < m_breadcrumb_buttons.size(); i++ ) {
        delete m_breadcrumb_buttons.at( i );
    }
    m_breadcrumb_buttons.resize( max_path_components );

    m_win->contMaximize( true, false, true );

    showData();
}

void PathJumpUI::update_entries()
{
    int max_path_components = 0;

    m_lv_dirty = true;

    prepare_entries( m_include_archive, max_path_components );

    rebuild_breadcrumb( max_path_components );

    apply_filter( m_filter, [this]( int matches ) {
        showData();
        handleMainLVRowChange( m_lv->getActiveRow() );
    });
}

std::string PathJumpUI::get_current_entry() const
{
    if ( m_current_entry_pos >= m_entries.size() ) return "";

    return m_entries.at( m_current_entry_pos ).m_path;
}

void PathJumpUI::lock_to_current_path()
{
    set_lock_dir( getCurrentPath() );
}

void PathJumpUI::unlock_directory()
{
    set_lock_dir( "" );
}

bool PathJumpUI::is_locked_on_directory() const
{
    return !m_lock_base_dir.empty();
}

void PathJumpUI::set_lock_dir( const std::string &path )
{
    m_lock_base_dir = path;
    m_lock_dir_text->setText( m_lock_base_dir.c_str() );

    if ( path.empty() ) {
        m_lock_dir_status_b->setText( 0, catalog.getLocale( 1027 ) );
    } else {
        m_lock_dir_status_b->setText( 0, catalog.getLocale( 1437 ) );
    }

    m_lock_dir_status_b->maximize( true );

    resetHeldEntriesAndUpdateUI();
}
