/* virtualdirmode.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2012-2026 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 "virtualdirmode.hh"
#include "aguix/acontainer.h"
#include "aguix/fieldlistviewdnd.h"
#include "aguix/awindow.h"
#include "aguix/stringgadget.h"
#include "aguix/karteibutton.h"
#include "aguix/button.h"
#include "aguix/acontainerbb.h"
#include "aguix/choosebutton.h"
#include "nmfilter.hh"
#include "worker_locale.h"
#include "worker.h"
#include "nwc_file.hh"
#include "dmcacheentrynwc.hh"
#include "nwcentryselectionstate.hh"
#include "wconfig.h"
#include "filenameshrinker.hh"
#include "nwc_path.hh"
#include "argclass.hh"
#include "wcfiletype.hh"
#include "simplelist.hh"
#include "startprogop.h"
#include "basic_actions.h"
#include "datei.h"
#include "configheader.h"
#include "configparser.hh"
#include "prefixpathtree.hh"
#include "showdircacheop.h"
#include "pathname_watcher.hh"
#include "bookmarkdbproxy.hh"
#include <set>
#include <algorithm>
#include "run_custom_action.hh"
#include "bookmarkdbentry.hh"
#include "string_completion.hh"
#include "nmrowdata.h"
#include "dnd.h"
#include "renameorder.hh"
#include "changesymlinkorder.hh"
#include "createsymlinkorder.hh"
#include "dirsizeorder.hh"
#include "chmodorder.hh"
#include "chownorder.hh"
#include "chmodowncore.hh"
#include "copyopwin.hh"
#include "copyorder.hh"
#include "flagreplacer.hh"
#include "grouphash.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include "exprfilter.hh"
#include "aguix/utf8.hh"
#include "chtimeorder.hh"
#include "chtimecore.hh"
#include "setfilterop.h"
#include "normalops.h"
#include <utility>
#include <functional>
#include "aguix/icons.hh"
#include "execlass.h"
#include "filterstash.hh"
#include "wpucontext.h"

const char *VirtualDirMode::type = "VirtualDirMode";
int VirtualDirMode::maxfilestrlen = 1;
int VirtualDirMode::maxdirstrlen = 1;
int VirtualDirMode::maxbytesstrlen = 1;
int VirtualDirMode::maxwbytesstrlen = 1;
int VirtualDirMode::maxnochanges = 0;
int VirtualDirMode::maxhiddenfilesstrlen = 1;
int VirtualDirMode::maxhiddendirsstrlen = 1;

const static int DIRWATCHER_TIMEOUT = 1000;

int VirtualDirMode::s_vdir_refine_number = 1;
int VirtualDirMode::s_flatdir_number = 1;
int VirtualDirMode::s_selvdir_number = 1;
int VirtualDirMode::s_genericvdir_number = 1;

class VDMBookmarkCB : public GenericCallback<int>
{
public:
    explicit VDMBookmarkCB( VirtualDirMode *vdm ) : m_vdm( vdm )
    {
    }

    int callback()
    {
        if ( m_vdm ) {
            m_vdm->bookmarksChanged();
        }
        return 0;
    }
private:
    VirtualDirMode *m_vdm;
};

VirtualDirMode::VirtualDirMode( Lister *parent )
    : ListerMode( parent ),
      m_lv( NULL ),
      m_sg( NULL ),
      m_cont( NULL ),
      m_cont2( NULL ),
      m_tab_cont( NULL ),
      m_parent_b( NULL ),
      m_cache_select_b{ NULL, NULL },
      m_tab_b( NULL ),
      m_tab_new( NULL ),
      m_tab_close( NULL ),
      m_info_line_sg( NULL ),
      m_bookmarks_has_been_changed( false ),
      ssh_allow( SSH_ASK ),
      m_vis_changed( true ),
      m_old_lv_yoffset( -1 ),
      m_busy_flag( 0 ),
      m_show_free_space( true ),
      m_update_time( 5 ),
      m_last_fs_update( 0 ),
      m_last_eagain( false ),
      m_current_space_update_ms( 0 ),
      m_lv_ce_valid( false ),
      m_apply_matcher( false ),
      m_activate_search_on_keypress( true ),
      m_path_entry_on_top( false ),
      m_previous_dir_pos( 0 ),
      m_go_to_previous_dir_active( false ),
      m_dummy_dotdot_fse( "/.." ),
      m_show_dotdot( false ),
      m_enable_info_line( true ),
      m_last_info_line_fse( NULL ),
      m_enable_custom_lvb_line( false ),
      m_tabs_loaded( false ),
      m_use_custom_columns( false ),
      m_directory_presets_pt( "/" )
{
    registerCommand( "up", [this]{ command_up(); },
                     catalog.getLocale( 1246 ), FunctionProto::CAT_CURSOR );
    registerCommand( "down", [this]{ command_down(); },
                     catalog.getLocale( 1247 ), FunctionProto::CAT_CURSOR );
    registerCommand( "first", [this]{ command_first(); },
                     catalog.getLocale( 1250 ), FunctionProto::CAT_CURSOR );
    registerCommand( "last", [this]{ command_last(); },
                     catalog.getLocale( 1251 ), FunctionProto::CAT_CURSOR );
    registerCommand( "pageup", [this]{ command_pageup(); },
                     catalog.getLocale( 1252 ), FunctionProto::CAT_CURSOR );
    registerCommand( "pagedown", [this]{ command_pagedown(); },
                     catalog.getLocale( 1253 ), FunctionProto::CAT_CURSOR );
    registerCommand( "selectentry", [this]{ command_selectentry(); },
                     catalog.getLocale(1254), FunctionProto::CAT_SELECTIONS );
    registerCommand( "selectentry", [this]( const std::list< RefCount< ArgClass > > &a ) { command_selectentry( a ); },
                     catalog.getLocale( 1398 ), FunctionProto::CAT_CURSOR,
                     []( auto &current_input ) -> std::string {
                         if ( current_input.empty() ) {
                             return catalog.getLocale( 1479 );
                         }
                         if ( current_input == "p" ) {
                             return "'pageup'/'pagedown'/'p'";
                         }
                         if ( current_input == "-" ) {
                             return catalog.getLocale( 1481 );
                         }
                         if ( current_input == "-p" ) {
                             return catalog.getLocale( 1482 );
                         }
                         if ( AGUIXUtils::starts_with( "pageup", current_input ) &&
                              AGUIXUtils::starts_with( "pagedown", current_input ) ) {
                             return "'pageup'/'pagedown'";
                         }
                         if ( AGUIXUtils::starts_with( "pageup", current_input ) ) {
                             return "'pageup'";
                         }
                         if ( AGUIXUtils::starts_with( "pagedown", current_input ) ) {
                             return "'pagedown'";
                         }
                         return catalog.getLocale( 1485 );
                     } );
    registerCommand( "activate_path_input", [this]{ command_pathinput(); },
                     catalog.getLocale( 1275 ), FunctionProto::CAT_OTHER );
    registerCommand( "parentdir", [this]{ command_parentdir( false ); },
                     catalog.getLocale( 1258 ), FunctionProto::CAT_CURSOR );
    registerCommand( "enter_dir", [this]( const std::list< RefCount< ArgClass > > &a ) { enter_dir( a ); },
                     catalog.getLocale( 1259 ), FunctionProto::CAT_CURSOR,
                     []( auto &current_input ) -> std::string {
                         return catalog.getLocale( 1486 );
                     } );
    registerCommand( "enter_active", [this]{ enter_active(); },
                     catalog.getLocale( 318 ), FunctionProto::CAT_CURSOR );
    registerCommand( "simulate_doubleclick", [this]{ command_simdd(); },
                     catalog.getLocale(1272), FunctionProto::CAT_CURSOR );
    registerCommand( "new_tab", [this]{ newTab(); },
                     catalog.getLocale( 852 ), FunctionProto::CAT_OTHER );
    registerCommand( "close_current_tab", [this]{ closeCurrentTab(); },
                     catalog.getLocale( 853 ), FunctionProto::CAT_OTHER );
    registerCommand( "toggle_lock_current_tab", [this]{ toggleLockCurrentTab(); },
                     catalog.getLocale( 1311 ), FunctionProto::CAT_OTHER );
    registerCommand( "open_current_tab_menu", [this]{ open_current_tab_menu(); },
                     catalog.getLocale( 1310 ), FunctionProto::CAT_OTHER );
    registerCommand( "open_label_popup", [this]{ openLabelPopUp(); },
                     catalog.getLocale( 1312 ), FunctionProto::CAT_OTHER );
    registerCommand( "activate_entry", [this]( const std::list< RefCount< ArgClass > > &a ) { activate_entry( a ); },
                     catalog.getLocale( 1394 ), FunctionProto::CAT_FILELIST,
                     []( auto &current_input ) -> std::string {
                         return catalog.getLocale( 1487 );
                     } );
    registerCommand( "show_cache_entry", [this]( const std::list< RefCount< ArgClass > > &a ) { show_cache_entry( a ); },
                     catalog.getLocale( 1395 ), FunctionProto::CAT_FILELIST,
                     []( auto &current_input ) -> std::string {
                         return catalog.getLocale( 1488 );
                     } );
    registerCommand( "selectall", [this]{ command_selectall(); },
                     catalog.getLocale( 1255 ), FunctionProto::CAT_SELECTIONS );
    registerCommand( "selectnone", [this]{ command_selectnone(); },
                     catalog.getLocale( 1256 ), FunctionProto::CAT_SELECTIONS );
    registerCommand( "invertall", [this]{ command_invertall(); },
                     catalog.getLocale( 1257 ), FunctionProto::CAT_SELECTIONS );
    registerCommand( "switch_to_tab_direction", [this]( const std::list< RefCount< ArgClass > > &a ) { switch_to_tab_direction( a ); },
                     catalog.getLocale( 1396 ), FunctionProto::CAT_OTHER,
                     []( auto &current_input ) -> std::string {
                         return catalog.getLocale( 1489 );
                     } );
    registerCommand( "move_tab_direction", [this]( const std::list< RefCount< ArgClass > > &a ) { move_tab_direction( a ); },
                     catalog.getLocale( 1397 ), FunctionProto::CAT_OTHER,
                     []( auto &current_input ) -> std::string {
                         return catalog.getLocale( 1490 );
                     } );
    registerCommand( "recenter_top_bottom", [this]{ recenterTopBottom(); },
                     catalog.getLocale( 1313 ), FunctionProto::CAT_OTHER );
    registerCommand( "flatten_dir", [this]{ flatten_dir( false ); },
                     catalog.getLocale( 1314 ), FunctionProto::CAT_OTHER );
    registerCommand( "flatten_dir_follow_symlinks", [this]{ flatten_dir( true ); },
                     catalog.getLocale( 1315 ), FunctionProto::CAT_OTHER );
    registerCommand( "go_to_previous_dir", [this]{ go_to_previous_dir(); },
                     catalog.getLocale( 1126 ), FunctionProto::CAT_CURSOR );
    registerCommand( "vdir_from_selected", [this]{ vdir_from_selected(); },
                     catalog.getLocale( 1316 ), FunctionProto::CAT_OTHER );
    registerCommand( "vdir_add_selected_from_other_side", [this]{ vdir_add_selected_from_other_side(); },
                     catalog.getLocale( 1317 ), FunctionProto::CAT_OTHER );
    registerCommand( "vdir_from_script_stack", [this]( const std::list< RefCount< ArgClass > > &a ){ vdir_from_script_stack( a ); },
                     catalog.getLocale( 1406 ), FunctionProto::CAT_OTHER,
                     []( auto &current_input ) -> std::string {
                         return catalog.getLocale( 1491 );
                     } );
    registerCommand( "vdir_add_entry", [this]( const std::list< RefCount< ArgClass > > &a ){ vdir_add_entry( a ); },
                     catalog.getLocale( 1407 ), FunctionProto::CAT_OTHER,
                     []( auto &current_input ) -> std::string {
                         return catalog.getLocale( 1492 );
                     } );
    registerCommand( "show_entry", [this]( const std::list< RefCount< ArgClass > > &a ) { show_entry( a ); },
                     catalog.getLocale( 1389 ), FunctionProto::CAT_CURSOR,
                     []( auto &current_input ) -> std::string {
                         return catalog.getLocale( 1493 );
                     } );
    registerCommand( "copy_files_to_clipboard", [this]{ copy_files_to_clipboard( {} ); },
                     catalog.getLocale( 1459 ), FunctionProto::CAT_FILELIST );
    registerCommand( "copy_files_to_clipboard", [this]( const std::list< RefCount< ArgClass > > &a ) { copy_files_to_clipboard( a ); },
                     catalog.getLocale( 1460 ), FunctionProto::CAT_FILELIST,
                     []( auto &current_input ) -> std::string {
                         if ( current_input.empty() ) {
                             return catalog.getLocale( 1494 );
                         }
                         if ( current_input == "cut" ) {
                             return catalog.getLocale( 1495 );
                         }
                         if ( current_input == "cu" ) {
                             return "'cut'";
                         }
                         if ( current_input == "c" ) {
                             return catalog.getLocale( 1497 );
                         }
                         return catalog.getLocale( 1496 );
                     } );
    registerCommand( "paste_files_from_clipboard", [this]{ paste_files_from_clipboard(); },
                     catalog.getLocale( 1461 ), FunctionProto::CAT_FILELIST );
    registerCommand( "copy_filecontent_to_clipboard", [this]{ copy_filecontent_to_clipboard(); },
                     catalog.getLocale( 1610 ), FunctionProto::CAT_FILELIST );

    registerCommand( "stash_current_filters", [this]{
        parentlister->getWorker()->getFilterStash()->stash( this );
        unsetAllFilters();
    }, catalog.getLocale( 1445 ), FunctionProto::CAT_FILELIST );

    registerCommand( "pop_stashed_filters", [this]{
        parentlister->getWorker()->getFilterStash()->applyPop( this );
    }, catalog.getLocale( 1450 ), FunctionProto::CAT_FILELIST );

    registerCommand( "top_stashed_filters", [this]{
        parentlister->getWorker()->getFilterStash()->applyTop( this );
    }, catalog.getLocale( 1449 ), FunctionProto::CAT_FILELIST );

    registerCommand( "revert_selection", [this]{ revert_selection( {} ); },
                     catalog.getLocale( 1474 ), FunctionProto::CAT_SELECTIONS );
    registerCommand( "revert_selection", [this]( const std::list< RefCount< ArgClass > > &a ) { revert_selection( a ); },
                     catalog.getLocale( 1474 ), FunctionProto::CAT_SELECTIONS,
                     []( auto &current_input ) -> std::string {
                         if ( current_input.empty() ) {
                             return catalog.getLocale( 1484 );
                         }
                         if ( std::isdigit( current_input[0] ) ) {
                             return catalog.getLocale( 1480 );
                         }
                         return catalog.getLocale( 1483 );
                     } );
    registerCommand( "set_custom_attribute", [this]( const std::list< RefCount< ArgClass > > &a ) { set_custom_attribute( a ); },
                     catalog.getLocale( 1537 ), FunctionProto::CAT_FILELIST );
    registerCommand( "clear_custom_attributes", [this]{ clear_custom_attributes(); },
                     catalog.getLocale( 1538 ), FunctionProto::CAT_FILELIST );

    m_shrinker = RefCount< TextShrinker >( new HalfShrinker() );

    m_tab_store.tab_entries.push_back( { NULL, "" } );

    m_max_cache_size = wconfig->getCacheSize();

    m_ft_thread.setWorker( parentlister->getWorker() );
    ft_list_update();
    m_ft_thread.start();
    m_ft_thread.switchOrder( NM_Filetype_Thread::THREAD_RUN );

    m_last_busy_flag_update = time( NULL );

    parentlister->getWorker()->PS_setLifetime( (double)m_update_time );

    m_bookmark_cb = new VDMBookmarkCB( this );
    Worker::getBookmarkDBInstance().registerChangeCallback( m_bookmark_cb );

    m_nonflexible_matcher.setMatchFlexible( false );

    m_dummy_dotdot_fse.resetToDummy();

#ifdef HAVE_LUA
    m_info_line_lua_mode = true;
    m_info_line_content = DEFAULT_INFO_LINE_CONTENT_LUA;
#else
    m_info_line_lua_mode = false;
    m_info_line_content = DEFAULT_INFO_LINE_CONTENT_NOLUA;
#endif

    m_last_no_of_unchecked_entries = 0;

    m_last_explicit_directory_presets.sortmode = m_dir_sort_sets.getSortMode();
    m_last_explicit_directory_presets.show_hidden = m_dir_filter_sets.getShowHidden();
    m_last_explicit_directory_presets.filters = m_dir_filter_sets.getFilters();

    rebuildDirectoryPresets();

    m_lvb_flag_producer = std::make_shared< FlagReplacer::FlagProducerCallback >( this );
    m_lvb_flag_replacer = std::make_shared< FlagReplacer >( m_lvb_flag_producer );

    m_lvb_flag_producer->registerFlag( "indicators", [this]( void *user_data ) { return m_lvb_indicators; } );
    m_lvb_flag_producer->registerFlag( "modeinfo", [this]( void *user_data ) { return m_lvb_modeinfo; } );
    m_lvb_flag_producer->registerFlag( "freespace", [this]( void *user_data ) { return m_free_space_str; } );
    m_lvb_flag_producer->registerFlag( "cdi", [this]( void *user_data ) { return getCustomDirectoryInfo(); } );
}

VirtualDirMode::~VirtualDirMode()
{
    wait_for_bg_copies();

    Worker::getBookmarkDBInstance().unregisterChangeCallback( m_bookmark_cb );
    delete m_bookmark_cb;

    m_ft_thread.switchOrder( NM_Filetype_Thread::THREAD_EXIT );
    m_ft_thread.join();
    ft_list_clear();
    ft_request_list_clear();
    ft_result_list_clear();

    write_current_tabs();
}

void VirtualDirMode::messageHandler( AGMessage *msg )
{
    bool ma = false;
    switch ( msg->type ) {
        case AG_MOUSECLICKED:
            // if ( m_tv->isParent( msg->mouse.window, false ) ) {
            //     ma = true;
            // }
            break;
        case AG_STRINGGADGET_OK:
            if ( m_filtered_search_state.searchmode_on == false ) {
                if ( msg->stringgadget.sg == m_sg ) {
                    std::string tstr1 = m_sg->getText();
                    if ( tstr1[0] == '$' ) {
                        char *tstr2 = NWC::Path::handlePathExt( tstr1.c_str() );
                        tstr1 = tstr2;
                        _freesafe( tstr2 );
                    }
                    showDir( tstr1 );
                }
            }
            break;
        case AG_STRINGGADGET_CANCEL:
            if ( m_filtered_search_state.searchmode_on == false ) {
                if ( msg->stringgadget.sg == m_sg ) {
                    if ( ce.get() ) m_sg->setText( m_filtered_search_state.old_sg_content.c_str() );
                }
                break;
            }
        case AG_STRINGGADGET_ACTIVATE:
            if ( msg->stringgadget.sg == m_sg ) {
                ma = true;

                if ( m_filtered_search_state.searchmode_on == false ) {
                    m_filtered_search_state.old_sg_content = m_sg->getText();
                }
            } else if ( msg->stringgadget.sg == m_info_line_sg ) {
                ma = true;
            }
            break;
        case AG_STRINGGADGET_DEACTIVATE:
            if ( m_filtered_search_state.searchmode_on == true &&
                 msg->stringgadget.sg == m_sg ) {
                finishsearchmode();
            }
            break;
        case AG_STRINGGADGET_CONTENTCHANGE:
            if ( m_filtered_search_state.searchmode_on == true &&
                 msg->stringgadget.sg == m_sg) {
                std::string newtext = getFilterStringFromSG();
                bool reset = true;

                if ( newtext.length() > 0 && newtext[ newtext.length() - 1 ] == '/' ) {
                    // if active entry is a directory, enter it and keep searchmode enabled
                    // otherwise go back to previous string
                    if ( ce.get() ) {
                        auto es = ce->getEntry( ce->getActiveEntryPos() );

                        if ( es ) {
                            auto fse = es->getNWCEntry();

                            if ( fse && fse->isDir( true ) ) {
                                std::string s1 = fse->getFullname();

                                if ( showDir( s1 ) == 0 ) {
                                    // highlight first entry

                                    int row = m_lv->getActiveRow();
                                    if ( ! m_lv->isValidRow( row ) ) {
                                        makeRowActive( ( m_show_dotdot ? 1 : 0 ) );
                                    }

                                    startsearchmode( false, "" );
                                }

                                reset = false;
                            }
                        }
                    }
                } else {
                    if ( shownextentry( newtext.c_str(), false, true ) == true ) {
                        m_filtered_search_state.last_search = newtext;

                        reset = false;
                    }
                }

                if ( reset ) {
                    resetToLastSearchString();
                }
            }
            break;
        case AG_BUTTONCLICKED:
            if ( msg->button.button == m_cache_select_b[0] ) {
                changeCacheEntry( 1 );
                ma = true;
            } else if ( msg->button.button == m_cache_select_b[1] ) { 
                changeCacheEntry( -1 );
                ma = true;
            } else if ( msg->button.button == m_parent_b ) { 
                command_parentdir( true );
                ma = true;
            } else if ( msg->button.button == m_tab_new ) {
                newTab();

                ma = true;
            } else if ( msg->button.button == m_tab_close ) {
                closeCurrentTab();

                ma = true;
            } else if ( checkBreadcrumb( msg ) ) {
                ma = true;
            }
            break;
        case AG_KARTEIBUTTONCLICKED:
            if ( msg->karteibutton.karteibutton == m_tab_b ) {
                ma = true;

                if ( msg->karteibutton.mousebutton == Button3 ) {
                    openTabPopUp( msg->karteibutton.option );
                } else {
                    switchToTab( msg->karteibutton.option );
                }
            }
            break;
        case AG_KEYPRESSED:
            if ( parentlister->isActive() == true ) {
                if ( msg->key.key == XK_Prior && KEYSTATEMASK( msg->key.keystate ) == ControlMask ) {
                    switchToTabDirection( -1 );
                } else if ( msg->key.key == XK_Next && KEYSTATEMASK( msg->key.keystate ) == ControlMask ) {
                    switchToTabDirection( 1 );
                } else if ( msg->key.key == XK_t && KEYSTATEMASK( msg->key.keystate ) == ControlMask ) {
                    newTab();
                } else if ( msg->key.key == XK_w && KEYSTATEMASK( msg->key.keystate ) == ControlMask ) {
                    closeCurrentTab();
                } else if ( m_filtered_search_state.searchmode_on == true &&
                            msg->key.key == XK_Tab &&
                            KEYSTATEMASK( msg->key.keystate ) == 0 ) {
                    if ( ! m_filtered_search_state.last_completion.empty() ) {
                        std::string s = getFilterStringFromSG();

                        if ( ! AGUIXUtils::starts_with( s, "(" ) ) {
                            s += m_filtered_search_state.last_completion;

                            m_sg->setText( s.c_str() );
                            m_sg->setCursor( s.length() );

                            m_filtered_search_state.last_search = s;

                            shownextentry( s.c_str(), false, true );
                        }
                    }
                } else if ( m_filtered_search_state.searchmode_on == true &&
                            msg->key.key == XK_BackSpace ) {
                    if ( KEYSTATEMASK( msg->key.keystate ) == ControlMask &&
                         m_filtered_search_state.last_search.empty() ) {
                        if ( ce.get() ) {
                            char *tstr = NWC::Path::parentDir( ce->getCommonPrefix().c_str(),
                                                               NULL );

                            if ( tstr ) {
                                if ( showDir( tstr ) == 0 ) {
                                    startsearchmode( false, "" );
                                }

                                _freesafe( tstr );
                            }
                        }
                    }
                } else if ( m_filtered_search_state.searchmode_on == false &&
                            m_activate_search_on_keypress == true &&
                            msg->key.keybuf != NULL &&
                            strlen( msg->key.keybuf ) > 0 ) {

                    // skip some special chars like backspace, escape, delete
                    if ( ( strlen( msg->key.keybuf ) == 1 &&
                           std::isprint( msg->key.keybuf[0] ) ) ||
                         strlen( msg->key.keybuf ) > 1 ) {

                        m_filtered_search_state.setCaseSensitive( false );
                        m_filtered_search_state.infix_search = false;

                        startsearchmode( false, "" );

                        m_sg->setText( msg->key.keybuf );
                        m_sg->setCursor( strlen( msg->key.keybuf ) );

                        if ( shownextentry( msg->key.keybuf, false, true ) == true ) {
                            m_filtered_search_state.last_search = msg->key.keybuf;
                        } else {
                            resetToLastSearchString();
                        }
                    }
                }
            }
            break;
        case AG_FIELDLV_PRESSED:
            if ( msg->fieldlv.lv == m_lv ) ma = true;
            break;
        case AG_FIELDLV_HEADERCLICKED:
            if ( msg->fieldlv.lv == m_lv &&
                 msg->fieldlv.button == Button1 ) {
                changeSortModeForField( msg->fieldlv.row );
            }
            break;
        case AG_FIELDLV_DOUBLECLICK:
            if ( msg->fieldlv.lv == m_lv ) {
                ma = true;
                command_simdd();
            }
            break;
        case AG_FIELDLV_ENTRY_PRESSED:
            if ( msg->fieldlv.lv == m_lv ) {
                lv_pressed( msg->fieldlv.row );
            }
            break;
        case AG_FIELDLV_VSCROLL:
        case AG_FIELDLV_HSCROLL:
            if ( msg->fieldlv.lv == m_lv &&
                 parentlister->isActive() == true &&
                 aguix->lastClickModifier( ShiftMask ) ) {
                auto l2 = parentlister->getWorker()->getOtherLister( parentlister );
                auto lm1 = l2->getActiveMode();
                auto vdm = dynamic_cast< VirtualDirMode *>( lm1 );

                if ( vdm ) {
                    if ( msg->type == AG_FIELDLV_VSCROLL ) {
                        vdm->scrollVertToAbs( msg->fieldlv.row );
                    } else {
                        vdm->scrollHorizToAbs( msg->fieldlv.row );
                    }
                }
            }
            break;
        case AG_POPUPMENU_CLICKED:
        case AG_POPUPMENU_ENTRYEDITED:
            if ( msg->popupmenu.menu == m_current_popup_settings.lv_popup_menu ) {
                startLVPopUpAction( msg );
            } else if ( msg->popupmenu.menu == m_current_popup_settings.label_popup_menu.get() ) {
                handleLabelPopUp( msg );
            } else if ( msg->popupmenu.menu == m_current_popup_settings.tab_popup_menu.get() ) {
                handleTabPopUp( msg );
            }
            break;
        case AG_POPUPMENU_CLOSED:
            if ( msg->popupmenu.menu == m_current_popup_settings.lv_popup_menu ||
                 msg->popupmenu.menu == m_current_popup_settings.label_popup_menu.get() ) {
                if ( ! m_current_popup_settings.label_entry_clicked ) {
                    Worker::getKVPStore().setStringValue( "last-selected-label", "" );
                }
            }
            m_current_popup_settings.label_entry_clicked = false;
            break;
        case AG_PASTE_FILE_LIST:
            if ( msg->paste.file_list &&
                 msg->paste.agmessage_data == this ) {
                pasteFileList( *msg->paste.file_list );
            }
            break;
    }
    if ( ma == true ) parentlister->makeActive();

    handle_bg_copy_ops( msg );
}

void VirtualDirMode::messageHandlerInactive( AGMessage *msg )
{
    handle_bg_copy_ops( msg );
    //handle_bg_copy_ops();
}

void VirtualDirMode::on()
{
    int side = parentlister->getSide();
    AGUIXFont *afont = aguix->getFont( wconfig->getFont( 2 + side ).c_str() );
    int sgh = ( afont ? afont->getCharHeight() : aguix->getCharHeight() ) + 8;
    int hbw = aguix->getTextWidth( "<", afont ) + 10;
    int m;
    bool path_on_top = wconfig->getPathEntryOnTop( side );

    m_cont = new AContainer( parentawindow, 1, 5 );
    m_cont->setBorderWidth( 0 );
    m_cont->setMinSpace( 0 );
    m_cont->setMaxSpace( 0 );
    parentlister->setContainer( m_cont );

    // for the text field
    m_cont2 = m_cont->add( new AContainer( parentawindow, 4, 1 ), 0,
                           path_on_top == false ? 3 : 1 );
    m_cont2->setBorderWidth( 0 );
    m_cont2->setMinSpace( 0 );
    m_cont2->setMaxSpace( 0 );

    m_parent_b = new Button( aguix, 0, 0, aguix->getTextWidth( "..", afont ) + 10, "..", 5 );
    m_parent_b->setBubbleHelpText( catalog.getLocale( 1125 ) );
    m_cache_select_b[0] = new Button( aguix, 0, 0, hbw, "<", 2 );
    m_cache_select_b[0]->setBubbleHelpText( catalog.getLocale( 1126 ) );
    m_cache_select_b[1] = new Button( aguix, 0, 0, hbw, ">", 3 );
    m_cache_select_b[1]->setBubbleHelpText( catalog.getLocale( 1127 ) );

    // set max height for breadcrumb
    if ( m_breadcrumb.enabled ) {
        m_cont->setMaxHeight( m_parent_b->getHeight() + 2 * 2, 0 ,0 );
    } else {
        m_cont->setMaxHeight( 0, 0 ,0 );
    }

    m_tab_cont = m_cont->add( new AContainer( parentawindow, 3, 1 ), 0,
                              path_on_top == false ? 1 : 2 );
    m_tab_cont->setBorderWidth( 0 );
    m_tab_cont->setMinSpace( 0 );
    m_tab_cont->setMaxSpace( 0 );

    m_tab_b = (KarteiButton*)m_tab_cont->add( new KarteiButton( aguix,
                                                                0, 0,
                                                                100, 0 ),
                                              1, 0,
                                              AContainer::ACONT_MINH + AContainer::ACONT_MAXH );
    m_tab_b->setAcceptFocus( false );

    m_tab_b->setTextShrinker( m_shrinker );
    m_tab_b->setFont( wconfig->getFont( 2 + side ).c_str() );

    m_tab_new = (Button*)m_tab_cont->add( new Button( aguix, 0, 0,
                                                      "", 0 ),
                                          0, 0,
                                          AContainer::ACONT_MINW + AContainer::ACONT_MAXW );
    m_tab_new->setAcceptFocus( false );
    m_tab_new->setFont( wconfig->getFont( 2 + side ).c_str() );
    m_tab_new->resize( aguix_icon_plus_width + 5, m_tab_new->getHeight() );
    m_tab_new->setBubbleHelpText( catalog.getLocale( 1129 ) );
    m_tab_new->setIcon( aguix_icon_plus, aguix_icon_plus_len,
                        Button::ICON_LEFT );

    m_tab_close = (Button*)m_tab_cont->add( new Button( aguix, 0, 0,
                                                        "", 0 ),
                                            2, 0,
                                            AContainer::ACONT_MINW + AContainer::ACONT_MAXW );
    m_tab_close->setAcceptFocus( false );
    m_tab_close->setFont( wconfig->getFont( 2 + side ).c_str() );
    m_tab_close->resize( aguix_icon_cross_width + 5, m_tab_new->getHeight() );
    m_tab_close->setBubbleHelpText( catalog.getLocale( 1130 ) );
    m_tab_close->setIcon( aguix_icon_cross, aguix_icon_cross_len,
                          Button::ICON_LEFT );

    m_tab_cont->readLimits();

    m_lv = (FieldListViewDND*)m_cont->add( new FieldListViewDND( aguix, 0, 0, 50, 50, 0 ),
                                           0,
                                           path_on_top == false ? 2 : 3  );

    m_lv->setShowHeader( wconfig->getShowHeader( side ) );

    m_lv->setAcceptFocus( false );

    m_lv->setMBG( aguix->getFaces().getColor( "dirview-bg" ) );

    m = ( wconfig->getHBarTop( side ) == true ) ? 1 : 2;
    m_lv->setHBarState( m );

    m = ( wconfig->getVBarLeft( side ) == true ) ? 1 : 2;
    m_lv->setVBarState( m );

    m = wconfig->getHBarHeight( side );
    m_lv->setHBarHeight( m );

    m = wconfig->getVBarWidth( side );
    m_lv->setVBarWidth( m );

    m_lv->setFont( wconfig->getFont( 2 + side ).c_str() );
    m_lv->setHeaderFG( aguix->getFaces().getColor( "dirview-header-fg" ) );
    m_lv->setHeaderBG( aguix->getFaces().getColor( "dirview-header-bg" ) );

    int space_width = aguix->getTextWidth( " ", afont );

    m_lv->setGlobalFieldSpace( space_width );

    m_lv->setSelectHandler( [this]( FieldListView *lv, int element) { lv_select_handler( lv, element ); } );
    m_lv->setDNDDataCallback( []( FieldListViewDND *lv, const FieldLVRowData *rowdata ) -> std::string {
            auto *rdp = dynamic_cast< const NMRowData *>( rowdata );
            if ( rdp ) {
                return AGUIXUtils::escape_uri( rdp->getFullname() );
            }
            return "";
        } );

    m_lv->setShowSelectionInVBar( true );

    // now the text field container

    m_parent_b->resize( m_parent_b->getWidth(), sgh );
    m_cont2->add( m_parent_b, ( side == 0 ) ? 0 : 3, 0, AContainer::CO_FIX );
    m_parent_b->setAcceptFocus( false );
    m_parent_b->setFont( wconfig->getFont( 2 + side ).c_str() );
  
    m_cache_select_b[0]->resize( m_cache_select_b[0]->getWidth(), sgh );
    m_cont2->add( m_cache_select_b[0], ( side == 0 ) ? 1 : 0, 0, AContainer::CO_FIX );
    m_cache_select_b[0]->setAcceptFocus( false );
    m_cache_select_b[0]->setFont( wconfig->getFont( 2 + side ).c_str() );
  
    m_cache_select_b[1]->resize( m_cache_select_b[1]->getWidth(), sgh );
    m_cont2->add( m_cache_select_b[1], ( side == 0 ) ? 3 : 2, 0, AContainer::CO_FIX );
    m_cache_select_b[1]->setAcceptFocus( false );
    m_cache_select_b[1]->setFont( wconfig->getFont( 2 + side ).c_str() );

    m_sg = (StringGadget*)m_cont2->add( new StringGadget( aguix, 0, 0, 50, "", 0 ),
                                        ( side == 0 ) ? 2 : 1, 0 );

    m_sg->setAcceptFocus( false );
    m_sg->resize( m_sg->getWidth(), sgh );
    m_sg->setFont( wconfig->getFont( 2 + side ).c_str() );

    m_info_line_sg = (StringGadget*)m_cont->add( new StringGadget( aguix, 0, 0, 50, sgh, "", 0 ),
                                                 0, 4, AContainer::CO_INCW );
    
    m_info_line_sg->setAcceptFocus( false );
    m_info_line_sg->setReadOnly( true );
    m_info_line_sg->setFont( wconfig->getFont( 2 + side ).c_str() );

    if ( ! m_enable_info_line ) {
        m_info_line_sg->hide();
        m_cont->setMinHeight( 0, 0, 4 );
        m_cont->setMaxHeight( 0, 0, 4 );
    }

    parentlister->setActiveMode( this );
    setName();
    updateName();

    setupLVFields();

    updateTabs();

    m_lv_ce_valid = false;

    rebuildView();

    parentawindow->updateCont();

    m_path_entry_on_top = path_on_top;

    update_breadcrumb_gui();

    restore_tabs();
}

void VirtualDirMode::off()
{
    parentlister->setLastPath( getCurrentDirectory() );

    if ( m_lv && ce.get() ) {
        ce->setXOffset( m_lv->getXOffset() );
        ce->setYOffset( m_lv->getYOffset() );
    }

    storeFieldWidths();

    finishsearchmode();

    freePopUpMenus();
  
    if ( m_lv ) {
        delete m_lv;
    }
    m_lv = NULL;

    delete m_cache_select_b[0];
    m_cache_select_b[0] = NULL;
    delete m_cache_select_b[1];
    m_cache_select_b[1] = NULL;
    delete m_sg;
    m_sg = NULL;
    delete m_info_line_sg;
    m_info_line_sg = NULL;
    delete m_parent_b;
    m_parent_b = NULL;

    delete m_tab_b;
    m_tab_b = NULL;
    delete m_tab_new;
    m_tab_new = NULL;
    delete m_tab_close;
    m_tab_close = NULL;

    for ( auto &b : m_breadcrumb.buttons ) {
        delete b;
    }
    m_breadcrumb.buttons.clear();

    parentlister->setContainer( NULL );

    if ( m_cont ) {
        delete m_cont;
    }
    m_cont = m_cont2 = NULL;
    m_tab_cont = NULL;
    m_breadcrumb.current_co = NULL;

    parentlister->setActiveMode( NULL );
    parentlister->setName( "" );

    if ( parentlister->getFocus() == true ) {
        parentlister->getWorker()->setTitle(NULL);
    }
}

void VirtualDirMode::activate()
{
    if ( ce.get() != NULL ) {
        if ( ce->getActiveEntryPos() >= 0 ) {
            int row = 0;

            while ( row < m_lv->getElements() ) {
                if ( m_lv->getData( row, -1 ) == ce->getActiveEntryPos() ) {
                    m_lv->setActiveRow( row );
                    break;
                }
                row++;
            }
        }
    }

    showCacheState();
    parentlister->getWorker()->setTitle( m_sg->getText() );

    parentlister->getWorker()->showNextHint();
}

void VirtualDirMode::deactivate()
{
    int row = m_lv->getActiveRow();
    if ( row >= 0 ) {
        m_lv->setActiveRow( -1);
    }

    finishsearchmode();
    parentlister->getWorker()->setTitle( NULL );
}

bool VirtualDirMode::isType( const char *str )
{
    if ( strcmp( str, type ) == 0 ) return true; else return false;
}

const char *VirtualDirMode::getType()
{
    return type;
}

const char *VirtualDirMode::getStaticType()
{
    return type;
}

void VirtualDirMode::resetState()
{
    m_ft_thread.switchOrder( NM_Filetype_Thread::THREAD_STOP );

    // update filetype list
    ft_list_update();
    
    // clear lists
    ft_request_list_clear();
    ft_result_list_clear();
    
    m_ft_thread.switchOrder( NM_Filetype_Thread::THREAD_RUN );

    for ( auto &cache_entry_pair : m_cache ) {
        cache_entry_pair.second->resetFiletypesAndColors();
        cache_entry_pair.second->checkForDCD();
    }


    if ( parentlister->getActiveMode() == this ) {
        setupLVFields();
    }

    bookmarksChanged();
    updateOnBookmarkChange();

    rebuildDirectoryPresets();
}

void VirtualDirMode::cyclicfunc( cyclicfunc_mode_t mode )
{
    if ( mode == CYCLICFUNC_MODE_NORMAL ) {
        handle_bg_copy_ops();

        if ( ce.get() != NULL ) {
            if ( m_enable_custom_lvb_line &&
                 ! ce->customDirectoryInfoPending() &&
                 ce->customDirectoryInfoState() == DMCacheEntryNWC::CDI_RESULT_AVAILABLE ) {
                updateName();
            }

            updateFiletypeCheck();

            showFreeSpace(false);

            updateOnBookmarkChange();

            if ( m_dirwatcher.timer_enabled &&
                 time( NULL ) != m_dirwatcher.last_update ) {
                parentlister->getWorker()->unregisterTimeout( DIRWATCHER_TIMEOUT );

                m_dirwatcher.timer_enabled = false;

                if ( ce.get() != NULL && ce->isRealDir() ) {
                    if ( worker_access( ce->getNameOfDir().c_str(), R_OK | X_OK ) == 0 ) {
                        // enable watch again to get next change
                        // before actual update so we don't miss any event
                        enableDirWatch();

                        update( false, true );
                    }
                }
            }
        }

        update_info_line();

        if ( m_delayed_filetype_action.pending == false && m_delayed_filetype_action.cb ) {
            m_delayed_filetype_action.cb();

            m_delayed_filetype_action.cb = nullptr;
        }
    } else {
        resetState();
    }
}

void VirtualDirMode::cyclicfuncInactive( cyclicfunc_mode_t mode )
{
    if ( mode == CYCLICFUNC_MODE_NORMAL ) {
        handle_bg_copy_ops();
    } else {
        resetState();
    }
}

const char* VirtualDirMode::getLocaleName()
{
    return getStaticLocaleName();
}

const char* VirtualDirMode::getStaticLocaleName()
{
    return catalog.getLocale( 1026 );
}

int VirtualDirMode::load()
{
    int found_error = 0;
    int tsortmode;
    NM_Filter *tfi;
    std::string str1;
    std::list<NM_Filter> tfilters;

    tsortmode = SORT_NAME;
    for (;;) {
        if ( worker_token == HIDDENFILES_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token == SHOW_WCP ) {
                setShowHidden( true );
            } else if ( worker_token == HIDE_WCP ) {
                setShowHidden( false );
            } else {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == SORTBY_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token == SIZE_WCP ) {
                tsortmode = SORT_SIZE | ( tsortmode & ~0xff );
            } else if ( worker_token == ACCTIME_WCP ) {
                tsortmode = SORT_ACCTIME | ( tsortmode & ~0xff );
            } else if ( worker_token == MODTIME_WCP ) {
                tsortmode = SORT_MODTIME | ( tsortmode & ~0xff );
            } else if ( worker_token == CHGTIME_WCP ) {
                tsortmode = SORT_CHGTIME | ( tsortmode & ~0xff );
            } else if ( worker_token == TYPE_WCP ) {
                tsortmode = SORT_TYPE | ( tsortmode & ~0xff );
            } else if ( worker_token == OWNER_WCP ) {
                tsortmode = SORT_OWNER | ( tsortmode & ~0xff );
            } else if ( worker_token == INODE_WCP ) {
                tsortmode = SORT_INODE | ( tsortmode & ~0xff );
            } else if ( worker_token == NLINK_WCP ) {
                tsortmode = SORT_NLINK | ( tsortmode & ~0xff );
            } else if ( worker_token == PERMISSION_WCP ) {
                tsortmode = SORT_PERMISSION | ( tsortmode & ~0xff );
            } else if ( worker_token == EXTENSION_WCP ) {
                tsortmode = SORT_EXTENSION | ( tsortmode & ~0xff );
            } else if ( worker_token == CUSTOMATTRIBUTE_WCP ) {
                tsortmode = SORT_CUSTOM_ATTRIBUTE | ( tsortmode & ~0xff );
            } else if ( worker_token == NAME_WCP ) {
                tsortmode = SORT_NAME | ( tsortmode & ~0xff );
            } else {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == SORTFLAG_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token == REVERSE_WCP ) {
                tsortmode |= SORT_REVERSE;
            } else if ( worker_token == DIRLAST_WCP ) {
                tsortmode |= SORT_DIRLAST;
                tsortmode &= ~SORT_DIRMIXED;
            } else if ( worker_token == DIRMIXED_WCP ) {
                tsortmode |= SORT_DIRMIXED;
                tsortmode &= ~SORT_DIRLAST;
            } else {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == SHOWFREESPACE_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token == YES_WCP ) {
                m_show_free_space = true;
            } else if ( worker_token == NO_WCP ) {
                m_show_free_space = false;
            } else {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == UPDATETIME_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token == NUM_WCP ) {
                setUpdatetime( yylval.num );
            } else {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == FILTER_WCP ) {
            readtoken();

            if ( worker_token != LEFTBRACE_WCP ) {
                found_error = 1;
                break;
            }
            readtoken();

            tfi = new NM_Filter();
            if ( tfi->load() == 0 ) {
                tfilters.push_back( *tfi );
            } else {
                delete tfi;
                found_error = 1;
                break;
            }
            delete tfi;

            if ( worker_token != RIGHTBRACE_WCP ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == FIELD_WIDTH_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != STRING_WCP ) {
                found_error = 1;
                break;
            }

            str1 = yylval.strptr;

            readtoken();
            if ( worker_token != ',' ) {
                found_error = 1;
                break;
            }
            readtoken();
            if ( worker_token != NUM_WCP ) {
                found_error = 1;
                break;
            }
            m_field_name_to_width[str1] = yylval.num;

            readtoken();
            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == QUICKSEARCHENABLED_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token == YES_WCP ) {
                //quicksearch_enabled = true;
            } else if ( worker_token == NO_WCP ) {
                //quicksearch_enabled = false;
            } else {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == FILTEREDSEARCHENABLED_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token == YES_WCP ) {
                //setFilteredSearchEnabled( true );
            } else if ( worker_token == NO_WCP ) {
                //setFilteredSearchEnabled( false );
            } else {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == WATCHMODE_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token == YES_WCP ) {
                setWatchMode( true );
            } else if ( worker_token == NO_WCP ) {
                setWatchMode( false );
            } else {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == SEARCHMODEONKEYPRESS_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token == YES_WCP ) {
                setActivateSearchModeOnKeyPress( true );
            } else if ( worker_token == NO_WCP ) {
                setActivateSearchModeOnKeyPress( false );
            } else {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == SHOWDOTDOT_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token == YES_WCP ) {
                setShowDotDot( true );
            } else if ( worker_token == NO_WCP ) {
                setShowDotDot( false );
            } else {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == SHOWBREADCRUMB_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token == YES_WCP ) {
                setShowBreadcrumb( true );
            } else if ( worker_token == NO_WCP ) {
                setShowBreadcrumb( false );
            } else {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == ENABLE_INFO_LINE_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token == YES_WCP ) {
                setEnableInfoLine( true );
            } else if ( worker_token == NO_WCP ) {
                setEnableInfoLine( false );
            } else {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == INFO_LINE_LUA_MODE_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token == YES_WCP ) {
                setInfoLineLUAMode( true );
            } else if ( worker_token == NO_WCP ) {
                setInfoLineLUAMode( false );
            } else {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == INFO_LINE_CONTENT_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != STRING_WCP ) {
                found_error = 1;
                break;
            }

            m_info_line_content = yylval.strptr;

            readtoken();
            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == ENABLE_CUSTOM_LVB_LINE_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token == YES_WCP ) {
                setEnableCustomLVBLine( true );
            } else if ( worker_token == NO_WCP ) {
                setEnableCustomLVBLine( false );
            } else {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == CUSTOM_LVB_LINE_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != STRING_WCP ) {
                found_error = 1;
                break;
            }

            setCustomLVBLine( yylval.strptr );

            readtoken();
            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else if ( worker_token == CUSTOM_DIRECTORY_INFO_COMMAND_WCP ) {
            readtoken();

            if ( worker_token != '=' ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( worker_token != STRING_WCP ) {
                found_error = 1;
                break;
            }

            setCustomeDirectoryInfoCommand( yylval.strptr );

            readtoken();
            if ( worker_token != ';' ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else {
            break;
        }
    }
    setSortmode( tsortmode );
    setFilters( tfilters );
  
    return found_error;
}

bool VirtualDirMode::save( Datei *fh )
{
    storeFieldWidths();

    if ( fh == NULL ) return false;
 
    fh->configPutPair( "hiddenfiles" , ( m_dir_filter_sets.getShowHidden() == true ) ? "show" : "hide" );

    int sortmode = m_dir_sort_sets.getSortMode();

    switch ( sortmode & 0xff ) {
        case SORT_SIZE:
            fh->configPutPair( "sortby", "size" );
            break;
        case SORT_ACCTIME:
            fh->configPutPair( "sortby", "acctime" );
            break;
        case SORT_MODTIME:
            fh->configPutPair( "sortby", "modtime" );
            break;
        case SORT_CHGTIME:
            fh->configPutPair( "sortby", "chgtime" );
            break;
        case SORT_TYPE:
            fh->configPutPair( "sortby", "type" );
            break;
        case SORT_OWNER:
            fh->configPutPair( "sortby", "owner" );
            break;
        case SORT_INODE:
            fh->configPutPair( "sortby", "inode" );
            break;
        case SORT_NLINK:
            fh->configPutPair( "sortby", "nlink" );
            break;
        case SORT_PERMISSION:
            fh->configPutPair( "sortby", "permission" );
            break;
        case SORT_EXTENSION:
            fh->configPutPair( "sortby", "extension" );
            break;
        case SORT_CUSTOM_ATTRIBUTE:
            fh->configPutPair( "sortby", "customattribute" );
            break;
        default:
            fh->configPutPair( "sortby", "name" );
            break;
    }
    if ( ( sortmode & SORT_REVERSE ) != 0 ) fh->configPutPair( "sortflag", "reverse" );
    if ( ( sortmode & SORT_DIRLAST ) != 0 ) fh->configPutPair( "sortflag", "dirlast" );
    else if ( ( sortmode & SORT_DIRMIXED ) != 0 ) fh->configPutPair( "sortflag", "dirmixed" );
  
    fh->configPutPairBool( "showfreespace", m_show_free_space );
    fh->configPutPairNum( "updatetime", m_update_time );

    //fh->configPutPairBool( "filteredsearchenabled", _filtered_search_enabled );

    if ( m_dirwatcher.watch_mode == false ) {
        fh->configPutPairBool( "watchmode", m_dirwatcher.watch_mode );
    }

    if ( m_activate_search_on_keypress == false ) {
        fh->configPutPairBool( "searchmodeonkeypress", m_activate_search_on_keypress );
    }

    if ( m_show_dotdot == true ) {
        fh->configPutPairBool( "showdotdot", m_show_dotdot );
    }

    if ( m_breadcrumb.enabled == false ) {
        fh->configPutPairBool( "showbreadcrumb", m_breadcrumb.enabled );
    }

    if ( m_enable_info_line == false ) {
        fh->configPutPairBool( "enableinfoline", m_enable_info_line );
    }

#ifdef HAVE_LUA
    if ( m_info_line_lua_mode == false ) {
        fh->configPutPairBool( "infolineluamode", m_info_line_lua_mode );
    }
#else
    if ( m_info_line_lua_mode == true ) {
        fh->configPutPairBool( "infolineluamode", m_info_line_lua_mode );
    }
#endif

    if ( ( m_info_line_lua_mode == true && m_info_line_content != DEFAULT_INFO_LINE_CONTENT_LUA ) ||
         ( m_info_line_lua_mode == false && m_info_line_content != DEFAULT_INFO_LINE_CONTENT_NOLUA )) {
        fh->configPutPairString( "infolinecontent", m_info_line_content.c_str() );
    }

    if ( m_enable_custom_lvb_line == true ) {
        fh->configPutPairBool( "enablecustomlvbline", m_enable_custom_lvb_line );
    }
    if ( ! m_custom_lvb_line.empty() ) {
        fh->configPutPairString( "customlvbline", m_custom_lvb_line.c_str() );
    }
    if ( ! m_custom_directory_info_command.empty() ) {
        fh->configPutPairString( "customdirectoryinfocommand", m_custom_directory_info_command.c_str() );
    }

    for ( auto &p : m_field_name_to_width ) {
        char numbuf[A_BYTESFORNUMBER( int )];

        sprintf( numbuf, "%d", p.second );

        std::string str1 = "field_width = ";
        str1 += "\"";
        str1 += p.first;
        str1 += "\"";
        str1 += ",";
        str1 += numbuf;
        str1 += ";";
        fh->configPutInfo( str1.c_str(), false );
    }

    std::list<NM_Filter> filters = m_dir_filter_sets.getFilters();
    std::list<NM_Filter>::iterator fil_it;

    for ( fil_it = filters.begin();
          fil_it != filters.end();
          fil_it++ ) {
        fh->configOpenSection( "filter" );
        (*fil_it).save( fh );
        fh->configCloseSection(); //filter
    }

    // write tab if mode is always or as exit state
    if ( wconfig->getStoreTabsMode() == WConfig::STORE_TABS_ALWAYS ||
         wconfig->getStoreTabsMode() == WConfig::STORE_TABS_AS_EXIT_STATE ) {
        write_current_tabs( true );
    }

    return false;
}

void VirtualDirMode::relayout()
{
    int side = parentlister->getSide();

    if ( wconfig->getPathEntryOnTop( side ) != m_path_entry_on_top ) {
        off();
        on();
        return;
    }

    AGUIXFont *afont = aguix->getFont( wconfig->getFont( 2 + side ).c_str() );
    int sgh = ( ( afont != NULL ) ? afont->getCharHeight() : aguix->getCharHeight() ) + 8;
    int hbw = aguix->getTextWidth( "<", afont ) + 10;

    storeFieldWidths();

    m_lv->setMBG( aguix->getFaces().getColor( "dirview-bg" ) );
    m_lv->setHBarState( ( wconfig->getHBarTop( side ) == true ) ? 1 : 2 );
    m_lv->setVBarState( ( wconfig->getVBarLeft( side ) == true ) ? 1 : 2 );
    m_lv->setHBarHeight( wconfig->getHBarHeight( side ) );
    m_lv->setVBarWidth( wconfig->getVBarWidth( side ) );
    m_lv->setShowHeader( wconfig->getShowHeader( side ) );
    m_lv->setFont( wconfig->getFont( 2 + side ).c_str() );
    m_sg->setFont( wconfig->getFont( 2 + side ).c_str() );
    if ( m_info_line_sg ) {
        m_info_line_sg->setFont( wconfig->getFont( 2 + side ).c_str() );
    }
    m_cache_select_b[0]->setFont( wconfig->getFont( 2 + side ).c_str() );
    m_cache_select_b[1]->setFont( wconfig->getFont( 2 + side ).c_str() );
    m_parent_b->setFont( wconfig->getFont( 2 + side ).c_str() );
    m_lv->setHeaderFG( aguix->getFaces().getColor( "dirview-header-fg" ) );
    m_lv->setHeaderBG( aguix->getFaces().getColor( "dirview-header-bg" ) );
  
    m_sg->resize( m_sg->getWidth(), sgh );
    if ( m_info_line_sg ) {
        m_info_line_sg->resize( m_info_line_sg->getWidth(), sgh );
    }
    m_cache_select_b[0]->resize( hbw, sgh );
    m_cache_select_b[1]->resize( hbw, sgh );
    m_parent_b->resize( aguix->getTextWidth( "..", afont ) + 10, sgh );

    m_tab_b->setFont( wconfig->getFont( 2 + side ).c_str() );
    m_tab_new->setFont( wconfig->getFont( 2 + side ).c_str() );
    m_tab_new->resize( aguix_icon_plus_width + 5, m_tab_new->getHeight() );
    m_tab_close->setFont( wconfig->getFont( 2 + side ).c_str() );
    m_tab_close->resize( aguix_icon_cross_width + 5, m_tab_new->getHeight() );
    m_tab_cont->readLimits();

    m_cont->readLimits();
    m_cont2->readLimits();

    // info line is INCW so fix min width again
    m_cont->setMinWidth( 50, 0, 4 );
    if ( ! m_enable_info_line ) {
        m_cont->setMinHeight( 0, 0, 4 );
        m_cont->setMaxHeight( 0, 0, 4 );
    }

    if ( m_breadcrumb.enabled ) {
        for ( auto &b : m_breadcrumb.buttons ) {
            b->resize( b->getMaximumWidth(),
                       b->getPreferredHeight() );
        }

        update_breadcrumb_gui();
    }

    parentawindow->updateCont();
    m_lv->centerActive();
}

void VirtualDirMode::command_up()
{
    command_up( false, false );
}

void VirtualDirMode::command_up( bool keep_search_mode, bool wrap_around )
{
    if ( ce.get() == NULL ) return;

    if ( m_lv->getElements() < 1 ) return;

    if ( ! keep_search_mode ) {
        finishsearchmode();
    }

    int row = m_lv->getActiveRow();

    if ( row < 0 ) {
        row = m_lv->getElements() - 1;

        ce->setActiveEntryPos( m_lv->getData( row, -1 ) );

        makeListViewRowActive( row );
    } else {
        if ( row > 0 ) {
            row--;

            ce->setActiveEntryPos( m_lv->getData( row, -1 ) );

            makeListViewRowActive( row );
        } else {
            if ( wrap_around ) {
                row = m_lv->getElements() - 1;

                ce->setActiveEntryPos( m_lv->getData( row, -1 ) );

                makeListViewRowActive( row );
            }
        }
    }

    m_lv->showActive();
}

void VirtualDirMode::command_down()
{
    command_down( false, false );
}

void VirtualDirMode::command_down( bool keep_search_mode, bool wrap_around )
{
    if ( ce.get() == NULL ) return;

    if ( m_lv->getElements() < 1 ) return;

    if ( ! keep_search_mode ) {
        finishsearchmode();
    }

    int row = m_lv->getActiveRow();

    if ( row < 0 ) {
        ce->setActiveEntryPos( m_lv->getData( 0, -1 ) );

        makeListViewRowActive( 0 );
    } else {
        if ( row + 1 < m_lv->getElements() ) {
            row++;

            ce->setActiveEntryPos( m_lv->getData( row, -1 ) );

            makeListViewRowActive( row );
        } else {
            if ( wrap_around ) {
                ce->setActiveEntryPos( m_lv->getData( 0, -1 ) );

                makeListViewRowActive( 0 );
            }
        }
    }

    m_lv->showActive();
}

void VirtualDirMode::command_first()
{
    if ( ce.get() == NULL ) return;

    if ( m_lv->getElements() < 1 ) return;

    finishsearchmode();

    ce->setActiveEntryPos( m_lv->getData( 0, -1 ) );

    makeListViewRowActive( 0 );

    m_lv->showActive();
}

void VirtualDirMode::command_last()
{
    if ( ce.get() == NULL ) return;

    if ( m_lv->getElements() < 1 ) return;

    finishsearchmode();

    int row = m_lv->getElements() - 1;

    ce->setActiveEntryPos( m_lv->getData( row, -1 ) );

    makeListViewRowActive( row );

    m_lv->showActive();
}

void VirtualDirMode::command_pageup()
{
    if ( ce.get() == NULL ) return;

    if ( m_lv->getElements() < 1 ) return;

    finishsearchmode();

    int row = m_lv->getActiveRow();

    int delta = m_lv->getMaxDisplayV();

    if ( row < 0 ) {
        row = m_lv->getElements() - 1;

        ce->setActiveEntryPos( m_lv->getData( row, -1 ) );

        makeListViewRowActive( row );
    } else {
        int new_row = a_max( row - delta,
                             0 );
        if ( new_row != row ) {
            ce->setActiveEntryPos( m_lv->getData( new_row, -1 ) );

            makeListViewRowActive( new_row );
            m_lv->setYOffset( m_lv->getYOffset() + new_row - row );
        }

        //TODO possible improvement: try to keep the current row as long as possible
        // (scroll less than delta at end of list)
    }

    m_lv->showActive();
}

void VirtualDirMode::command_pagedown()
{
    if ( ce.get() == NULL ) return;

    if ( m_lv->getElements() < 1 ) return;

    finishsearchmode();

    int row = m_lv->getActiveRow();

    int delta = m_lv->getMaxDisplayV();

    if ( row < 0 ) {
        ce->setActiveEntryPos( m_lv->getData( 0, -1 ) );

        makeListViewRowActive( 0 );
    } else {
        int new_row = a_min( row + delta,
                             m_lv->getElements() - 1 );
        if ( new_row != row ) {
            ce->setActiveEntryPos( m_lv->getData( new_row, -1 ) );

            makeListViewRowActive( new_row );
            m_lv->setYOffset( m_lv->getYOffset() + new_row - row );
        }

        //TODO possible improvement: try to keep the current row as long as possible
        // (scroll less than delta at end of list)
    }

    m_lv->showActive();
}

void VirtualDirMode::command_selectentry()
{
    if ( ce.get() == NULL ) return;

    if ( m_lv->getElements() < 1 ) return;

    finishsearchmode();

    int row = m_lv->getActiveRow();

    if ( row < 0 ) return;

    bool state = m_lv->getSelect( row );

    state = state ? false : true;

    if ( state == false ) {
        createSelectionCheckpoint();
    }

    m_lv->setSelect( row, state );
    //ce->getEntry( m_lv->getData( row, -1 ) )->setSelected( state );
    ce->modifyEntry( m_lv->getData( row, -1 ),
                     [state]( NWCEntrySelectionState &es ) { es.setSelected( state ); } );

    showCacheState();

    command_down();

    if ( state == false ) {
        m_selection_checkpoint_dirty = true;
    }
}

void VirtualDirMode::command_selectentry(  const std::list< RefCount< ArgClass > > &args )
{
    if ( ce.get() == NULL ) return;

    if ( m_lv->getElements() < 1 ) return;

    finishsearchmode();

    int row = m_lv->getActiveRow();

    if ( row < 0 ) return;

    bool state = m_lv->getSelect( row );

    state = state ? false : true;

    if ( state == false ) {
        createSelectionCheckpoint();
    }

    int elements = 0;

    if ( ! args.empty() ) {
        RefCount< ArgClass > a1 = args.front();
        if ( a1.get() != NULL ) {
            StringArg *a2 = dynamic_cast< StringArg *>( a1.get() );
            if ( a2 != NULL ) {
                std::string entry = a2->getValue();

                if ( entry == "pagedown" || entry == "p" ) {
                    elements = m_lv->getMaxDisplayV();
                } else if ( entry == "pageup" || entry == "-p" ) {
                    elements = - m_lv->getMaxDisplayV();
                } else {
                    int v;

                    if ( sscanf( entry.c_str(), "%d", &v ) == 1 ) {
                        elements = v;
                    }
                }
            }
        }
    }

    while ( elements != 0 ) {
        m_lv->setSelect( row, state );

        ce->modifyEntry( m_lv->getData( row, -1 ),
                         [state]( NWCEntrySelectionState &es ) { es.setSelected( state ); } );

        if ( elements < 0 ) {
            command_up();
            elements++;
        } else {
            command_down();
            elements--;
        }

        int new_row = m_lv->getActiveRow();

        if ( new_row < 0 || new_row == row ) {
            break;
        }

        row = new_row;
    }

    showCacheState();

    if ( state == false ) {
        m_selection_checkpoint_dirty = true;
    }
}

int VirtualDirMode::showDir( std::unique_ptr< NWC::Dir > &dir_list )
{
    finishsearchmode();

    RefCount< DMCacheEntryNWC > new_ce( new DMCacheEntryNWC( std::move( dir_list ),
                                                             &m_dir_filter_sets,
                                                             &m_dir_sort_sets,
                                                             &m_dir_bookmarks_sets,
                                                             [this]() {
                                                                 aguix->getLastKeyRelease();
                                                                 parentlister->getWorker()->getMainWin()->forbidUserInput();
                                                                 parentlister->getWorker()->setWaitCursor();
                                                             },
                                                             [this]() {
                                                                 parentlister->getWorker()->getMainWin()->permitUserInput();
                                                                 parentlister->getWorker()->unsetWaitCursor();
                                                             } ) );
    new_ce->checkForDCD();

    setCurrentCE( new_ce );

    return 0;
}

int VirtualDirMode::showDir( const NWC::Dir &dir_list )
{
    std::unique_ptr< NWC::Dir > d( new NWC::Dir( dir_list ) );

    return showDir( d );
}

int VirtualDirMode::showDir( const std::string &path, showdir_store_type store_type )
{
    std::string path2use;
    std::string highlight_entry;
    std::string old_dir;

    if ( path.empty() ) return 1;

    parentlister->getWorker()->setWaitCursor();

    parentlister->setName( catalog.getLocale( 1011 ) );

    aguix->Flush();

    if ( ce.get() ) {
        old_dir = ce->getNameOfDir();
    }

    // normalize given dir
    if ( path[0] != '/' ) {
        // resolve current dir
        char *cwd = NWC::Path::handlePathExt( "." );

        std::string str1 = cwd;
        str1 += "/";
        str1 += path;
        
        _freesafe( cwd );
        char *tstr = NWC::Path::handlePath( str1.c_str() );
        path2use = tstr;
        _freesafe( tstr );
    } else {
        char *tstr = NWC::Path::handlePath( path.c_str() );
        path2use = tstr;
        _freesafe( tstr );
    }

    NWC::FSEntry e( path2use );

    bool invalid_dir = false;

    if ( e.entryExists() == true ) {
        if ( e.isDir( true ) == false ) {
            path2use = e.getDirname();
            highlight_entry = e.getBasename();
        }
    } else {
        invalid_dir = true;
    }

    // check whether it is okay to enter the dir
    if ( invalid_dir || parentlister->getWorker()->checkEnterDir( path2use ) == false ) {
        // restore stringgadget
        if ( ce.get() ) {
            m_sg->setText( m_visible_dirname.c_str() );
        } else {
            m_sg->setText( "" );
        }
      
        updateName();
        parentlister->getWorker()->unsetWaitCursor();
        return 1;
    }

    if ( ce.get() != NULL &&
         ce->getNameOfDir() == path2use ) {
    } else {
        finishsearchmode();
    }

    for ( auto &cache_entry_pair : m_cache ) {
        if ( cache_entry_pair.first == path2use ) {
            if ( cache_entry_pair.second.get() ) {
                cache_entry_pair.second->reload( DMCacheEntryNWC::RESET_FILETYPE_FOR_CHANGED_SIZE_OF_UNKNOWN );
            }

            setCurrentCE( cache_entry_pair.second );
            updateName();

            if ( ! highlight_entry.empty() ) {
                activateEntry( highlight_entry, true );
            }

            parentlister->getWorker()->unsetWaitCursor();
            return 0;
        }
    }

    NWC::Dir d( path2use );

    if ( d.readDir( false ) == 0 ) {
        if ( showDir( d ) != 0 ) {
            updateName();
            parentlister->getWorker()->unsetWaitCursor();
            return 1;
        }

        if ( ! highlight_entry.empty() ) {
            activateEntry( highlight_entry, true );
        }

        DeepPathNode::node_change_t changed = DeepPathNode::NODE_UNCHANGED;

        time_t now = time( NULL );

        parentlister->getWorker()->getPathStore().storePath( path2use, now, &changed, DeepPathStore::DPS_TS_UPDATE_ALWAYS );

        if ( changed != DeepPathNode::NODE_UNCHANGED && store_type == SHOWDIR_PERS_STORE_PATH ) {
            parentlister->getWorker()->storePathPers( path2use, now );
        }
    } else {
        updateName();
        parentlister->getWorker()->unsetWaitCursor();
        return 1;
    }

    updateName();
    parentlister->getWorker()->unsetWaitCursor();

    return 0;
}

void VirtualDirMode::updateLV()
{
    if ( ! ce.get() ) {
        m_lv->setSize( 0 );
        m_lv->redraw();
        m_sg->setText( "" );
        return;
    }

    updateLVSortHint();

    int nr_of_entries = ce->getNrOfFiles() + ce->getNrOfDirs();

    if ( m_show_dotdot ) {
        nr_of_entries++;
    }

    if ( m_lv_ce_valid ) {
        ce->setXOffset( m_lv->getXOffset() );
        ce->setYOffset( m_lv->getYOffset() );
    }

    m_lv->setSize( nr_of_entries );
    m_lv->setSizeDNDText( nr_of_entries );
    m_lv->setActiveRowQ( -1 );

    m_common_prefix = ce->getCommonPrefix();

    m_visible_dirname = ce->getNameOfDir();

    if ( /*AGUIXUtils::starts_with( m_visible_dirname,
           m_common_prefix )*/
        ce->isRealDir() ) {
    } else {
        m_visible_dirname += ":";
        m_visible_dirname += m_common_prefix;
    }

    m_ce_pos_to_view_row.resize( (int)ce->getSize() );

    int row = 0;

    if ( m_filtered_search_state.searchmode_on == true ) {
        std::string newtext;

        if ( ! m_filtered_search_state.getFilter().isExprFilter() ) {
            newtext = m_filtered_search_state.getFilter().getFilterString();
        }

        if ( ! newtext.empty() ) {
            if ( m_filtered_search_state.flexible_matching_active ) {
                m_flexible_matcher.setMatchString( newtext );
            } else {
                m_nonflexible_matcher.setMatchString( newtext );
            }
            m_apply_matcher = true;
        } else if ( m_apply_matcher == true ) {
            m_lv->clearHighlightSegments();
            m_apply_matcher = false;
        }
    } else if ( m_apply_matcher == true ) {
        m_lv->clearHighlightSegments();
        m_apply_matcher = false;
    }

    if ( m_show_dotdot ) {
        m_lv->setData( row, -1 );
        updateLVRow( row );

        row++;
    }

    for ( int ce_id = 0;
          ce_id < (int)ce->getSize();
          ce_id++ ) {
        if ( ce->getUse( ce_id ) ) {
            m_lv->setData( row, ce_id );

            updateLVRow( row );

            m_ce_pos_to_view_row.at( ce_id ) = row;

            row++;
        } else {
            m_ce_pos_to_view_row.at( ce_id ) = -1;
        }
    }

    if ( ! m_filtered_search_state.searchmode_on ) {
        m_sg->setText( m_visible_dirname.c_str() );
    }

    if ( row != nr_of_entries ) {
        fprintf( stderr, "Worker error: Nr of entries does not match real value!\n" );
        //TODO remove potential remaining entries?
    }

    m_lv->setXOffset( ce->getXOffset() );
    m_lv->setYOffset( ce->getYOffset() );

    m_lv_ce_valid = true;

    m_lv->redraw();

    if ( parentlister->getFocus() == true ) {
        parentlister->getWorker()->setTitle( m_visible_dirname.c_str() );
    }
}

void VirtualDirMode::updateLVRow( int row )
{
    if ( row < 0 || row >= m_lv->getElements() ) return;

    int side = parentlister->getSide();
    const std::vector<WorkerTypes::listcol_t> *dis;

    if ( m_use_custom_columns ) {
        dis = &m_custom_columns;
    } else {
        dis = wconfig->getVisCols( side );
    }

    if ( m_show_dotdot && row == 0 ) {
        int column = 0;

        for ( const auto &ct : *dis ) {
            std::string r = NWCEntrySelectionState::getStringRepresentation( ct, 1,
                                                                             &m_dummy_dotdot_fse, NULL, NULL, "" );

            if ( ct == WorkerTypes::LISTCOL_NAME ) {
                m_lv->setText( row, column, r, true );
            } else {
                m_lv->setText( row, column, r, false );
            }

            column++;
        }

        m_lv->replacedndtext( row, "" );
        m_lv->setDataExt( row, new NMRowData( row, NULL, "" ) );
        m_lv->setSelectQ( row, false );

        updateColor( row );

        if ( ce->getActiveEntryPos() == -1 ) {
            if ( parentlister->isActive() == true ) {
                m_lv->setActiveRowQ( row );
            }
        }

        return;
    }

    int ce_id = m_lv->getData( row, -1 );
    int file_name_skip = m_common_prefix.size();

    if ( ce->getUse( ce_id ) ) {
        int column = 0;
        const NWCEntrySelectionState *es = ce->getEntry( ce_id );

        if ( ! es ) return;

        const NWC::FSEntry *fse = es->getNWCEntry();

        if ( ! fse ) return;

        auto fpr = std::make_shared< NWCFlagProducer >( es, nullptr );
        FlagReplacer rpl( fpr );

        for ( const auto &ct : *dis ) {
            std::string r = es->getStringRepresentation( ct, file_name_skip, &rpl );
            m_lv->setText( row, column, r, ct == WorkerTypes::LISTCOL_NAME ? true : false );

            if ( ct == WorkerTypes::LISTCOL_NAME && m_apply_matcher ) {
                std::vector< size_t > segments;

                // skip the first character which is the type of the entry
                std::string name( r, 1 );

                if ( m_filtered_search_state.flexible_matching_active ) {
                    m_flexible_matcher.countNonMatchingBlocks<true>( name, &segments );
                } else {
                    segments = m_nonflexible_matcher.getMatchSegments( name );
                }

                if ( segments.size() > 1 ) {
                    // correct the result for the skipped first char
                    // the first segment is always the non-matching which may be
                    // zero
                    segments[0]++;
                    m_lv->setHighlightSegments( row, column, segments );
                } else {
                    m_lv->setHighlightSegments( row, column, {} );
                }
            }

            column++;
        }

        m_lv->replacedndtext( row, fse->getBasename().c_str() );

        FileEntry fe( *fse );
        fe.filetype = es->getFiletype();
        
        m_lv->setDataExt( row, new NMRowData( row, &fe, fse->getFullname().c_str() ) );

        if ( es->getSelected() ) m_lv->setSelectQ( row, true );
        else m_lv->setSelectQ( row, false );

        if ( ce_id == ce->getActiveEntryPos() ) {
            // only make the lvc active if we are active
            if ( parentlister->isActive() == true ) {
                m_lv->setActiveRowQ( row );
            }
        }

        updateColor( row );
    }
}

void VirtualDirMode::setupLVFields()
{
    int side = parentlister->getSide();
    const std::vector<WorkerTypes::listcol_t> *sets;
    int used_fields, li;
    int cur_field;
    int tw;
  
    if ( m_use_custom_columns ) {
        sets = &m_custom_columns;
    } else {
        sets = wconfig->getVisCols( side );
    }
    if ( sets == NULL ) return;
  
    used_fields = sets->size();

    if ( used_fields == 0 ) {
        // oops, what's this?
        m_lv->setNrOfFields( 1 );  // 0 fields are not supported, blame the author (me ;-) )

        return;
    }

    m_lv->setNrOfFields( used_fields );

    m_field_name_to_column.clear();
  
    for( cur_field = 0; cur_field < (int)sets->size(); cur_field++ ) {
        //m_lv->setFieldTextResizeable( cur_field, true );
        li = WorkerTypes::getAvailListColEntry( (*sets)[cur_field] );
        if ( li >= 0 ) {
            m_lv->setFieldTextQ( cur_field, catalog.getLocale( WorkerTypes::availListCols[li].catalogid ) );
        }

        m_lv->setHideEmptyField( cur_field, false );

        switch ( (*sets)[cur_field] ) {
            case WorkerTypes::LISTCOL_NAME:
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_LEFT );

                tw = getStoredFieldWidth( "LISTCOL_NAME" );
                if ( tw < -1 || tw > 10000 ) tw = -1;

                m_lv->setFieldWidthQ( cur_field, tw );
	  
                m_field_name_to_column["LISTCOL_NAME"] = cur_field;
                break;
            case WorkerTypes::LISTCOL_SIZE:
#ifdef LEFTJUSTIFY
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_LEFT );
#else
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_RIGHT );
#endif
                tw = getStoredFieldWidth( "LISTCOL_SIZE" );
                if ( tw < -1 || tw > 10000 ) tw = -1;

                m_lv->setFieldWidthQ( cur_field, tw );

                m_field_name_to_column["LISTCOL_SIZE"] = cur_field;
                break;
            case WorkerTypes::LISTCOL_SIZEH:
#ifdef LEFTJUSTIFY
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_LEFT );
#else
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_RIGHT );
#endif
                tw = getStoredFieldWidth( "LISTCOL_SIZEH" );
                if ( tw < -1 || tw > 10000 ) tw = -1;

                m_lv->setFieldWidthQ( cur_field, tw );

                m_field_name_to_column["LISTCOL_SIZEH"] = cur_field;
                break;
            case WorkerTypes::LISTCOL_TYPE:
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_LEFT );

                tw = getStoredFieldWidth( "LISTCOL_TYPE" );
                if ( tw < -1 || tw > 10000 ) tw = -1;
                m_lv->setFieldWidthQ( cur_field, tw );

                m_field_name_to_column["LISTCOL_TYPE"] = cur_field;
                break;
            case WorkerTypes::LISTCOL_PERM:
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_LEFT );

                tw = getStoredFieldWidth( "LISTCOL_PERM" );
                if ( tw < -1 || tw > 10000 ) tw = -1;
                m_lv->setFieldWidthQ( cur_field, tw );

                m_field_name_to_column["LISTCOL_PERM"] = cur_field;
                break;
            case WorkerTypes::LISTCOL_OWNER:
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_LEFT );

                tw = getStoredFieldWidth( "LISTCOL_OWNER" );
                if ( tw < -1 || tw > 10000 ) tw = -1;
                m_lv->setFieldWidthQ( cur_field, tw );

                m_field_name_to_column["LISTCOL_OWNER"] = cur_field;
                break;
            case WorkerTypes::LISTCOL_DEST:
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_LEFT );

                tw = getStoredFieldWidth( "LISTCOL_DEST" );
                if ( tw < -1 || tw > 10000 ) tw = -1;
                m_lv->setFieldWidthQ( cur_field, tw );

                m_field_name_to_column["LISTCOL_DEST"] = cur_field;
                break;
            case WorkerTypes::LISTCOL_MOD:
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_LEFT );

                tw = getStoredFieldWidth( "LISTCOL_MOD" );
                if ( tw < -1 || tw > 10000 ) tw = -1;
                m_lv->setFieldWidthQ( cur_field, tw );

                m_field_name_to_column["LISTCOL_MOD"] = cur_field;
                break;
            case WorkerTypes::LISTCOL_ACC:
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_LEFT );

                tw = getStoredFieldWidth( "LISTCOL_ACC" );
                if ( tw < -1 || tw > 10000 ) tw = -1;
                m_lv->setFieldWidthQ( cur_field, tw );

                m_field_name_to_column["LISTCOL_ACC"] = cur_field;
                break;
            case WorkerTypes::LISTCOL_CHANGE:
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_LEFT );

                tw = getStoredFieldWidth( "LISTCOL_CHANGE" );
                if ( tw < -1 || tw > 10000 ) tw = -1;
                m_lv->setFieldWidthQ( cur_field, tw );

                m_field_name_to_column["LISTCOL_CHANGE"] = cur_field;
                break;
            case WorkerTypes::LISTCOL_INODE:
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_RIGHT );

                tw = getStoredFieldWidth( "LISTCOL_INODE" );
                if ( tw < -1 || tw > 10000 ) tw = -1;
                m_lv->setFieldWidthQ( cur_field, tw );

                m_field_name_to_column["LISTCOL_INODE"] = cur_field;
                break;
            case WorkerTypes::LISTCOL_NLINK:
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_RIGHT );

                tw = getStoredFieldWidth( "LISTCOL_NLINK" );
                if ( tw < -1 || tw > 10000 ) tw = -1;
                m_lv->setFieldWidthQ( cur_field, tw );

                m_field_name_to_column["LISTCOL_NLINK"] = cur_field;
                break;
            case WorkerTypes::LISTCOL_BLOCKS:
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_RIGHT );

                tw = getStoredFieldWidth( "LISTCOL_BLOCKS" );
                if ( tw < -1 || tw > 10000 ) tw = -1;
                m_lv->setFieldWidthQ( cur_field, tw );

                m_field_name_to_column["LISTCOL_BLOCKS"] = cur_field;
                break;
            case WorkerTypes::LISTCOL_EXTENSION:
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_LEFT );

                tw = getStoredFieldWidth( "LISTCOL_EXTENSION" );
                if ( tw < -1 || tw > 10000 ) tw = -1;
                m_lv->setFieldWidthQ( cur_field, tw );

                m_field_name_to_column["LISTCOL_EXTENSION"] = cur_field;
                break;
            case WorkerTypes::LISTCOL_CUSTOM_ATTR:
                m_lv->setFieldAlignQ( cur_field, FieldListView::ALIGN_LEFT );
                m_lv->setHideEmptyField( cur_field, true );

                tw = getStoredFieldWidth( "LISTCOL_CUSTOM_ATTR" );
                if ( tw < -1 || tw > 10000 ) tw = -1;
                m_lv->setFieldWidthQ( cur_field, tw );

                m_field_name_to_column["LISTCOL_CUSTOM_ATTR"] = cur_field;
                break;
            default:
                break;
        }
    }
}

void VirtualDirMode::lv_select_handler( FieldListView *lv, int element )
{
    if ( ce.get() == NULL ) return;

    if ( lv != m_lv ) return;

    if ( m_lv->getElements() < 1 ) return;

    bool state = m_lv->getSelect( element );

    //ce->getEntry( m_lv->getData( element, -1 ) )->setSelected( state );
    ce->modifyEntry( m_lv->getData( element, -1 ),
                     [state]( NWCEntrySelectionState &es ) { es.setSelected( state ); } );

    if ( m_lv->getActiveRow() == element ) {
        ce->setActiveEntryPos( m_lv->getData( element, -1 ) );
    }

    showCacheState();

    if ( state == false ) {
        m_selection_checkpoint_dirty = true;
    }
}

void VirtualDirMode::bookmarksChanged()
{
    m_bookmarks_has_been_changed = true;
}

void VirtualDirMode::updateOnBookmarkChange()
{
    if ( m_bookmarks_has_been_changed == true ) {
        m_dir_filter_sets.bookmarksChanged();
        m_dir_bookmarks_sets.bookmarksChanged();
        
        rebuildView();
        
        m_bookmarks_has_been_changed = false;
    }
}

void VirtualDirMode::rebuildView()
{
    if ( m_lv != NULL ) {
        if ( ce.get() ) {
            ce->updateView( false );
        }

        updateLV();

        showCacheState();

        setName();
        updateTabs();
    }
}

void VirtualDirMode::setTabTitle( int pos )
{
    if ( pos < 0 || pos >= (int)m_tab_store.tab_entries.size() ) return;

    if ( ! m_tab_store.tab_entries[pos].ce.get() ) return;

    std::string p = m_tab_store.tab_entries[pos].ce->getCommonPrefix();

    if ( p.empty() ) {
        p = m_tab_store.tab_entries[pos].ce->getNameOfDir();
    }

    if ( p.length() > 1 && AGUIXUtils::ends_with( p, "/" ) ) {
        p.resize( p.length() - 1 );
    }

    std::string p2 = NWC::Path::basename( p );

    if ( p2.empty() ) p2 = p;

    if ( m_tab_store.tab_entries[pos].locked ) {
        p2 = "(" + p2 + ")";
    }

    m_tab_b->setOption( pos, p2.c_str() );
}

void VirtualDirMode::updateTabs( bool update_all )
{
    while ( m_tab_b->getNrOfOptions() < (int)m_tab_store.tab_entries.size() ) {
        int pos = m_tab_b->getNrOfOptions();
        m_tab_b->addOption( "" );

        setTabTitle( pos );
    }

    while ( m_tab_b->getNrOfOptions() > (int)m_tab_store.tab_entries.size() ) {
        int pos = m_tab_b->getNrOfOptions();

        if ( pos < 2 ) break;

        m_tab_b->removeOption( pos - 1 );
    }

    if ( update_all ) {
        for ( int pos = 0; pos < (int)m_tab_store.tab_entries.size(); pos++ ) {
            setTabTitle( pos );
        }
    } else {
        setTabTitle( m_tab_store.active_tab );
    }

    m_tab_b->setOption( m_tab_store.active_tab );
}

void VirtualDirMode::newTab()
{
    if ( m_tab_store.active_tab < 0 || m_tab_store.active_tab >= (int)m_tab_store.tab_entries.size() ) return;

    std::string current_breadcrumb_path = m_tab_store.tab_entries.at( m_tab_store.active_tab ).path;

    m_tab_store.tab_entries.push_back( { ce, current_breadcrumb_path } );

    m_tab_store.active_tab = m_tab_store.tab_entries.size() - 1;

    if ( m_lv && ce.get() ) {
        ce->setXOffset( m_lv->getXOffset() );
        ce->setYOffset( m_lv->getYOffset() );
    }

    updateLV();

    updateTabs();
}

void VirtualDirMode::closeCurrentTab()
{
    closeTab( m_tab_store.active_tab );
}

void VirtualDirMode::closeTab( int tab )
{
    if ( tab < 0 || tab >= (int)m_tab_store.tab_entries.size() ) return;

    if ( m_tab_store.tab_entries.size() < 2 ) return;

    if ( m_tab_store.tab_entries[ tab ].locked ) return;

    auto it = m_tab_store.tab_entries.begin();

    for ( int a = tab; a > 0; a--, it++ );

    m_tab_store.tab_entries.erase( it );

    if ( m_tab_store.active_tab >= (int)m_tab_store.tab_entries.size() ) {
        m_tab_store.active_tab = m_tab_store.tab_entries.size() - 1;
    }

    if ( m_tab_store.active_tab >= 0 ) {
        m_tab_store.tab_entries[ m_tab_store.active_tab ].ce->reload();

        setCurrentCE( m_tab_store.tab_entries[ m_tab_store.active_tab ].ce );
    } else {
        setCurrentCE( NULL );
    }

    updateTabs( true );
}

void VirtualDirMode::lockTab( int tab )
{
    if ( tab < 0 || tab >= (int)m_tab_store.tab_entries.size() ) return;

    m_tab_store.tab_entries[ tab ].locked = true;

    updateTabs( true );
}

void VirtualDirMode::unlockTab( int tab )
{
    if ( tab < 0 || tab >= (int)m_tab_store.tab_entries.size() ) return;

    m_tab_store.tab_entries[ tab ].locked = false;

    updateTabs( true );
}

void VirtualDirMode::toggleLockTab( int tab )
{
    if ( tab < 0 || tab >= (int)m_tab_store.tab_entries.size() ) return;

    m_tab_store.tab_entries[ tab ].locked = ! m_tab_store.tab_entries[ tab ].locked;

    updateTabs( true );
}

void VirtualDirMode::toggleLockCurrentTab()
{
    toggleLockTab( m_tab_store.active_tab );
}

void VirtualDirMode::switchToTab( int tab )
{
    if ( tab < 0 || tab >= (int)m_tab_store.tab_entries.size() ) return;

    if ( ! m_tab_store.tab_entries[ tab ].ce.get() ) return;

    m_tab_store.active_tab = tab;

    m_tab_store.tab_entries[ tab ].ce->reload();

    // this is to apply previous deepest path to breadcrumb
    // actual path will be set by setCurrentCE
    m_breadcrumb.current_path_components.clear();
    update_breadcrumb( m_tab_store.tab_entries[ tab ].path, true );

    setCurrentCE( m_tab_store.tab_entries[ tab ].ce );
}

void VirtualDirMode::switchToTabDirection( int direction )
{
    if ( m_tab_b == NULL ) return;

    int tab = m_tab_b->getSelectedOption() + ( direction % m_tab_b->getNrOfOptions() );

    tab += m_tab_b->getNrOfOptions();
    tab %= m_tab_b->getNrOfOptions();

    switchToTab( tab );
}

void VirtualDirMode::moveTabDirection( int direction )
{
    if ( m_tab_store.active_tab < 0 ||
         m_tab_store.active_tab >= (int)m_tab_store.tab_entries.size() ) {
        return;
    }

    int tab = m_tab_b->getSelectedOption() + direction;

    for (;;) {
        if ( tab < 0 ||
             tab >= (int)m_tab_store.tab_entries.size() ) {
            break;
        }

        if ( m_tab_store.tab_entries[ tab ].locked ) {
            tab += direction;
            continue;
        }

        // got a valid new tab position so swap current tab and new tab

        std::swap( m_tab_store.tab_entries[ m_tab_store.active_tab ],
                   m_tab_store.tab_entries[ tab ] );

        m_tab_store.active_tab = tab;

        updateTabs( true );

        break;
    }
}

void VirtualDirMode::updateCE()
{
    if ( ! ce.get() ) return;

    ce->reload();
}

void VirtualDirMode::showCacheState()
{
    if ( parentlister->isActive() == false ) return;

    if ( ce.get() == NULL ) {
        // no directory but at least clear statebar
        parentlister->setStatebarText( "" );
        return;
    }

    const char *tstr1 = catalog.getLocale( 107 );
    const char *tstr2 = catalog.getLocale( 108 );
    const char *tstr3 = catalog.getLocale( 13 );
    std::string buf1, buf2, buf3, buf4;
    bool human_strings = false;
    loff_t l1,l2;
    int l,tl;
  
    l1 = ce->getSizeOfFiles( true ) + ce->getSizeOfDirs( true );
    l2 = ce->getSizeOfFiles( false ) + ce->getSizeOfDirs( false );

    buf3 = AGUIXUtils::bytes_to_human_readable_f( l1, 10 );
    buf4 = AGUIXUtils::bytes_to_human_readable_f( l2, 10 );

    if ( l2 > 10 * 1024 ) {
        human_strings = true;
    }

    MakeLong2NiceStr( l1, buf1 );
    MakeLong2NiceStr( l2, buf2 );
  
    if ( maxnochanges > 100 ) {
        maxnochanges = 0;
        maxfilestrlen--;
        maxdirstrlen--;
        maxbytesstrlen--;
        maxwbytesstrlen--;
        maxhiddenfilesstrlen--;
        maxhiddendirsstrlen--;
    }

    tl = 0;
    l = (int)( log10( (double)ce->getNrOfFiles( false ) + 1 ) + 1.0 );
    if ( l > maxfilestrlen ) {
        maxfilestrlen = l;
        tl++;
    }
    l = (int)( log10( (double)ce->getNrOfDirs( false ) + 1 ) + 1.0 );
    if ( l > maxdirstrlen ) {
        maxdirstrlen = l;
        tl++;
    }
    l = buf2.length();
    if ( l > maxbytesstrlen ) {
        maxbytesstrlen = l;
        tl++;
    }

    if ( ce->getNrOfHiddenFiles() > 0 ) {
        l = (int)( log10( (double)ce->getNrOfHiddenFiles() + 1 ) + 1.0 );
        if ( l > maxhiddenfilesstrlen ) {
            maxhiddenfilesstrlen = l;
            tl++;
        }
    } else {
        if ( maxhiddenfilesstrlen < 0 ) {
            maxhiddenfilesstrlen = 0;
            tl++;
        }
    }

    if ( ce->getNrOfHiddenDirs() > 0 ) {
        l = (int)( log10( (double)ce->getNrOfHiddenDirs() + 1 ) + 1.0 );
        if ( l > maxhiddendirsstrlen ) {
            maxhiddendirsstrlen = l;
            tl++;
        }
    } else {
        if ( maxhiddendirsstrlen < 0 ) {
            maxhiddendirsstrlen = 0;
            tl++;
        }
    }
  
    // we have to check for buf3 and buf4 because this is possibly:
    //  1200 KB / 100 MB
    // the left part takes more space
    l = buf3.length();
    if ( l > maxwbytesstrlen ) {
        maxwbytesstrlen = l;
        tl++;
    }
    l = buf4.length();
    if ( l > maxwbytesstrlen ) {
        maxwbytesstrlen = l;
        tl++;
    }

    std::string hidden_files_str;
    std::string hidden_dirs_str;

    if ( ce->getNrOfHiddenFiles() > 0 ) {
        std::string fstr = AGUIXUtils::formatStringToString( " (+%%%dld)", maxhiddenfilesstrlen );
        hidden_files_str = AGUIXUtils::formatStringToString( fstr.c_str(), ce->getNrOfHiddenFiles() );
    } else if ( maxhiddenfilesstrlen > 0 ) {
        std::string fstr = AGUIXUtils::formatStringToString( "   %%%ds ", maxhiddenfilesstrlen );
        hidden_files_str = AGUIXUtils::formatStringToString( fstr.c_str(), "" );
    }
    if ( ce->getNrOfHiddenDirs() > 0 ) {
        std::string fstr = AGUIXUtils::formatStringToString( " (+%%%dld)", maxhiddendirsstrlen );
        hidden_dirs_str = AGUIXUtils::formatStringToString( fstr.c_str(), ce->getNrOfHiddenDirs() );
    } else if ( maxhiddendirsstrlen > 0 ) {
        std::string fstr = AGUIXUtils::formatStringToString( "   %%%ds ", maxhiddendirsstrlen );
        hidden_dirs_str = AGUIXUtils::formatStringToString( fstr.c_str(), "" );
    }

    // this are the "double" format strings for the statebar
    // the first is when we will also print some nice byte-values (in B, KB or MB)
    // the second otherwise
    // they have to be same length and modifiers
    // from this strings the real format string is build according to the needed string/number lengths
    // NOTE: the two last %s in _SLIM will be replaced by spaces
    //       this is needed to keep the string at the same place in the statebar when switching between listers
#define STATEBAR_FORMAT      "[%%s %%%dld / %%%dld%%s]   [%%s %%%dld / %%%dld%%s]   [%%s %%%ds / %%%ds   (%%%ds / %%%ds) ]"
#define STATEBAR_FORMAT_SLIM "[%%s %%%dld / %%%dld%%s]   [%%s %%%dld / %%%dld%%s]   [%%s %%%ds / %%%ds]   %%%ds   %%%ds   "

    std::string tstr;
    
    if ( human_strings == true ) {
        // this only when the field is visible

        std::string fstr = AGUIXUtils::formatStringToString( STATEBAR_FORMAT,
                                                             maxfilestrlen,
                                                             maxfilestrlen,
                                                             maxdirstrlen,
                                                             maxdirstrlen,
                                                             maxbytesstrlen,
                                                             maxbytesstrlen,
                                                             maxwbytesstrlen,
                                                             maxwbytesstrlen );
        tstr = AGUIXUtils::formatStringToString( fstr.c_str(),
                                                 tstr1,
                                                 ce->getNrOfFiles( 1 ),
                                                 ce->getNrOfFiles( 0 ),
                                                 hidden_files_str.c_str(),
                                                 tstr2,
                                                 ce->getNrOfDirs( 1 ),
                                                 ce->getNrOfDirs( 0 ),
                                                 hidden_dirs_str.c_str(),
                                                 tstr3,
                                                 buf1.c_str(),
                                                 buf2.c_str(),
                                                 buf3.c_str(),
                                                 buf4.c_str() );
    } else {
        std::string fstr = AGUIXUtils::formatStringToString( STATEBAR_FORMAT_SLIM,
                                                             maxfilestrlen,
                                                             maxfilestrlen,
                                                             maxdirstrlen,
                                                             maxdirstrlen,
                                                             maxbytesstrlen,
                                                             maxbytesstrlen,
                                                             maxwbytesstrlen,
                                                             maxwbytesstrlen );
        tstr = AGUIXUtils::formatStringToString( fstr.c_str(),
                                                 tstr1,
                                                 ce->getNrOfFiles( 1 ),
                                                 ce->getNrOfFiles( 0 ),
                                                 hidden_files_str.c_str(),
                                                 tstr2,
                                                 ce->getNrOfDirs( 1 ),
                                                 ce->getNrOfDirs( 0 ),
                                                 hidden_dirs_str.c_str(),
                                                 tstr3,
                                                 buf1.c_str(),
                                                 buf2.c_str(),
                                                 "",    // print just spaces
                                                 "" );  // print just spaces
    }
  
    if ( tl == 0 ) // no changes
        maxnochanges++;
  
    parentlister->setStatebarText( tstr.c_str() );
#undef STATEBAR_FORMAT
#undef STATEBAR_FORMAT_SLIM
}

void VirtualDirMode::setName()
{
    bool filteractive;
    std::string tstr;
    bool doUpdate = false;
    int sortmode;
  
    filteractive = m_dir_filter_sets.filterActive();

    sortmode = m_dir_sort_sets.getSortMode();

    if ( m_filtered_search_state.non_empty_filter_active == true ) {
        if ( getEntrySearchString().isExprFilter() ) {
            if ( m_expression_filter_interrupted ) {
                tstr = "(intr e):";
            } else {
                tstr = "e:";
            }

            //tstr += getEntrySearchString().getFilterString();
            tstr += m_filtered_search_state.getLastValidExprFilter();

            tstr += " ";
        } else {
            tstr = "(";

            if ( m_filtered_search_state.flexible_matching_active ) {
                tstr += "~";

                std::string s = getEntrySearchString().getFilterString();

                if ( s != m_filtered_search_state.cached_previous_match_string ) {
                    m_filtered_search_state.cached_cleaned_match_string = StringMatcherFNMatch::cleanFlexibleMatchString( s );
                    s = m_filtered_search_state.cached_previous_match_string;
                }

                tstr += m_filtered_search_state.cached_cleaned_match_string;
            } else {
                tstr += getEntrySearchString().getFilterString();
            }
            tstr += ") ";
        }
    }

    switch ( m_busy_flag ) {
        case 1:
            tstr += '|';
            break;
        case 2:
            tstr += '/';
            break;
        case 3:
            tstr += '-';
            break;
        case 4:
            tstr += '\\';
            break;
        default:
            tstr += ' ';
            break;
    }

    tstr += ' ';

    switch ( sortmode & 0xff ) {
        case SORT_NAME:
            tstr += 'N';
            break;
        case SORT_SIZE:
            tstr += 'S';
            break;
        case SORT_CHGTIME:
            tstr += 'C';
            break;
        case SORT_MODTIME:
            tstr += 'M';
            break;
        case SORT_ACCTIME:
            tstr += 'A';
            break;
        case SORT_TYPE:
            tstr += 'T';
            break;
        case SORT_OWNER:
            tstr += 'O';
            break;
        case SORT_INODE:
            tstr += 'I';
            break;
        case SORT_NLINK:
            tstr += 'L';
            break;
        case SORT_PERMISSION:
            tstr += 'P';
            break;
        case SORT_EXTENSION:
            tstr += 'E';
            break;
        case SORT_CUSTOM_ATTRIBUTE:
            tstr += 'X';
            break;
        default:
            tstr += '?';
            break;
    }

    if ( ( sortmode & SORT_REVERSE ) == SORT_REVERSE ) tstr += 'R';
    else tstr += ' ';

    if ( m_dir_filter_sets.getShowHidden() == false ) tstr += 'H';
    else tstr += ' ';

    /*  if ( filteredSearchActive() == true ) {
        tstr += '+';
        } else */
    if ( filteractive == true ) {
        tstr += '*';
    } else {
        tstr += ' ';
    }

    if ( m_dir_filter_sets.getBookmarkFilter() != DirFilterSettings::SHOW_ALL ) {
        tstr += 'B';
    } else {
        tstr += ' ';
    }

    if ( tstr != m_lvb_indicators ) {
        m_lvb_indicators = tstr;
        doUpdate = true;
    }

    std::string modeinfo;
    if ( currentDirIsReal() ) {
        modeinfo = catalog.getLocale( 1024 );
    } else {
        modeinfo = catalog.getLocale( 1025 );
    }

    if ( modeinfo != m_lvb_modeinfo ) {
        m_lvb_modeinfo = modeinfo;
        doUpdate = true;
    }
  
    if ( doUpdate == true ) updateName();
}

void VirtualDirMode::updateName()
{
    std::string tstr;

    if ( m_enable_custom_lvb_line ) {
        if ( m_lvb_flag_replacer ) {
            tstr = m_lvb_flag_replacer->replaceFlags( m_custom_lvb_line, false );
        }
    } else {
        tstr = m_lvb_indicators;
        tstr += "  ";
        tstr += m_lvb_modeinfo;

        if ( ! m_free_space_str.empty() ) {
            tstr += " - ";
            tstr += m_free_space_str;
        }
    }

    if ( parentlister->getActiveMode() == this ) {
        parentlister->setName( tstr.c_str() );
    }
}

void VirtualDirMode::cleanupCache()
{
    auto oldest_element = m_cache.end();

    if ( (int)m_cache.size() <= m_max_cache_size ) return;

    for ( auto e = m_cache.begin();
          e != m_cache.end();
          e++ ) {
        if ( e->second.use_count() == 1 ) {
            oldest_element = e;
        }
    }

    if ( oldest_element != m_cache.end() ) {
        m_cache.erase( oldest_element );
    }
}

void VirtualDirMode::command_pathinput()
{
    finishsearchmode();

    m_filtered_search_state.old_sg_content = m_sg->getText();

    m_sg->activate();
}

void VirtualDirMode::command_parentdir( bool force )
{
    if ( ! force && filteredSearchActive() ) {
        deactivateFilteredSearch();
        return;
    }

    if ( ce.get() == NULL ) return;

    finishsearchmode();

    if ( ce->isRealDir() ) {
        std::string dirname = ce->getNameOfDir();
        std::string basename = NWC::Path::basename( dirname );

        dirname = NWC::Path::dirname( dirname );

        showDir( dirname );

        activateEntry( basename, true );
    } else {
        std::string dirname = ce->getCommonPrefix();

        if ( AGUIXUtils::ends_with( dirname, "/" ) ) {
            dirname.resize( dirname.length() - 1 );
        }

        std::string basename = NWC::Path::basename( dirname );

        dirname = NWC::Path::dirname( dirname );

        RefCount< DMCacheEntryNWC > res = findCacheEntry( ce->getNameOfDir(),
                                                          dirname, true  );

        if ( res.get() ) {
            // found entry
            setCurrentCE( res );
        } else {
            showDir( dirname );

            activateEntry( basename, true );
        }
    }
}

void VirtualDirMode::updateLastDirSettings( const struct directory_presets &old_settings )
{
    if ( m_dir_sort_sets.getSortMode() != old_settings.sortmode ) {
        m_last_explicit_directory_presets.sortmode = m_dir_sort_sets.getSortMode();
    }
    if ( m_dir_filter_sets.getShowHidden() != old_settings.show_hidden ) {
        m_last_explicit_directory_presets.show_hidden = m_dir_filter_sets.getShowHidden();
    }

    if ( m_dir_filter_sets.getFilters().size() != old_settings.filters.size() ||
         ! std::equal( m_dir_filter_sets.getFilters().begin(),
                       m_dir_filter_sets.getFilters().end(),
                       old_settings.filters.begin() ) ) {
        m_last_explicit_directory_presets.filters = m_dir_filter_sets.getFilters();
    }
}

void VirtualDirMode::checkDirectoryPresets( RefCount< DMCacheEntryNWC > &new_ce,
                                            RefCount< DMCacheEntryNWC > &old_ce,
                                            bool &update_directory_settings,
                                            struct directory_presets &new_settings )
{
    if ( ! new_ce.get() ||
         ! new_ce->isRealDir() ) {
        return;
    }

    // check if we need to apply directory presets
    auto new_node = m_directory_presets_pt.outerFind( new_ce->getNameOfDir(), true );
    const PathTree< std::string > *old_node = NULL;

    if ( old_ce.get() &&
         old_ce->isRealDir() ) {
        old_node = m_directory_presets_pt.outerFind( old_ce->getNameOfDir(), true );
    }

    if ( new_node ) {
        auto presets = wconfig->getDirectoryPresets();

        // we found an entry, check if it is different to current directory_presets
        if ( old_node == new_node ) {
        } else {
            if ( ! old_node ) {
                // store current settings for later restoration
                m_last_explicit_directory_presets.sortmode = m_dir_sort_sets.getSortMode();
                m_last_explicit_directory_presets.show_hidden = m_dir_filter_sets.getShowHidden();
                m_last_explicit_directory_presets.filters = m_dir_filter_sets.getFilters();
            } else {
                auto old_settings = presets.at( old_node->getNodeData() );
                // only store differences to old_settings
                updateLastDirSettings( old_settings );
            }

            update_directory_settings = true;
            new_settings = presets.at( new_node->getNodeData() );
        }
    } else if ( old_node != NULL ) {
        // no setting found, reset to last user default

        auto presets = wconfig->getDirectoryPresets();
        auto old_settings = presets.at( old_node->getNodeData() );
        updateLastDirSettings( old_settings );

        update_directory_settings = true;
        new_settings = m_last_explicit_directory_presets;
    }
}

void VirtualDirMode::setCurrentCE( RefCount< DMCacheEntryNWC > new_ce )
{
    bool disable_dir_watch = true;

    // special if the new CE is the same dir as the old one then don't
    // disable dir watch so we don't miss any update
    if ( new_ce.get() != NULL &&
         ce.get() != NULL ) {
        if ( ce->isRealDir() &&
             new_ce->isRealDir() &&
             ce->getNameOfDir() == new_ce->getNameOfDir() ) {
            disable_dir_watch = false;
        }
    }

    if ( disable_dir_watch ) {
        disableDirWatch( true );
    }

    if ( m_lv && ce.get() ) {
        ce->setXOffset( m_lv->getXOffset() );
        ce->setYOffset( m_lv->getYOffset() );
    }

    m_lv_ce_valid = false;

    // only reset string filter if not entering same directory
    if ( ce.get() == NULL ||
         new_ce.get() == NULL ||
         ce->getNameOfDir() != new_ce->getNameOfDir() ) {
        setEntrySearchString( filter_search_string(), false );

        finishsearchmode();
    }

    bool update_directory_settings = false;
    struct directory_presets new_settings;

    checkDirectoryPresets( new_ce, ce, update_directory_settings,
                           new_settings );

    ce = new_ce;

    if ( m_search_mode_restore_hidden ) {
        m_dir_filter_sets.setShowHidden( false );
        m_search_mode_restore_hidden = false;
    }

    if ( update_directory_settings ) {
        m_dir_sort_sets.setSortMode( new_settings.sortmode );
        m_dir_filter_sets.setShowHidden( new_settings.show_hidden );
        m_dir_filter_sets.setFilters( new_settings.filters );
    }

    if ( ce.get() != NULL ) {

        ce->updateView( false );

        for ( auto it = m_cache.begin();
              it != m_cache.end();
              it++ ) {
            if ( it->first == ce->getNameOfDir() ) {
                m_cache.erase( it );
                break;
            }
        }

        m_cache.push_front( std::make_pair( ce->getNameOfDir(),
                                            ce ) );

        if ( ! m_go_to_previous_dir_active ) {
            m_previous_dir_pos = 0;
        }

        if ( m_tab_store.active_tab < 0 ) {
            m_tab_store.active_tab = 0;
        }

        if ( (int)m_tab_store.tab_entries.size() <= m_tab_store.active_tab ) {
            m_tab_store.tab_entries.resize( m_tab_store.active_tab + 1 );
        }

        m_tab_store.tab_entries[m_tab_store.active_tab].ce = ce;

        if ( ce->getActiveEntryPos() < 0 ) {

            if ( m_show_dotdot ) {
                ce->setActiveEntryPos( -1 );
            } else {
                for ( int pos = 0;
                      pos < (int)ce->getSize();
                      pos++ ) {
                    if ( ce->getUse( pos ) ) {
                        ce->setActiveEntryPos( pos );
                        break;
                    }
                }
            }
        }

        if ( m_dirwatcher.watch_mode == true &&
             ce->isRealDir() ) {

            if ( disable_dir_watch ) {
                enableDirWatch( ce->getNameOfDir() );
            }
        }
        m_dirwatcher.last_update = time( NULL);

        if ( m_breadcrumb.enabled ) {
            if ( ce->isRealDir() ) {
                update_breadcrumb( ce->getNameOfDir() );
            } else {
                update_breadcrumb( ce->getCommonPrefix() );
            }
        }
    }

    m_last_info_line_fse = NULL;
    m_last_info_line_ft = NULL;

    updateCDI();

    rebuildView();

    cleanupCache();
}

int VirtualDirMode::getSelFiles( std::list< NM_specialsourceExt > &return_list,
                                 lm_getfiles_t get_mode,
                                 bool unselect,
                                 int options )
{
    return getSelFiles( return_list, get_mode, unselect, options, CONSIDER_PREFIX_ELEMENTS_ONLY );
}

int VirtualDirMode::getSelFiles( std::list< NM_specialsourceExt > &return_list,
                                 lm_getfiles_t get_mode,
                                 bool unselect,
                                 int options,
                                 vdm_get_sel_mode_t consider_mode )
{
    int added = 0;

    if ( ce.get() == NULL ) return -1;

    if ( get_mode != LM_GETFILES_ONLYACTIVE ) {
        createSelectionCheckpoint();
    }

    bool use_candidates = true;

    if ( consider_mode == CONSIDER_ALL_ELEMENTS ) {
        use_candidates = false;
    }

    if ( ce->isRealDir() ) use_candidates = false;

    PrefixPathTree ppt;
    std::list< std::pair< NM_specialsourceExt, int > > candidates;

    if ( get_mode != LM_GETFILES_ONLYACTIVE ) {
        for ( int pos = 0;
              pos < (int)ce->getSize();
              pos++ ) {
            if ( ce->getUse( pos ) ) {
                auto es = ce->getEntry( pos );

                if ( ! es ) continue;

                auto fse = es->getNWCEntry();

                if ( ! fse ) continue;

                if ( fse->getBasename() != ".." &&
                     es->getSelected() ) {
                    FileEntry fe( *fse );
                    fe.filetype = es->getFiletype();
                    fe.setFiletypeFileOutput( es->getFiletypeFileOutput() );
                    fe.setMimeType( es->getMimeType() );

                    if ( use_candidates ) {
                        candidates.push_back( std::make_pair( NM_specialsourceExt( &fe, pos,
                                                                                   ( options & LM_GETFILES_OPTION_CUSTOM_ATTRIBUTES ) ? es->getCustomAttribute() : "" ),
                                                              pos ) );

                        ppt.insert( fse->getFullname() );
                    } else {
                        return_list.push_back( NM_specialsourceExt( &fe, pos,
                                                                    ( options & LM_GETFILES_OPTION_CUSTOM_ATTRIBUTES ) ? es->getCustomAttribute() : "" ) );
                        added++;

                        if ( unselect ) {
                            setEntrySelectionState( pos, false );
                        }
                    }
                }
            }
        }
    }

    bool list_empty;

    if ( use_candidates ) {
        list_empty = candidates.empty();
    } else {
        list_empty = return_list.empty();
    }

    // for ONLYACTIVE always take the active
    // otherwise only if not ONLYSELECT and empty list
    if ( get_mode == LM_GETFILES_ONLYACTIVE ||
         ( get_mode != LM_GETFILES_ONLYSELECT && list_empty ) ) {
        if ( ce->getActiveEntryPos() >= 0 ) {
            int row = m_lv->getActiveRow();
            if ( m_lv->isValidRow( row ) == true ) {
                if ( m_lv->getData( row, -1 ) == ce->getActiveEntryPos() ) {
                    auto es = ce->getEntry( ce->getActiveEntryPos() );

                    if ( es ) {
                        auto fse = es->getNWCEntry();

                        if ( fse ) {

                            if ( fse->getBasename() != ".." ) {
                                FileEntry fe( *fse );
                                fe.filetype = es->getFiletype();
                                fe.setFiletypeFileOutput( es->getFiletypeFileOutput() );
                                fe.setMimeType( es->getMimeType() );

                                if ( use_candidates ) {
                                    candidates.push_back( std::make_pair( NM_specialsourceExt( &fe,
                                                                                               ce->getActiveEntryPos(),
                                                                                               ( options & LM_GETFILES_OPTION_CUSTOM_ATTRIBUTES ) ? es->getCustomAttribute() : "" ),
                                                                          ce->getActiveEntryPos() ) );

                                    ppt.insert( fse->getFullname() );
                                } else {
                                    return_list.push_back( NM_specialsourceExt( &fe, ce->getActiveEntryPos(),
                                                                                ( options & LM_GETFILES_OPTION_CUSTOM_ATTRIBUTES ) ? es->getCustomAttribute() : "" ) );
                                    added++;

                                    if ( unselect ) {
                                        setEntrySelectionState( ce->getActiveEntryPos(), false );
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    if ( use_candidates ) {
        for ( auto &e : candidates ) {
            auto res = ppt.lookup( e.first.entry()->fullname );

            if ( res == PrefixPathTree::IS_MATCH ) {
                return_list.push_back( e.first );
                added++;

                if ( unselect ) {
                    setEntrySelectionState( e.second, false );
                }
            }
        }
    }
  
    return added;
}

int VirtualDirMode::copySourceList( const std::list< NM_specialsourceExt * > &inlist,
                                    std::list< NM_specialsourceExt > &outlist,
                                    const bool external_sources )
{
    int added = 0;

    if ( ce.get() == NULL ) return -1;

    for ( auto &entry : inlist ) {
        if ( ! entry ) continue;

        if ( ! entry->entry() ) continue;

        if ( external_sources ) {
            FileEntry fe( *entry->entry() );

            outlist.push_back( NM_specialsourceExt( &fe, -1 ) );

            added++;
        } else {
            int pos = findPosition( -1, entry->entry()->fullname );

            if ( pos >= 0 ) {
                auto es = ce->getEntry( pos );

                if ( ! es ) continue;

                auto fse = es->getNWCEntry();

                if ( ! fse ) continue;

                FileEntry fe( *fse );
                fe.filetype = es->getFiletype();

                outlist.push_back( NM_specialsourceExt( &fe, pos ) );

                added++;
            }
        }
    }

    return added;
}

std::string VirtualDirMode::getCurrentDirectory()
{
    if ( ! ce.get() ) return "";

    if ( ce->isRealDir() ) {
        return m_visible_dirname;
    } else {
        return m_common_prefix;
    }
}

bool VirtualDirMode::setEntrySelectionState( const std::string &filename, bool state )
{
    if ( ce.get() == NULL ) return false;

    int total_entries = ce->getSize();
    int max_entries_to_check = total_entries;
    int forward_pos = 0;
    int backward_pos = total_entries - 1;

    if ( m_deselect_info.last_pos >= 0 &&
         m_deselect_info.last_pos < total_entries ) {
        // start at last pos

        forward_pos = ( m_deselect_info.last_pos + 1 ) % max_entries_to_check;
        backward_pos = m_deselect_info.last_pos;
    }

    while ( max_entries_to_check > 0 ) {

        for ( int direction = 0; direction < 2; direction++ ) {
            int pos;

            if ( direction == 0 ) {
                pos = forward_pos;
            } else {
                pos = backward_pos;
            }

            auto es = ce->getEntry( pos );

            if ( ! es ) continue;

            auto fse = es->getNWCEntry();

            if ( ! fse ) continue;

            if ( fse->getFullname() == filename ) {

                setEntrySelectionState( pos, state );

                m_deselect_info.last_pos = pos;
                return true;
            }
        }

        forward_pos = ( forward_pos + 1 ) % total_entries;
        backward_pos = ( backward_pos - 1 + total_entries ) % total_entries;

        if ( max_entries_to_check > 2 ) {
            max_entries_to_check -= 2;
        } else {
            max_entries_to_check -= 1;
        }
    }

    return false;
}

void VirtualDirMode::enter_dir( const std::list< RefCount< ArgClass > > &args )
{
    if ( Worker::getKVPStore().getIntValue( "init-state", 0 ) == 1 &&
         m_tabs_loaded ) {

        if ( Worker::getKVPStore().getStringValue( "initial-path-source", "" ) == "config" ) {
            // skip if from config and tabs has been loaded
            return;
        } else {
            // if from arg and tabs has been loaded, add new tab
            newTab();
        }
    }

    if ( ! args.empty() ) {
        std::string entry;
        bool stored = false;

        for ( auto &e : args ) {
            if ( e.get() != NULL ) {
                StringArg *a = dynamic_cast< StringArg *>( e.get() );
                KeyValueArg *b = dynamic_cast< KeyValueArg *>( e.get() );

                if ( a ) {
                    entry = a->getValue();
                } else if ( b && b->key() == "pathDBStored" ) {
                    auto c = std::dynamic_pointer_cast< BoolArg >( b->value() );
                    if ( c ) {
                        stored = c->getValue();
                    }
                }
            }
        }

        if ( ! entry.empty() ) {
            NWC::FSEntry fse( entry );

            if ( fse.isDir( true ) ) {
                showDir( entry,
                         stored == true ? SHOWDIR_DONT_PERS_STORE_PATH : SHOWDIR_PERS_STORE_PATH );
            } else {

                if ( ! activateEntry( fse.getFullname(), false ) ) {
                    std::string dirname = fse.getDirname();
                    std::string basename = fse.getBasename();

                    if ( showDir( dirname,
                                  stored == true ? SHOWDIR_DONT_PERS_STORE_PATH : SHOWDIR_PERS_STORE_PATH ) == 0 ) {
                        activateEntry( basename, true );
                    }
                }
            }
        }
    }
}

void VirtualDirMode::activate_entry( const std::list< RefCount< ArgClass > > &args )
{
    if ( ! args.empty() ) {
        RefCount< ArgClass > a1 = args.front();
        if ( a1.get() != NULL ) {
            StringArg *a2 = dynamic_cast< StringArg *>( a1.get() );
            if ( a2 != NULL ) {
                std::string entry = a2->getValue();

                activateEntry( entry, true );
            }
        }
    }
}

void VirtualDirMode::show_entry( const std::list< RefCount< ArgClass > > &args )
{
    if ( args.empty() ) {
        return;
    }

    std::string entry;

    for ( auto &e : args ) {
        if ( e.get() != NULL ) {
            StringArg *a = dynamic_cast< StringArg *>( e.get() );

            if ( a ) {
                entry = a->getValue();
            }
        }
    }

    if ( entry.empty() ) {
        return;
    }

    NWC::FSEntry fse( entry );

    std::string dirname = fse.getDirname();
    std::string basename = fse.getBasename();

    if ( dirname.empty() ||
         showDir( dirname,
                  SHOWDIR_DONT_PERS_STORE_PATH ) == 0 ) {
        activateEntry( basename, true );
    }
}

bool VirtualDirMode::isyours( Widget *elem )
{
    if ( elem == m_lv ) return true;
    return false;
}

void VirtualDirMode::command_simdd()
{
    if ( ! ce.get() ) return;

    auto es = ce->getEntry( ce->getActiveEntryPos() );

    if ( es ) {

        // if filetype is not yet set, issue check

        if ( ! es->getFiletype() ) {
            const NWC::FSEntry *fse = es->getNWCEntry();

            if ( fse && fse->isDir( true ) == false ) {
                addActiveRowForFTCheck();

                m_delayed_filetype_action.cb = [this]() { if ( is_active_entry( m_delayed_filetype_action.path ) ) { command_simdd(); } };

                setDelayedFTAction( fse->getFullname() );
                
                return;
            }
        }

        startAction( *es );
    } else if ( m_show_dotdot && m_lv->getActiveRow() == 0 ) {
        command_parentdir( true );
    }
}

static bool checkForEmptyActionList( WCFiletype *ft )
{
    bool res = true;

    while ( ft != NULL ) {
        auto l = ft->getDoubleClickActions();
        //TODO ParentOp pruefen, da so trotz Aktion leere Liste vorkommen kann
        if ( ! l.empty() ) {
            res = false;
            break;
        }
        ft = ft->getParentType();
    }
    return res;
}

void VirtualDirMode::startAction( const NWCEntrySelectionState &es )
{
    WCFiletype *ft;
    bool fallbackEnterDir;

    const NWC::FSEntry *fse = es.getNWCEntry();

    if ( fse == NULL ) return;

    if ( fse->isBrokenLink() == true ) return;

    finishsearchmode();

    fallbackEnterDir = false;

    parentlister->getWorker()->storePathPersIfInProgress( fse->getFullname() );

    ft = es.getFiletype();
    if ( ft == NULL ) {
        if ( fse->isDir( true ) == true ) {
            ft = wconfig->getdirtype();
        } else {
            ft = wconfig->getnotyettype();
        }
    }

    if ( ft != NULL ) {
        if ( fse->isDir( true ) == true ) {
            auto l = ft->getDoubleClickActions();
            if ( l.empty() ) fallbackEnterDir = true;
        }
        if ( fallbackEnterDir == false ) {
            if ( checkForEmptyActionList( ft ) == true ) {
                // empty action list
                ActionMessage amsg( parentlister->getWorker() );
                auto spop = std::make_shared< StartProgOp >();

                // prepare command list with startprogop
                command_list_t templist;
                templist.push_back( spop );
                spop->setStart( StartProgOp::STARTPROGOP_START_IN_TERMINAL_AND_WAIT4KEY );
                spop->setRequestFlags( true );
                spop->setGlobal( true );
                spop->setGUIMsg( catalog.getLocale( 646 ) );
                //TODO Vielleicht auch Moeglichkeit haben, um Initialfenster im startprogop einzustellen

                // workaround for handling ..
                if ( fse->getBasename() == ".." ) {
                    amsg.mode = amsg.AM_MODE_SPECIAL;

                    FileEntry tfe( *fse );

                    amsg.setFE( &tfe );
                } else {
                    prepareContext( amsg );
                }

                amsg.filetype = ft;
                amsg.m_action_descr = RefCount<ActionDescr>( new DoubleClickAction::DoubleClickActionDescr() );
                parentlister->getWorker()->interpret( templist, &amsg );
            } else {
                ActionMessage amsg( parentlister->getWorker() );

                // workaround for handling ..
                if ( fse->getBasename() == ".." ) {
                    amsg.mode = amsg.AM_MODE_SPECIAL;

                    FileEntry tfe( *fse );

                    amsg.setFE( &tfe );
                } else {
                    prepareContext( amsg );
                }

                amsg.filetype = ft;
                amsg.m_action_descr = RefCount<ActionDescr>( new DoubleClickAction::DoubleClickActionDescr() );
                parentlister->getWorker()->interpret( ft->getDoubleClickActions(), &amsg );
            }
        }
    } else {
        if ( fse->isDir( true ) == true ) {
            fallbackEnterDir = true;
        }
    }

    if ( fallbackEnterDir == true ) {
        showDir( fse->getFullname() );
    }
}

/**
 * this function will prepare the elements use to run the action
 * it will put all selected entries into the actionmessage
 */
int VirtualDirMode::prepareContext( ActionMessage &amsg )
{
    RefCount< NWC::VirtualDir > vd( new NWC::VirtualDir( "context", false, false, false ) );
    std::list< NM_specialsourceExt > tlist;
    std::string active_fullname;

    // first get the active entry
    int res = getSelFiles( tlist, LM_GETFILES_ONLYACTIVE, false );
    if ( res > 0 ) {
        for ( const auto &se : tlist ) {
            if ( ! se.entry() ) continue;

            vd->add( NWC::FSEntry( se.entry()->fullname ) );

            active_fullname = se.entry()->fullname;
        }
        tlist.clear();
    }

    // now also get the selected entries but skip the active entry

    res = getSelFiles( tlist, LM_GETFILES_ONLYSELECT, false );
    if ( res > 0 ) {
        for ( const auto &se : tlist ) {
            if ( active_fullname != se.entry()->fullname ) {
                vd->add( NWC::FSEntry( se.entry()->fullname ) );
            }
        }
    }

    if ( ! vd->empty() ) {
        amsg.setEntriesToConsider( vd );
        return 1;
    }

    return 0;
}

int VirtualDirMode::getRowForCEPos( int pos, bool dont_check_lv_data )
{
    if ( pos < 0 || pos >= (int)m_ce_pos_to_view_row.size() ) return -1;

    int row = m_ce_pos_to_view_row[pos];

    if ( dont_check_lv_data == false ) {
        if ( ! m_lv->isValidRow( row ) ) return -1;

        if ( m_lv->getData( row, -1 ) != pos ) return -1;
    }

    return row;
}

bool VirtualDirMode::isPosVisible( int pos )
{
    if ( ! ce.get() ) return false;

    if ( pos < 0 || pos >= (int)ce->getSize() ) return false;

    int row = getRowForCEPos( pos );

    return m_lv->isRowVisible( row );
}

bool VirtualDirMode::setEntrySelectionState( int pos, bool state )
{
    if ( ! ce.get() ) return false;

    if ( pos < 0 || pos >= (int)ce->getSize() ) return false;

    auto es = ce->getEntry( pos );

    if ( ! es ) return false;

    auto fse = es->getNWCEntry();

    if ( ! fse ) return false;

    if ( es->getSelected() != state ) {
        ce->modifyEntry( pos,
                         [state]( NWCEntrySelectionState &tes ) { tes.setSelected( state ); } );

        showCacheState();

        int row = getRowForCEPos( pos );

        if ( row >= 0 ) {
            m_lv->setSelect( row, state );
        }

        if ( state == false ) {
            m_selection_checkpoint_dirty = true;
        }
    }

    return true;
}

bool VirtualDirMode::activateEntry( const std::string &filename,
                                    bool only_compare_basename )
{
    if ( ! ce.get() ) return false;

    for ( int pos = 0;
          pos < (int)ce->getSize();
          pos++ ) {
        if ( ce->getUse( pos ) ) {
            auto es = ce->getEntry( pos );

            if ( ! es ) continue;

            auto fse = es->getNWCEntry();

            if ( ! fse ) continue;

            if ( ( only_compare_basename && fse->getBasename() == filename ) ||
                 ( ! only_compare_basename && fse->getFullname() == filename ) ) {

                ce->setActiveEntryPos( pos );

                int row = getRowForCEPos( pos );

                if ( row >= 0 ) {
                    makeListViewRowActive( row );
                
                    m_lv->showActive();

                    return true;
                }
            }
        }
    }

    return false;
}

void VirtualDirMode::updateColor( int row )
{
    if ( row < 0 || row >= m_lv->getElements() ) return;

    if ( ! ce.get() ) return;

    if ( m_show_dotdot && row == 0 ) {
        int fg[4] = { 1, 1, 1, 1 }, bg[4] = { 0, 0, 0, 0 };

        fg[0] = aguix->getFaces().getColor( "dirview-dir-normal-fg" );
        bg[0] = aguix->getFaces().getColor( "dirview-dir-normal-bg" );
        fg[1] = fg[0];
        bg[1] = bg[0];
        fg[2] = aguix->getFaces().getColor( "dirview-dir-active-fg" );
        bg[2] = aguix->getFaces().getColor( "dirview-dir-active-bg" );
        fg[3] = fg[2];
        bg[3] = bg[2];

        m_lv->setFG( row, FieldListView::CC_NORMAL, fg[0] );
        m_lv->setBG( row, FieldListView::CC_NORMAL, bg[0] );
        m_lv->setFG( row, FieldListView::CC_SELECT, fg[1] );
        m_lv->setBG( row, FieldListView::CC_SELECT, bg[1] );
        m_lv->setFG( row, FieldListView::CC_ACTIVE, fg[2] );
        m_lv->setBG( row, FieldListView::CC_ACTIVE, bg[2] );
        m_lv->setFG( row, FieldListView::CC_SELACT, fg[3] );
        m_lv->setBG( row, FieldListView::CC_SELACT, bg[3] );

        return;
    }

    int ce_id = m_lv->getData( row, -1 );

    if ( ce->getUse( ce_id ) ) {
        const NWCEntrySelectionState *es = ce->getEntry( ce_id );

        if ( ! es ) return;

        const NWC::FSEntry *fse = es->getNWCEntry();

        if ( ! fse ) return;

        int fg[4] = { 1, 1, 1, 1 }, bg[4] = { 0, 0, 0, 0 };
        int missing_colors = FileEntryCustomColor::ALL_COLORS;

        const FileEntryColor &colors = es->getColor();

        int custom_color_set = colors.getCustomColorSet();
        int override_color_set =  colors.getOverrideColorSet();

        if ( override_color_set != 0 ) {
            if ( override_color_set & FileEntryCustomColor::NORMAL ) {
                colors.getOverrideColor( FileEntryCustomColor::NORMAL, &fg[0], &bg[0] );
                missing_colors &= ~FileEntryCustomColor::NORMAL;
            }
            if ( override_color_set & FileEntryCustomColor::SELECT ) {
                colors.getOverrideColor( FileEntryCustomColor::SELECT, &fg[1], &bg[1] );
                missing_colors &= ~FileEntryCustomColor::SELECT;
            }
            if ( override_color_set & FileEntryCustomColor::ACTIVE ) {
                colors.getOverrideColor( FileEntryCustomColor::ACTIVE, &fg[2], &bg[2] );
                missing_colors &= ~FileEntryCustomColor::ACTIVE;
            }
            if ( override_color_set & FileEntryCustomColor::SELACT ) {
                colors.getOverrideColor( FileEntryCustomColor::SELACT, &fg[3], &bg[3] );
                missing_colors &= ~FileEntryCustomColor::SELACT;
            }
        }
        if ( custom_color_set != 0 ) {
            if ( ( custom_color_set & FileEntryCustomColor::NORMAL ) &&
                 ( missing_colors & FileEntryCustomColor::NORMAL ) ) {
                colors.getCustomColor( FileEntryCustomColor::NORMAL, &fg[0], &bg[0] );
                missing_colors &= ~FileEntryCustomColor::NORMAL;
            }
            if ( ( custom_color_set & FileEntryCustomColor::SELECT  ) &&
                 ( missing_colors & FileEntryCustomColor::SELECT ) ) {
                colors.getCustomColor( FileEntryCustomColor::SELECT, &fg[1], &bg[1] );
                missing_colors &= ~FileEntryCustomColor::SELECT;
            }
            if ( ( custom_color_set & FileEntryCustomColor::ACTIVE  ) &&
                 ( missing_colors & FileEntryCustomColor::ACTIVE ) ) {
                colors.getCustomColor( FileEntryCustomColor::ACTIVE, &fg[2], &bg[2] );
                missing_colors &= ~FileEntryCustomColor::ACTIVE;
            }
            if ( ( custom_color_set & FileEntryCustomColor::SELACT  ) &&
                 ( missing_colors & FileEntryCustomColor::SELACT ) ) {
                colors.getCustomColor( FileEntryCustomColor::SELACT, &fg[3], &bg[3] );
                missing_colors &= ~FileEntryCustomColor::SELACT;
            }
        }
        if ( missing_colors != 0 ) {
            if ( fse->isDir( true ) == true && ! fse->isBrokenLink() ) {
                if ( missing_colors & FileEntryCustomColor::NORMAL ) {
                    fg[0] = aguix->getFaces().getColor( "dirview-dir-normal-fg" );
                    bg[0] = aguix->getFaces().getColor( "dirview-dir-normal-bg" );
                }
                if ( missing_colors & FileEntryCustomColor::SELECT ) {
                    fg[1] = aguix->getFaces().getColor( "dirview-dir-select-fg" );
                    bg[1] = aguix->getFaces().getColor( "dirview-dir-select-bg" );
                }
                if ( missing_colors & FileEntryCustomColor::ACTIVE ) {
                    fg[2] = aguix->getFaces().getColor( "dirview-dir-active-fg" );
                    bg[2] = aguix->getFaces().getColor( "dirview-dir-active-bg" );
                }
                if ( missing_colors & FileEntryCustomColor::SELACT ) {
                    fg[3] = aguix->getFaces().getColor( "dirview-dir-selact-fg" );
                    bg[3] = aguix->getFaces().getColor( "dirview-dir-selact-bg" );
                }
            } else {
                if ( missing_colors & FileEntryCustomColor::NORMAL ) {
                    if ( fse->isBrokenLink() ) {
                        fg[0] = 4;
                    } else {
                        fg[0] = aguix->getFaces().getColor( "dirview-file-normal-fg" );
                    }
                    bg[0] = aguix->getFaces().getColor( "dirview-file-normal-bg" );
                }
                if ( missing_colors & FileEntryCustomColor::SELECT ) {
                    fg[1] = aguix->getFaces().getColor( "dirview-file-select-fg" );
                    bg[1] = aguix->getFaces().getColor( "dirview-file-select-bg" );
                }
                if ( missing_colors & FileEntryCustomColor::ACTIVE ) {
                    if ( fse->isBrokenLink() ) {
                        fg[2] = 4;
                    } else {
                        fg[2] = aguix->getFaces().getColor( "dirview-file-active-fg" );
                    }
                    bg[2] = aguix->getFaces().getColor( "dirview-file-active-bg" );
                }
                if ( missing_colors & FileEntryCustomColor::SELACT ) {
                    fg[3] = aguix->getFaces().getColor( "dirview-file-selact-fg" );
                    bg[3] = aguix->getFaces().getColor( "dirview-file-selact-bg" );
                }
            }
        }

        m_lv->setFG( row, FieldListView::CC_NORMAL, fg[0] );
        m_lv->setBG( row, FieldListView::CC_NORMAL, bg[0] );
        m_lv->setFG( row, FieldListView::CC_SELECT, fg[1] );
        m_lv->setBG( row, FieldListView::CC_SELECT, bg[1] );
        m_lv->setFG( row, FieldListView::CC_ACTIVE, fg[2] );
        m_lv->setBG( row, FieldListView::CC_ACTIVE, bg[2] );
        m_lv->setFG( row, FieldListView::CC_SELACT, fg[3] );
        m_lv->setBG( row, FieldListView::CC_SELACT, bg[3] );
    }
}

bool VirtualDirMode::ft_request_list::isFull()
{
    if ( m_elements.size() >= m_max_elements ) return true;
    return false;
}

bool VirtualDirMode::ft_request_list::isEmpty()
{
    return m_elements.empty();
}

int VirtualDirMode::ft_request_list::put( const ft_request_list_entry &elem )
{
    if ( isFull() == true ) return -1;

    m_elements.push_back( elem );

    m_elements_set.insert( elem.fullname );

    return (int)m_elements.size();
}

VirtualDirMode::ft_request_list::ft_request_list_entry VirtualDirMode::ft_request_list::remove()
{
    if ( m_elements.empty() ) {
        abort();
    }

    ft_request_list_entry te = m_elements.front();
    m_elements.pop_front();

    m_elements_set.erase( te.fullname );

    return te;
}

VirtualDirMode::ft_request_list::ft_request_list()
{
    m_max_elements = 10;
}

VirtualDirMode::ft_request_list::~ft_request_list()
{
}

VirtualDirMode::ft_request_list::ft_request_list_entry VirtualDirMode::ft_request_list::gettop()
{
    if ( m_elements.empty() ) {
        abort();
    }

    return m_elements.front();
}

bool VirtualDirMode::ft_request_list::contains( const std::string &fullname )
{
    if ( m_elements_set.find( fullname ) == m_elements_set.end() ) return false;

    return true;
}

void VirtualDirMode::ft_request_list_clear()
{
    parentlister->getWorker()->removeStatebarText( m_delayed_filetype_action.info_text );

    while ( m_request_list.isEmpty() == false ) {
        m_request_list.remove();
    }

    for ( auto &cache_entry_pair : m_cache ) {
        cache_entry_pair.second->resetFiletypeCheckState();
    }

    m_vis_changed = true;

    m_ft_thread.clearRequests();
}

void VirtualDirMode::ft_result_list_clear()
{
    m_ft_thread.result_list_clear();
}

/*
 * ft_list_clear
 *
 * will delete all filetypes from list
 *
 * only allowed for master thread
 */
void VirtualDirMode::ft_list_clear()
{
    m_ft_thread.clear_filetypes();
}

/*
 * ft_list_update
 *
 * will update the filetype-list to the current config
 *
 * only allowed for master thread
 */
void VirtualDirMode::ft_list_update()
{
    List *ftlist;

    ftlist = wconfig->getFiletypes();
    m_ft_thread.copy_filetypes( ftlist );
}

void VirtualDirMode::insert_CE_element_into_FT_request( int ce_pos )
{
    auto es = ce->getEntry( ce_pos );

    if ( ! es ) return;
    
    auto fse = es->getNWCEntry();

    if ( fse &&
         es->getFiletype() == NULL &&
         fse->isDir( true ) == false &&
         ! m_request_list.contains( fse->getFullname() ) ) {

        ft_request_list::ft_request_list_entry te;
        te.fullname = fse->getFullname();
        te.pos = ce_pos;

        bool dontCheckContent = ce->getDontCheckContent();

        if ( dontCheckContent == false &&
             wconfig->getDontCheckVirtual() == true ) {
            if ( worker_islocal( te.fullname.c_str() ) == 0 ) dontCheckContent = true;
        }

        m_request_list.put( te );

        NM_Filetype_Thread::check_entry_t check_entry;

        check_entry.fullname = te.fullname;
        check_entry.dont_check_content = dontCheckContent;

        m_ft_thread.putRequest( check_entry );
    }
}

void VirtualDirMode::fillFiletypeRequests()
{
    if ( m_vis_changed == false ) {
        if ( m_lv->getYOffset() != m_old_lv_yoffset ) m_vis_changed = true;
    }

    // first fill the slave buffer
        
    if ( m_vis_changed == true && ! m_request_list.isFull() ) {
        int useelements = a_min( m_lv->getMaxDisplayV(), m_lv->getElements() - m_lv->getYOffset() );
          
        int top_row = m_lv->getYOffset();

        if ( m_show_dotdot && top_row == 0 ) {
            top_row++;
        }

        auto top_es = ce->getEntry( m_lv->getData( top_row, -1 ) );

        while ( top_es != NULL && useelements > 0 && ! m_request_list.isFull() ) {

            // add topfe because:
            // 1.it's visible
            // 2.type is not checked
            // 3.it's not queued
            // 4.m_request_list is not full
            // 5.it's a file

            insert_CE_element_into_FT_request( m_lv->getData( top_row, -1 ) );

            useelements--;
            
            // find next visible entry
            top_row++;

            if ( m_lv->isValidRow( top_row ) ) {
                top_es = ce->getEntry( m_lv->getData( top_row, -1 ) );
            } else top_es = NULL;
        }

        if ( useelements == 0 ) {
            // all visible are already known or we added them
            m_vis_changed = false;
            m_old_lv_yoffset = m_lv->getYOffset();
        }
    }
        
    int pos = ce->getEntryForFiletypeCheck();

    auto es = ce->getEntry( pos );

    while ( es != NULL && ! m_request_list.isFull() ) {
        insert_CE_element_into_FT_request( pos );

        pos = ce->updateNextEntryForFiletypeCheck();

        es = ce->getEntry( pos );
    }

    // check for inconsistency

    if ( m_request_list.isEmpty() &&
         ce->getNumberOfUncheckedEntries() > 0 &&
         ce->getEntryForFiletypeCheck() == DMCacheEntryNWC::filetype_check::NO_ENTRY ) {

        // nothing requested, some are still unchecked, and next check is none
        // => restart in for next update

        ce->resetFiletypeCheckState();
    }
}

void VirtualDirMode::addActiveRowForFTCheck()
{
    if ( ! ce.get() ) return;

    // clear pending request so this request is processed next, all
    // others will be added again automatically
    ft_request_list_clear();
    insert_CE_element_into_FT_request( ce->getActiveEntryPos() );
}

void VirtualDirMode::gatherFiletypeResults()
{
    bool doflush = false;
    // now check for results
        
    m_ft_thread.result_list_lock();

    NM_Filetype_Thread::check_filetype_result rte;
    ft_request_list::ft_request_list_entry te;

    while ( m_ft_thread.result_list_is_empty_locked() == false ) {
        // alle Resultate entnehmen
        rte = m_ft_thread.result_list_remove_locked();
        // mit oberstem Element von m_request_list vergleichen

        if ( ! m_request_list.isEmpty() ) {
            te = m_request_list.gettop();
            if ( te.fullname == rte.entry.fullname ) {
                // correct answer
#ifdef DEBUG
                printf( "correct answer: %s\n", rte.entry.fullname.c_str() );
#endif

                te = m_request_list.remove();

                int pos = findPosition( te.pos, te.fullname );

                auto es = ce->getEntry( pos );

                if ( es ) {
                    WCFiletype *ft;

                    if ( ! rte.ft_index.empty() ) {
                        ft = NM_Filetype_Thread::findFiletype( wconfig, rte.ft_index );
                    } else {
                        ft = wconfig->getunknowntype();
                    }
                
                    if ( ft != NULL ) {
                        ce->modifyEntry( pos,
                                         [ft, &rte]( NWCEntrySelectionState &tes ) {
                                             tes.setFiletype( ft );
                                             if ( rte.custom_color.getColorSet() != 0 ) {
                                                 tes.setCustomColor( rte.custom_color );
                                             }
                                             if ( ! rte.filetype_file_output.empty() ) {
                                                 tes.setFiletypeFileOutput( rte.filetype_file_output );
                                             }
                                             if ( ! rte.mime_type.empty() ) {
                                                 tes.setMimeType( rte.mime_type );
                                             }
                                         } );

                        int row = getRowForCEPos( pos );
  
                        if ( m_lv->isValidRow( row ) == true ) {
                            updateLVRow( row );
                            doflush = true;
                        }

                        if ( m_delayed_filetype_action.path == te.fullname ) {
                            m_delayed_filetype_action.pending = false;
                            parentlister->getWorker()->removeStatebarText( m_delayed_filetype_action.info_text );
                        }
                    }
                }
            } else {
#ifdef DEBUG
                printf( "wrong answer: %s\n", rte.entry.fullname.c_str() );
#endif
                parentlister->getWorker()->removeStatebarText( m_delayed_filetype_action.info_text );
            }
        }
    }
        
    m_ft_thread.result_list_unlock();

    if ( doflush ) {
        m_lv->simpleRedrawUpdatedRows();
    }
}

void VirtualDirMode::updateFiletypeCheck()
{
    bool filter_update = false;

    fillFiletypeRequests();

    gatherFiletypeResults();

    if ( ce->getNumberOfUncheckedEntries() > 0 ) {
        // still some work to do so update m_busy_flag
        time_t now = time( NULL );
        if ( m_last_busy_flag_update < now || m_busy_flag == 0 ) {
            m_last_busy_flag_update = now;
            m_busy_flag++;
            if ( m_busy_flag > 4 ) m_busy_flag = 1;
            setName();

#if 0
            // this actually works but I find it too disturbing to have
            // updates every second or so
            // I leave it here just in case
            if ( m_last_no_of_unchecked_entries == 0 ) {
                m_last_no_of_unchecked_entries = ce->getNumberOfUncheckedEntries();
            } else if ( abs( m_last_no_of_unchecked_entries - ce->getNumberOfUncheckedEntries() ) > 20 ) {
                m_last_no_of_unchecked_entries = ce->getNumberOfUncheckedEntries();
                filter_update = true;
            }
#endif
        }
    } else {
        // recognition done so clear m_busy_flag
        if ( m_busy_flag > 0 ) {
            m_busy_flag = 0;
            setName();

            m_last_no_of_unchecked_entries = 0;
            filter_update = true;
        }
    }

    if ( filter_update &&
         m_filtered_search_state.non_empty_filter_active == true &&
         m_filtered_search_state.getFilter().isExprFilter() &&
         m_filtered_search_state.getContainsTypeExpression() ) {

        // actually I only want to trigger the re-evaluation of
        // the filter expression, but the bookmark update code
        // handles this already so I use it here
        bookmarksChanged();

        updateOnBookmarkChange();
    }
}

int VirtualDirMode::findPosition( int pos, const std::string &fullname )
{
    if ( ! ce.get() ) return -1;

    auto es = ce->getEntry( pos );

    if ( es ) {
        auto fse = es->getNWCEntry();

        if ( fse ) {
            if ( fse->getFullname() == fullname ) return pos;
        }
    }

    // not found, search for it

    for ( pos = 0; 
          pos < (int)ce->getSize();
          pos++ ) {
        es = ce->getEntry( pos );

        if ( ! es ) continue;

        auto fse = es->getNWCEntry();

        if ( ! fse ) continue;

        if ( fse->getFullname() == fullname ) return pos;
    }

    return -1;
}

void VirtualDirMode::setSortmode( int nmode )
{
    if ( ISVALID_SORTMODE( nmode ) ) {
        nmode &= 0xff | SORT_REVERSE | SORT_DIRLAST | SORT_DIRMIXED;

        m_dir_sort_sets.setSortMode( nmode );

        if ( m_lv ) {
            // aktualisieren
            rebuildView();

            m_lv->centerActive();
        }

        setName();
    }
}

void VirtualDirMode::changeSortModeForField( int field )
{
    int side = parentlister->getSide();
    const std::vector<WorkerTypes::listcol_t> *sets;
    int used_fields;
    int newsortmode = m_dir_sort_sets.getSortMode();

    if ( m_use_custom_columns ) {
        sets = &m_custom_columns;
    } else {
        sets = wconfig->getVisCols( side );
    }

    if ( sets == NULL ) return;
  
    used_fields = sets->size();
  
    if ( field >= 0 && field < used_fields ) {
        switch ( (*sets)[field] ) {
            case WorkerTypes::LISTCOL_NAME:
                if ( ( newsortmode & 0xff ) == SORT_NAME ) {
                    // toggle reverse
                    newsortmode ^= SORT_REVERSE;
                } else {
                    newsortmode = SORT_NAME | ( newsortmode & ~0xff );
                    newsortmode &= ~SORT_REVERSE;
                }
                break;
            case WorkerTypes::LISTCOL_SIZE:
            case WorkerTypes::LISTCOL_SIZEH:
                if ( ( newsortmode & 0xff ) == SORT_SIZE ) {
                    // toggle reverse
                    newsortmode ^= SORT_REVERSE;
                } else {
                    newsortmode = SORT_SIZE | ( newsortmode & ~0xff );
                    newsortmode &= ~SORT_REVERSE;
                }
                break;
            case WorkerTypes::LISTCOL_TYPE:
                if ( ( newsortmode & 0xff ) == SORT_TYPE ) {
                    // toggle reverse
                    newsortmode ^= SORT_REVERSE;
                } else {
                    newsortmode = SORT_TYPE | ( newsortmode & ~0xff );
                    newsortmode &= ~SORT_REVERSE;
                }
                break;
            case WorkerTypes::LISTCOL_OWNER:
                if ( ( newsortmode & 0xff ) == SORT_OWNER ) {
                    // toggle reverse
                    newsortmode ^= SORT_REVERSE;
                } else {
                    newsortmode = SORT_OWNER | ( newsortmode & ~0xff );
                    newsortmode &= ~SORT_REVERSE;
                }
                break;
            case WorkerTypes::LISTCOL_MOD:
                if ( ( newsortmode & 0xff ) == SORT_MODTIME ) {
                    // toggle reverse
                    newsortmode ^= SORT_REVERSE;
                } else {
                    newsortmode = SORT_MODTIME | ( newsortmode & ~0xff );
                    newsortmode &= ~SORT_REVERSE;
                }
                break;
            case WorkerTypes::LISTCOL_ACC:
                if ( ( newsortmode & 0xff ) == SORT_ACCTIME ) {
                    // toggle reverse
                    newsortmode ^= SORT_REVERSE;
                } else {
                    newsortmode = SORT_ACCTIME | ( newsortmode & ~0xff );
                    newsortmode &= ~SORT_REVERSE;
                }
                break;
            case WorkerTypes::LISTCOL_CHANGE:
                if ( ( newsortmode & 0xff ) == SORT_CHGTIME ) {
                    // toggle reverse
                    newsortmode ^= SORT_REVERSE;
                } else {
                    newsortmode = SORT_CHGTIME | ( newsortmode & ~0xff );
                    newsortmode &= ~SORT_REVERSE;
                }
                break;
            case WorkerTypes::LISTCOL_INODE:
                if ( ( newsortmode & 0xff ) == SORT_INODE ) {
                    // toggle reverse
                    newsortmode ^= SORT_REVERSE;
                } else {
                    newsortmode = SORT_INODE | ( newsortmode & ~0xff );
                    newsortmode &= ~SORT_REVERSE;
                }
                break;
            case WorkerTypes::LISTCOL_NLINK:
                if ( ( newsortmode & 0xff ) == SORT_NLINK ) {
                    // toggle reverse
                    newsortmode ^= SORT_REVERSE;
                } else {
                    newsortmode = SORT_NLINK | ( newsortmode & ~0xff );
                    newsortmode &= ~SORT_REVERSE;
                }
                break;
            case WorkerTypes::LISTCOL_BLOCKS:
                //no sort available
                break;
            case WorkerTypes::LISTCOL_PERM:
                if ( ( newsortmode & 0xff ) == SORT_PERMISSION ) {
                    // toggle reverse
                    newsortmode ^= SORT_REVERSE;
                } else {
                    newsortmode = SORT_PERMISSION | ( newsortmode & ~0xff );
                    newsortmode &= ~SORT_REVERSE;
                }
                break;
            case WorkerTypes::LISTCOL_DEST:
                //no sort available
                break;
            case WorkerTypes::LISTCOL_EXTENSION:
                if ( ( newsortmode & 0xff ) == SORT_EXTENSION ) {
                    // toggle reverse
                    newsortmode ^= SORT_REVERSE;
                } else {
                    newsortmode = SORT_EXTENSION | ( newsortmode & ~0xff );
                    newsortmode &= ~SORT_REVERSE;
                }
                break;
            case WorkerTypes::LISTCOL_CUSTOM_ATTR:
                if ( ( newsortmode & 0xff ) == SORT_CUSTOM_ATTRIBUTE ) {
                    // toggle reverse
                    newsortmode ^= SORT_REVERSE;
                } else {
                    newsortmode = SORT_CUSTOM_ATTRIBUTE | ( newsortmode & ~0xff );
                    newsortmode &= ~SORT_REVERSE;
                }
                break;
            default:
                break;
        }
        if ( newsortmode != m_dir_sort_sets.getSortMode() ) {
            setSortmode( newsortmode );
        }
    }
}

void VirtualDirMode::searchentry( bool ignore_case, bool reverse_search, bool infix_search,
                                  const std::string &initial_search_value,
                                  bool apply_only )
{
    m_filtered_search_state.setCaseSensitive( ( ignore_case == true ) ? false : true );

    m_filtered_search_state.infix_search = infix_search;

    if ( m_filtered_search_state.searchmode_on == false ) {
        startsearchmode( false, initial_search_value );

        m_filtered_search_state.setInitialCall( true );
    } else {
        if ( m_filtered_search_state.getInitialCall() ) {
            // this is to detect that the user call the search entry command twice without entering
            // anything. So the last string should be used (which is different to the last_search
            // which is "" in this case)

            std::string new_str = m_filtered_search_state.getSearchFilterFromPreviousActivation();

            if ( shownextentry( new_str.c_str(), reverse_search ) ) {
                m_sg->setText( new_str.c_str() );
                m_sg->setCursor( new_str.length() );

                m_filtered_search_state.last_search = new_str;
            } else {
                shownextentry( NULL, reverse_search );
            }
        } else {
            shownextentry( NULL, reverse_search );
        }
    }

    if ( apply_only ) {
        finishsearchmode();
    }
}

void VirtualDirMode::startsearchmode( bool use_previous_search,
                                      const std::string &search_value )
{
    if ( ! ce.get() ) {
        return;
    }

    m_filtered_search_state.old_sg_content = m_sg->getText();

    m_sg->setForbidPosChange( true );
    m_sg->setStrongKeyCapture( false );
    m_sg->activate();
    m_filtered_search_state.searchmode_on = true;

    if ( ! use_previous_search ) {
        m_filtered_search_state.flexible_matching_active = false;

        bool reset = true;

        if ( ! search_value.empty() ) {
            if ( shownextentry( search_value.c_str(), false, true ) == true ) {
                m_filtered_search_state.last_search = search_value;
                m_sg->setText( search_value.c_str() );
                m_sg->setCursor( search_value.length() );

                reset = false;
            }
        }

        if ( reset ) {
            m_filtered_search_state.last_search = "";
            m_sg->setText( "" );
            m_sg->setCursor( 0 );

            setEntrySearchString( filter_search_string() );
        }
    } else {
        resetToLastSearchString();
    }

    parentlister->getWorker()->setIgnoreKey( XK_Tab, true );
}

void VirtualDirMode::finishsearchmode()
{
    if ( m_filtered_search_state.searchmode_on == false ) return;

    parentlister->getWorker()->removeStatebarText( m_expr_filter_help );

    if ( ! m_filtered_search_state.last_search.empty() ) {
        m_filtered_search_state.storeSearchFilterForNextActivation( m_filtered_search_state.last_search );
    }
    
    m_sg->setForbidPosChange( false );
    m_sg->setStrongKeyCapture( true );
    m_sg->setText( m_filtered_search_state.old_sg_content.c_str() );
    m_sg->deactivate();
    m_filtered_search_state.last_search = "";
    m_filtered_search_state.searchmode_on = false;

    if ( m_search_mode_hidden_enabled ) {
        bool keep = false;
        if ( ce.get() ) {
            auto es = ce->getEntry( ce->getActiveEntryPos() );
            if ( es && es->getNWCEntry()->isHiddenEntry() ) {
                keep = true;
                m_search_mode_restore_hidden = true;
            }
        }

        if ( ! keep ) {
            m_dir_filter_sets.setShowHidden( false );
            rebuildView();
            setName();
        }
    }
    m_search_mode_hidden_enabled = false;

    if ( m_apply_matcher ) {
        m_lv->clearHighlightSegments();
        m_lv->redraw();

        m_apply_matcher = false;
    }

    parentlister->getWorker()->setIgnoreKey( XK_Tab, false );
}

bool VirtualDirMode::shownextentry( const char *str, bool reverse_search, bool start_at_same )
{
    bool found;
    std::string old_string_filter, string_filter;
  
    found = false;

    if ( ! ce.get() ) {
        return found;
    }

    if ( ! m_filtered_search_state.searchmode_on ) {
        return found;
    }

    if ( str == NULL ) {
        str = m_filtered_search_state.last_search.c_str();
    }

    m_filtered_search_state.setInitialCall( false );

    if ( strcmp( str, "." ) == 0 &&
         m_dir_filter_sets.getShowHidden() == false ) {

        m_dir_filter_sets.setShowHidden( true );
        m_search_mode_hidden_enabled = true;

        rebuildView();

        setName();
    }

    if ( AGUIXUtils::starts_with( str, "(" ) ) {
        if ( getEntrySearchString().getFilterString() != str ) {
            setEntrySearchString( std::string( str ) );

            if ( ce->getActiveEntryPos() < 0 ||
                 ce->getUse( ce->getActiveEntryPos() ) == false ) {

                int row = ( m_show_dotdot ? 1 : 0 );

                ce->setActiveEntryPos( m_lv->getData( row, -1 ) );
                m_lv->setActiveRow( row );
            }

            m_lv->showActive();
            m_lv->redraw();
        } else {
            if ( reverse_search ) {
                int row = m_lv->getActiveRow();

                if ( row == 0 ) {
                    command_last();
                } else {
                    command_up();
                }
            } else {
                command_down( true, true );
            }
        }

        return true;
    }

    if ( getEntrySearchString().isExprFilter() ) {
        old_string_filter = "";
    } else {
        old_string_filter = getEntrySearchString().getFilterString();
    }

    if ( m_filtered_search_state.infix_search == true ) {
        string_filter = "*";
    } else {
        string_filter = "";
    }

    string_filter += str;
    string_filter += "*";

    if ( string_filter == "**" ) {
        string_filter = "*";
    }

    std::string new_flexible_string_filter = "*" + StringMatcherFNMatch::convertMatchStringToFlexibleMatch( string_filter ) + "*";

    if ( ( m_filtered_search_state.flexible_matching_active == true &&
           new_flexible_string_filter == old_string_filter ) ||
         ( m_filtered_search_state.flexible_matching_active == false &&
           string_filter == old_string_filter ) ) {

        if ( reverse_search ) {
            int row = m_lv->getActiveRow();

            if ( row == 0 ) {
                command_last();
            } else {
                command_up();
            }
        } else {
            command_down( true, true );
        }

        found = true;
    } else {

        std::string flexible_string_filter = "*" + StringMatcherFNMatch::convertMatchStringToFlexibleMatch( string_filter ) + "*";

        if ( string_filter.length() > old_string_filter.length() &&
             m_filtered_search_state.flexible_matching_active ) {
            // try flexible first
            setEntrySearchString( flexible_string_filter, false );

            m_filtered_search_state.flexible_matching_active = true;
            found = true;
            string_filter = flexible_string_filter;
        } else {
            setEntrySearchString( string_filter, false );

            // try non-flexible first
            if ( ce->getNrOfFiles() + ce->getNrOfDirs() < 1 ) {
                if ( m_filtered_search_state.flexible_matching_enabled ) {
                    setEntrySearchString( flexible_string_filter, false );

                    if ( ce->getNrOfFiles() + ce->getNrOfDirs() >= 1 ) {
                        m_filtered_search_state.flexible_matching_active = true;
                        string_filter = flexible_string_filter;
                        found = true;
                    }
                }
            } else {
                m_filtered_search_state.flexible_matching_active = false;
                found = true;
            }
        }

        if ( found == true ) {
            setEntrySearchString( string_filter );
            //TODO do rebuildView only if filtered search is enabled

            if ( ce->getActiveEntryPos() < 0 ||
                 ce->getUse( ce->getActiveEntryPos() ) == false ) {

                int row = ( m_show_dotdot ? 1 : 0 );

                ce->setActiveEntryPos( m_lv->getData( row, -1 ) );
                m_lv->setActiveRow( row );
            }

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

            if ( m_filtered_search_state.searchmode_on ) {
                showSearchModeCompletion( string_filter );
            }
        } else {
            setEntrySearchString( old_string_filter );
            m_lv->showActive();
            m_lv->redraw();
        }
    }
  
    return found;
}

void VirtualDirMode::setFlexibleMatchingMode( bool nv )
{
    m_filtered_search_state.flexible_matching_enabled = nv;
}

bool VirtualDirMode::getFlexibleMatchingMode() const
{
    return m_filtered_search_state.flexible_matching_enabled;
}

void VirtualDirMode::setEntrySearchString( const filter_search_string &filter, bool update_lv )
{
    m_filtered_search_state.setFilter( filter );

    updateDirFilter();

    if ( update_lv == true && m_lv != NULL ) {
        bool active_visible = m_lv->isRowVisible( m_lv->getActiveRow() );

        rebuildView();

        if ( m_lv->isRowVisible( m_lv->getActiveRow() ) == false &&
             active_visible == true ) {
            m_lv->centerActive();
        }
    }
}

VirtualDirMode::filter_search_string VirtualDirMode::getEntrySearchString()
{
    return m_filtered_search_state.getFilter();
}

void VirtualDirMode::updateDirFilter()
{
    m_filtered_search_state.setContainsTypeExpression( false );

    if ( m_filtered_search_state.getFilter().getFilterString().empty() == true ||
         m_filtered_search_state.getFilter().getFilterString() == "*" ||
         m_filtered_search_state.searchmode_on == false ) {
        m_dir_filter_sets.setStringFilter( std::unique_ptr<StringMatcher>() );
        m_dir_filter_sets.setGenericFilter( nullptr );
        m_filtered_search_state.non_empty_filter_active = false;
    } else {
        if ( m_filtered_search_state.getFilter().isExprFilter() ) {
            m_dir_filter_sets.setStringFilter( std::unique_ptr<StringMatcher>() );

            ExprFilterOptions options;
            options.content_check_size_limit = 10240;
            ExprFilter *ef = new ExprFilter( m_filtered_search_state.getFilter().getFilterString(), options );
            m_filtered_search_state.setLastValidExprFilter( ef->getValidReconstructedFilter() );
            m_expression_filter_interrupted = false;

            ef->setCheckInterrupt( [this]() {
                if ( m_expression_filter_interrupted ) return true;

                aguix->doXMsgs( NULL, false );

                if ( aguix->getLastKeyRelease() == XK_Escape ) {
                    m_expression_filter_interrupted = true;
                    return true;
                }

                return false;

            } );

            auto list_of_expected_strings = ef->getListOfExpectedStrings();
            m_filtered_search_state.setContainsTypeExpression( ef->containsTypeExpression() );
            
            m_dir_filter_sets.setGenericFilter( std::unique_ptr< GenericDirectoryFilter >( ef ) );

            updateExprFilterHelp( list_of_expected_strings );
        } else {
            std::unique_ptr<StringMatcherFNMatch> t( new StringMatcherFNMatch() );

            t->setMatchString( m_filtered_search_state.getFilter().getFilterString() );
            t->setMatchCaseSensitive( m_filtered_search_state.getCaseSensitive() );
            
            m_dir_filter_sets.setStringFilter( std::move( t ) );
            m_dir_filter_sets.setGenericFilter( nullptr );
        }
        m_filtered_search_state.non_empty_filter_active = true;
    }

    if ( ce.get() ) {
        ce->updateView( false );
    }
}

void VirtualDirMode::showSearchModeCompletion( const std::string &str )
{
    if ( ! ce.get() ) return;
    if ( m_lv == NULL ) return;

    if ( AGUIXUtils::starts_with( str, "(" ) ) return;

    int nr_of_entries = ce->getNrOfFiles( 0 ) + ce->getNrOfDirs( 0 ) + 1;

    if ( nr_of_entries > 1000 ) return;

    std::list< std::string > strings;
    std::string cwd = getCurrentDirectory();

    for ( int pos = 0;
          pos < (int)ce->getSize();
          pos++ ) {
        if ( ce->getUse( pos ) ) {
            auto es = ce->getEntry( pos );

            if ( ! es ) continue;

            auto fse = es->getNWCEntry();

            if ( ! fse ) continue;
            if ( fse->getBasename() != ".." ) {
                strings.push_back( NWC::Path::get_extended_basename( cwd,
                                                                     fse->getFullname() ) );
            }
        }
    }

    std::string string_filter;

    if ( m_filtered_search_state.infix_search == true ) {
        string_filter = "*";
    } else {
        string_filter = "";
    }

    string_filter += str;

    RefCount< StringMatcherFNMatch > matcher( new StringMatcherFNMatch() );

    matcher->setMatchString( m_filtered_search_state.getFilter().getFilterString() );
    matcher->setMatchCaseSensitive( m_filtered_search_state.getCaseSensitive() );

    StringCompletion sc( matcher );
    
    std::string completion = sc.findCompletion( string_filter, strings );
    
    std::string newtext = getFilterStringFromSG();

    if ( ! completion.empty() ) {
        newtext += " [";
        newtext += completion;
        newtext += "]";
    }

    m_sg->setText( newtext.c_str() );

    m_filtered_search_state.last_completion = completion;
}

std::string VirtualDirMode::getFilterStringFromSG()
{
    if ( m_filtered_search_state.searchmode_on ) {
        std::string newtext = m_sg->getText();
        newtext.resize( m_sg->getCursor() );
        return newtext;
    } else {
        return "";
    }
}

void VirtualDirMode::resetToLastSearchString()
{
    if ( m_filtered_search_state.searchmode_on ) {
        std::string newtext = m_filtered_search_state.last_search;

        if ( ! AGUIXUtils::starts_with( newtext, "(" ) &&
             ! m_filtered_search_state.last_completion.empty() ) {
            newtext += " [";
            newtext += m_filtered_search_state.last_completion;
            newtext += "]";
        }

        m_sg->setText( newtext.c_str() );
        m_sg->setCursor( m_filtered_search_state.last_search.length() );
    }
}

bool VirtualDirMode::filteredSearchActive() const
{
    return m_filtered_search_state.non_empty_filter_active;
}

void VirtualDirMode::deactivateFilteredSearch()
{
    finishsearchmode();

    if ( filteredSearchActive() == true ) {
        setEntrySearchString( filter_search_string() );
    }
}

void VirtualDirMode::makeRowActive( int row )
{
    if ( m_lv->isValidRow( row ) ) {
        ce->setActiveEntryPos( m_lv->getData( row, -1 ) );

        makeListViewRowActive( row );

        m_lv->showActive();
    }
}

void VirtualDirMode::changeCacheEntry( int direction )
{
    finishsearchmode();

    if ( m_cache.size() <= 1 ) return;

    if ( direction > 0 ) {
        auto it = m_cache.begin();

        it++;

        if ( it != m_cache.end() ) {
            RefCount< DMCacheEntryNWC > tce = it->second;

            tce->reload();

            setCurrentCE( tce );
        }
    } else {
        auto it = m_cache.end();

        if ( it != m_cache.begin() ) {
            it--;

            RefCount< DMCacheEntryNWC > tce = it->second;

            tce->reload();

            setCurrentCE( tce );
        }
    }
}

int VirtualDirMode::getStoredFieldWidth( const std::string &str1 )
{
    int tw = -2;
    if ( m_field_name_to_width.count( str1 ) > 0 ) {
        tw = m_field_name_to_width[str1];
        m_field_name_to_width.erase( str1 );
    }
    return tw;
}

int VirtualDirMode::storeFieldWidths()
{
    if ( m_lv != NULL ) {
        for ( auto &p : m_field_name_to_column ) {
            int tw = m_lv->getFieldWidth( p.second );
            m_field_name_to_width[ p.first ] = tw;
        }
    }
    return 0;
}

std::list< std::string > VirtualDirMode::getNameOfCacheEntries()
{
    std::list< std::string > res;

    for ( auto &tce : m_cache ) {
        res.push_front( tce.second->getVisibleName() );
    }

    return res;
}

void VirtualDirMode::show_cache_entry( const std::list< RefCount< ArgClass > > &args )
{
    if ( ! args.empty() ) {
        RefCount< ArgClass > a1 = args.front();
        if ( a1.get() != NULL ) {
            StringArg *a2 = dynamic_cast< StringArg *>( a1.get() );
            if ( a2 != NULL ) {
                std::string entry = a2->getValue();

                for ( auto &tce : m_cache ) {
                    if ( tce.second->getVisibleName() == entry ) {
                        // hit
                        setCurrentCE( tce.second );
                        break;
                    }
                }
            }
        }
    }
}

void VirtualDirMode::lvbDoubleClicked()
{
    ActionMessage amsg( parentlister->getWorker() );

    command_list_t action_list = {};
    auto cop = std::make_shared< ShowDirCacheOp >();
    action_list.push_back( cop );

    parentlister->getWorker()->interpret( action_list, &amsg );
}

bool VirtualDirMode::pathsChanged( const std::set< std::string > changed_paths )
{
    bool found = false;

    if ( m_dirwatcher.watch_mode == true && ce.get() ) {
        for ( std::set< std::string >::const_iterator it1 = changed_paths.begin();
              it1 != changed_paths.end();
              ++it1 ) {
            if ( *it1 == ce->getNameOfDir() ) {

                found = true;

                if ( ! m_dirwatcher.timer_enabled ) {
                    parentlister->getWorker()->registerTimeout( DIRWATCHER_TIMEOUT );

                    m_dirwatcher.timer_enabled = true;

                }
                // always call disable since the watch could have been reenabled even if the timeout
                // is already set
                disableDirWatch();
            }
        }
    }

    return found;
}

void VirtualDirMode::enableDirWatch()
{
    enableDirWatch( "" );
}

void VirtualDirMode::enableDirWatch( const std::string &path )
{
    if ( path.empty() ) {
        // re-enable
        if ( ! m_dirwatcher.path_watch_enabled && ! m_dirwatcher.watched_path.empty() ) {
            parentlister->getWorker()->getPathnameWatcher()->watchPath( m_dirwatcher.watched_path );

            m_dirwatcher.path_watch_enabled = true;
        }
    } else {
        disableDirWatch();

        parentlister->getWorker()->getPathnameWatcher()->watchPath( path );

        m_dirwatcher.watched_path = path;

        m_dirwatcher.path_watch_enabled = true;
    }
}

void VirtualDirMode::disableDirWatch( bool clear_path )
{
    if ( m_dirwatcher.path_watch_enabled ) {
        parentlister->getWorker()->getPathnameWatcher()->unwatchPath( m_dirwatcher.watched_path );
        m_dirwatcher.path_watch_enabled = false;

        if ( clear_path ) {
            m_dirwatcher.watched_path.clear();
        }
    }
}

void VirtualDirMode::setWatchMode( bool nv )
{
    m_dirwatcher.watch_mode = nv;
}

void VirtualDirMode::setActivateSearchModeOnKeyPress( bool nv )
{
    m_activate_search_on_keypress = nv;
}

void VirtualDirMode::update( bool reset_dirsizes,
                             bool keep_filetypes )
{
    if ( ce.get() == NULL ) {
        return;
    }

    m_expression_filter_interrupted = false;

    if( ce->isRealDir() ) {
        std::string dir;
  
        dir = ce->getNameOfDir();
        for(;;) {
            int e = showDir( dir );
            if ( e == 0 ) break;  // read ok, so finish
            if ( dir == "/" ) break;  // there is no dir we can display anymore, so finish
            char *tstr = NWC::Path::parentDir( dir.c_str(), NULL );
            dir = tstr;
        }
    } else {
        updateCE();

        setCurrentCE( ce );
    }

    if ( ce.get() ) {
        if ( keep_filetypes == true ) {
            ce->resetFiletypeCheckState();
        } else {
            ce->resetFiletypesAndColors();
        }
        if ( reset_dirsizes == true ) {
            ce->resetDirSizes();
        }

        if ( ! keep_filetypes || reset_dirsizes ) {
            clear_custom_attributes();
        }
    }
    
    rebuildView();
}

void VirtualDirMode::showFreeSpace( bool force )
{
    int erg;

    if ( m_show_free_space == true ) {
        updateSpaceTimeout( m_update_time * 1000 );
    } else {
        updateSpaceTimeout( 0 );
    }

    if ( force == true ) erg = updateFreeSpaceStr( 0 );
    else erg = updateFreeSpaceStr( m_update_time );
    if ( erg != 0 ) updateName();
}

/*
 * method updates freespacestr
 * returns 0 for no change, 1 for change
 */
int VirtualDirMode::updateFreeSpaceStr( int ti )
{
    time_t now;
    int erg, rv = 0;

    if ( m_show_free_space == true ) {
        now = time(NULL);
        if ( ( now - m_last_fs_update >= ti ) || ( m_last_eagain == true ) ) {
            if ( ! getCurrentDirectory().empty() ) {
                erg = parentlister->getWorker()->PS_readSpace( getCurrentDirectory().c_str() );
                if ( erg == 0 ) {
                    std::string spaceh, freeh;

                    spaceh = parentlister->getWorker()->PS_getSpaceH();
                    freeh = parentlister->getWorker()->PS_getFreeSpaceH();
            
                    m_free_space_str = AGUIXUtils::formatStringToString( catalog.getLocale( 1383 ),
                                                                         freeh.c_str(), spaceh.c_str() );
                    m_last_eagain = false;
                    rv = 1;
                } else if ( erg == EAGAIN ) {
                    // no valid value so lets try again next call
                    m_last_eagain = true;
                } else {
                    // clear string since data is not available
                    m_free_space_str = "";
                    rv = 1;
                    m_last_eagain = false;
                }
                m_last_fs_update = now;
            }
        }
    } else {
        if ( ! m_free_space_str.empty() ) {
            m_free_space_str = "";
            rv = 1;
        }
    }
    return rv;
}

void VirtualDirMode::setShowFreeSpace( bool v )
{
    m_show_free_space = v;
    setName();
    showFreeSpace( true );
}

void VirtualDirMode::setUpdatetime( int nv )
{
    m_update_time = nv;
    if ( m_update_time < 1) m_update_time =1;
    parentlister->getWorker()->PS_setLifetime( (double)m_update_time );
}

void VirtualDirMode::updateSpaceTimeout( loff_t time_ms )
{
    if ( m_current_space_update_ms == time_ms ) return;

    if ( m_current_space_update_ms != 0 ) {
        parentlister->getWorker()->unregisterTimeout( m_current_space_update_ms );
        m_current_space_update_ms = 0;
    }
    
    if ( time_ms > 0 ) {
        parentlister->getWorker()->registerTimeout( time_ms );
        m_current_space_update_ms = time_ms;
    }
}

std::list<PopUpMenu::PopUpEntry> VirtualDirMode::buildTabPopUpData( int tab )
{
    int descr_id = 0;
    std::list<PopUpMenu::PopUpEntry> m1;

    if ( tab < 0 || tab >= (int)m_tab_store.tab_entries.size() ) return m1;

    PopUpMenu::PopUpEntry e1;
    // e1.name = "Tab menu";
    // e1.type = PopUpMenu::HEADER;
    // e1.id = descr_id++;
    // m1.push_back( e1 );

    // e1.type = PopUpMenu::HLINE;
    // m1.push_back( e1 );
    
    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 852 );
    e1.id = descr_id++;
    
    m_current_popup_settings.tab_id_to_action[e1.id] = tab_popup_table_t( tab_popup_table_t::NEW_TAB );
    
    m1.push_back( e1 );

    if ( m_tab_store.tab_entries.size() < 2 ) {
        e1.type = PopUpMenu::GREYED_OUT;
    } else {
        e1.type = PopUpMenu::NORMAL;
    }
    e1.name = catalog.getLocale( 952 );
    e1.id = descr_id++;
    
    m_current_popup_settings.tab_id_to_action[e1.id] = tab_popup_table_t( tab_popup_table_t::DEL_TAB );
    
    m1.push_back( e1 );

    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 953 );
    e1.id = descr_id++;
    
    m_current_popup_settings.tab_id_to_action[e1.id] = tab_popup_table_t( tab_popup_table_t::TAB_TO_OTHER_SIDE );
    
    m1.push_back( e1 );

    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 954 );
    e1.id = descr_id++;
    
    m_current_popup_settings.tab_id_to_action[e1.id] = tab_popup_table_t( tab_popup_table_t::TAB_TO_OTHER_SIDE_AS_NEW );
    
    m1.push_back( e1 );

    e1.type = PopUpMenu::HLINE;
    m1.push_back( e1 );

    e1.type = PopUpMenu::NORMAL;
    e1.id = descr_id++;

    if ( ! m_tab_store.tab_entries[tab].locked ) {
        e1.name = catalog.getLocale( 1213 );
    
        m_current_popup_settings.tab_id_to_action[e1.id] = tab_popup_table_t( tab_popup_table_t::LOCK_TAB );
    } else {
        e1.name = catalog.getLocale( 1214 );

        m_current_popup_settings.tab_id_to_action[e1.id] = tab_popup_table_t( tab_popup_table_t::UNLOCK_TAB );
    }
    
    m1.push_back( e1 );

    e1.type = PopUpMenu::HLINE;
    m1.push_back( e1 );

    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 1215 );
    e1.id = descr_id++;
    
    m_current_popup_settings.tab_id_to_action[e1.id] = tab_popup_table_t( tab_popup_table_t::MOVE_TAB_LEFT );
    
    m1.push_back( e1 );

    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 1216 );
    e1.id = descr_id++;
    
    m_current_popup_settings.tab_id_to_action[e1.id] = tab_popup_table_t( tab_popup_table_t::MOVE_TAB_RIGHT );
    
    m1.push_back( e1 );

    return m1;
}

void VirtualDirMode::openTabPopUp( int tab )
{
    freePopUpMenus();

    m_current_popup_settings.reset();

    std::list<PopUpMenu::PopUpEntry> tabmenu = buildTabPopUpData( tab );

    m_current_popup_settings.tab_popup_menu = std::unique_ptr<PopUpMenu>( new PopUpMenu( aguix, tabmenu ) );
    m_current_popup_settings.tab_popup_menu->create();
    m_current_popup_settings.tab_popup_menu->show();
    m_current_popup_settings.m_tab = tab;
}

void VirtualDirMode::freePopUpMenus()
{
    m_current_popup_settings.reset();
}

void VirtualDirMode::open_current_tab_menu()
{
    openTabPopUp( m_tab_store.active_tab );
}

VirtualDirMode::PopUpSettings::PopUpSettings() : label_menu_id( -1 ),
                                                 lv_popup_menu( NULL ),
                                                 m_tab( -1 ),
                                                 label_entry_clicked( false )
{}

void VirtualDirMode::PopUpSettings::reset()
{
    label_menu_id = -1;
    label_id_to_action.clear();
    label_popup_menu.reset();
    tab_popup_menu.reset();
    popup_descr.clear();
    entry_for_popup.clear();
    if ( lv_popup_menu != NULL ) {
        delete lv_popup_menu;
        lv_popup_menu = NULL;
    }
    m_tab = -1;
    label_entry_clicked = false;

    label_menu_id = -1;
    filter_menu_id = -1;
    presets_menu_id = -1;
    filetype_entry_id = -1;
    copy_entry_id = -1;
    copy_selected_id = -1;
    cut_entry_id = -1;
    cut_selected_id = -1;
    paste_id = -1;
}

void VirtualDirMode::handleTabPopUp( AGMessage *msg )
{
    if ( msg == NULL || m_lv == NULL ) return;

    if ( ! ( msg->type == AG_POPUPMENU_CLICKED ||
             msg->type == AG_POPUPMENU_ENTRYEDITED ) ) return;

    if ( msg->popupmenu.menu != m_current_popup_settings.tab_popup_menu.get() ) return;
    
    std::list<int> entry_ids = *msg->popupmenu.recursive_ids;

    if ( entry_ids.empty() == true ) return;

    int id = entry_ids.back();
    
    std::map<int, tab_popup_table_t>::const_iterator it1 = m_current_popup_settings.tab_id_to_action.find( id );

    if ( it1 != m_current_popup_settings.tab_id_to_action.end() ) {
        // found entry

        if ( it1->second.type == tab_popup_table_t::NEW_TAB ) {
            newTab();
        } else if ( it1->second.type == tab_popup_table_t::DEL_TAB ) {
            closeTab( m_current_popup_settings.m_tab );
        } else if ( it1->second.type == tab_popup_table_t::TAB_TO_OTHER_SIDE ||
                    it1->second.type == tab_popup_table_t::TAB_TO_OTHER_SIDE_AS_NEW ) {
            if ( m_current_popup_settings.m_tab >= 0 &&
                 m_current_popup_settings.m_tab < (int)m_tab_store.tab_entries.size() ) {
                auto tce = m_tab_store.tab_entries[m_current_popup_settings.m_tab];

                if ( tce.ce.get() ) {
                    if ( tce.ce->isRealDir() ) {
                        std::string dir = tce.ce->getNameOfDir();

                        if ( ! dir.empty() ) {
                            Lister *l2;
                            ListerMode *lm1;
                            l2 = parentlister->getWorker()->getOtherLister( parentlister );
                            lm1 = l2->getActiveMode();
                            if ( lm1 != NULL ) {

                                if ( it1->second.type == tab_popup_table_t::TAB_TO_OTHER_SIDE_AS_NEW ) {
                                    lm1->runCommand( "new_tab" );
                                }

                                std::list< RefCount< ArgClass > > args;

                                args.push_back( RefCount< ArgClass >( new StringArg( dir ) ) );
                                lm1->runCommand( "enter_dir", args );
                            }
                        }
                    } else {
                        // virtual dir, only for VDM
                        Lister *l2;
                        ListerMode *lm1;
                        l2 = parentlister->getWorker()->getOtherLister( parentlister );
                        lm1 = l2->getActiveMode();

                        auto vdm = dynamic_cast< VirtualDirMode *>( lm1 );

                        if ( vdm ) {
                            if ( it1->second.type == tab_popup_table_t::TAB_TO_OTHER_SIDE_AS_NEW ) {
                                lm1->runCommand( "new_tab" );
                            }

                            auto d = tce.ce->getNWCDir();

                            if ( d ) {
                                vdm->showDir( *d );
                            }
                        }
                    }
                }
            }
        } else if ( it1->second.type == tab_popup_table_t::LOCK_TAB ) {
            lockTab( m_current_popup_settings.m_tab );
        } else if ( it1->second.type == tab_popup_table_t::UNLOCK_TAB ) {
            unlockTab( m_current_popup_settings.m_tab );
        } else if ( it1->second.type == tab_popup_table_t::MOVE_TAB_LEFT ) {
            moveTabDirection( -1 );
        } else if ( it1->second.type == tab_popup_table_t::MOVE_TAB_RIGHT ) {
            moveTabDirection( 1 );
        }
    }
}

void VirtualDirMode::buildContextPopUpMenuFilterAndPreset( std::list<PopUpMenu::PopUpEntry> &menulist,
                                                           int &descr_id,
                                                           const NWCEntrySelectionState *es )
{
    PopUpMenu::PopUpEntry e1;

    e1.type = PopUpMenu::SUBMENU;
    e1.name = catalog.getLocale( 1171 );
    e1.submenu = buildFilterPopUpData( es );
    e1.id = descr_id++;

    m_current_popup_settings.filter_menu_id = e1.id;

    menulist.push_back( e1 );

    e1.type = PopUpMenu::HLINE;
    menulist.push_back( e1 );

    std::list<PopUpMenu::PopUpEntry> presets_menu;
    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 1333 );
    e1.id = 0;
    presets_menu.push_back( e1 );

    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 1334 );
    e1.id = 1;
    presets_menu.push_back( e1 );

    e1.type = PopUpMenu::SUBMENU;
    e1.name = catalog.getLocale( 1332 );
    e1.submenu = presets_menu;
    e1.id = descr_id++;

    m_current_popup_settings.presets_menu_id = e1.id;

    menulist.push_back( e1 );
}

void VirtualDirMode::buildContextPopUpMenu( const NWCEntrySelectionState *es )
{
    freePopUpMenus();

    const NWC::FSEntry *fse = es ? es->getNWCEntry() : NULL;

    int descr_id = 0;

    if ( ! es ) {
        std::list<PopUpMenu::PopUpEntry> m1;
        buildContextPopUpMenuFilterAndPreset( m1, descr_id, NULL );

        PopUpMenu::PopUpEntry e1;
        e1.type = PopUpMenu::HLINE;
        m1.push_back( e1 );
        e1.type = PopUpMenu::NORMAL;
        e1.name = catalog.getLocale( 1468 );
        e1.id = descr_id++;
        m_current_popup_settings.paste_id = e1.id;
        m1.push_back( e1 );

        m_current_popup_settings.lv_popup_menu = new PopUpMenu( aguix, m1 );
        m_current_popup_settings.lv_popup_menu->setHelpCB( [this]( const std::string &str ) {
            updatePopUpHelp( str );
        } );

        m_current_popup_settings.lv_popup_menu->create();
        return;
    }

    if ( ! fse ) return;

    if ( fse->isBrokenLink() == true ) return;
    if ( fse->getBasename() == ".." ) return;

    WCFiletype *ft = es ? es->getFiletype() : NULL;

    std::list<PopUpMenu::PopUpEntry> m1;
    PopUpMenu::PopUpEntry e1;

    m_current_popup_settings.reset();

    e1.type = PopUpMenu::SUBMENU;
    e1.name = catalog.getLocale( 833 );
    e1.submenu = buildLabelPopUpData( *es );
    e1.id = descr_id++;

    m_current_popup_settings.label_menu_id = e1.id;

    m1.push_back( e1 );

    buildContextPopUpMenuFilterAndPreset( m1, descr_id, es );

    e1.type = PopUpMenu::NORMAL;
    if ( ft != NULL && ft->getinternID() == UNKNOWNTYPE ) {
        e1.name = catalog.getLocale( 1343 );
    } else {
        e1.name = catalog.getLocale( 1344 );
    }
    e1.id = descr_id++;
    m_current_popup_settings.filetype_entry_id = e1.id;

    m1.push_back( e1 );
    e1.type = PopUpMenu::HLINE;
    m1.push_back( e1 );

    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 1467 );
    e1.id = descr_id++;
    m_current_popup_settings.cut_entry_id = e1.id;
    m1.push_back( e1 );

    const bool selected = ( ce->getNrOfFiles( true ) + ce->getNrOfDirs( true ) ) > 0;

    e1.type = PopUpMenu::GREYED_OUT;
    e1.name = catalog.getLocale( 1470 );
    e1.id = descr_id++;
    if ( selected ) {
        e1.type = PopUpMenu::NORMAL;
        m_current_popup_settings.cut_selected_id = e1.id;
    }
    m1.push_back( e1 );

    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 1466 );
    e1.id = descr_id++;
    m_current_popup_settings.copy_entry_id = e1.id;
    m1.push_back( e1 );

    e1.type = PopUpMenu::GREYED_OUT;
    e1.name = catalog.getLocale( 1469 );
    e1.id = descr_id++;
    if ( selected ) {
        e1.type = PopUpMenu::NORMAL;
        m_current_popup_settings.copy_selected_id = e1.id;
    }
    m1.push_back( e1 );

    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 1468 );
    e1.id = descr_id++;
    m_current_popup_settings.paste_id = e1.id;
    m1.push_back( e1 );

    e1.type = PopUpMenu::HLINE;
    m1.push_back( e1 );

    e1.name = catalog.getLocale( 768 );
    e1.type = PopUpMenu::HEADER;
    m1.push_back( e1 );
    e1.type = PopUpMenu::HLINE;
    m1.push_back( e1 );
    
    if ( ft == NULL ) {
        if ( fse->isDir() == true ) {
            ft = wconfig->getdirtype();
        } else {
            ft = wconfig->getnotyettype();
        }
    }

    m_current_popup_settings.popup_descr[descr_id] = RefCount<ActionDescr>( new DoubleClickAction::DoubleClickActionDescr() );

    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 252 );
    auto dc_l = ft->getActionList( true, DoubleClickAction::DoubleClickActionDescr() );
    e1.help = getStringReprForList( dc_l );
    e1.id = descr_id++;
    m1.push_back( e1 );
    
    auto s_l = ft->getActionList( true, ShowAction::ShowActionDescr() );
    if ( ! s_l.empty() ) {
        m_current_popup_settings.popup_descr[descr_id] = RefCount<ActionDescr>( new ShowAction::ShowActionDescr() );

        e1.type = PopUpMenu::NORMAL;
        e1.name = catalog.getLocale( 253 );
        e1.help = getStringReprForList( s_l );
        e1.id = descr_id++;
        m1.push_back( e1 );
    }

    auto rs_l = ft->getActionList( true, RawShowAction::RawShowActionDescr() );
    if ( ! rs_l.empty() ) {
        m_current_popup_settings.popup_descr[descr_id] = RefCount<ActionDescr>( new RawShowAction::RawShowActionDescr() );

        e1.type = PopUpMenu::NORMAL;
        e1.name = catalog.getLocale( 254 );
        e1.help = getStringReprForList( rs_l );
        e1.id = descr_id++;
        m1.push_back( e1 );
    }

    bool highlight_user_action = m_context_menu_sets.getHighlightUserAction();

    for ( int i = 0; i < 10; i++ ) {
        auto u_l = ft->getActionList( true, UserAction::UserActionDescr( i ) );
        if ( ! u_l.empty() ) {
            m_current_popup_settings.popup_descr[descr_id] = RefCount<ActionDescr>( new UserAction::UserActionDescr( i ) );

            e1.type = PopUpMenu::NORMAL;
            //TODO not nice using index for locale but works
            e1.name = catalog.getLocale( 255 + i );
            e1.help = getStringReprForList( u_l );
            e1.id = descr_id++;

            if ( highlight_user_action ) {
                e1.highlight_on_show = true;
                highlight_user_action = false;
            }
            
            m1.push_back( e1 );

            e1.highlight_on_show = false;
        }
    }

    std::map< std::string, std::string > action_name_to_help_string;

    std::list<std::string> action_names_of_current_entry_list;
    std::set< std::string > action_names_of_current_entry,
        common_action_names_of_all_entries,
        all_action_names_of_all_entries;

    ft->fillNamesOfCustomActions( true, action_names_of_current_entry_list );
    for ( std::list<std::string>::const_iterator it1 = action_names_of_current_entry_list.begin();
          it1 != action_names_of_current_entry_list.end();
          it1++ ) {
        action_names_of_current_entry.insert( *it1 );
        common_action_names_of_all_entries.insert( *it1 );
        all_action_names_of_all_entries.insert( *it1 );

        RunCustomAction::RunCustomActionDescr ad( *it1 );
        auto action_list = ft->getActionList( true, ad );

        action_name_to_help_string[*it1] = getStringReprForList( action_list );
    }

    //TODO build submenu for all selected entries

    for ( int pos = 0;
          pos < (int)ce->getSize();
          pos++ ) {
        if ( ce->getUse( pos ) ) {
            auto tes = ce->getEntry( pos );

            if ( ! tes ) continue;

            auto tfse = tes->getNWCEntry();

            if ( ! tfse ) continue;

            if ( ! tes->getSelected() ) continue;

            WCFiletype *tft = tes->getFiletype();

            if ( tft == NULL ) {
                if ( tfse->isDir() == true ) {
                    tft = wconfig->getdirtype();
                } else {
                    tft = wconfig->getnotyettype();
                }
            }

            if ( tft != NULL ) {
                std::list<std::string> custom_actions;
                std::set< std::string > temp_names, new_names;

                tft->fillNamesOfCustomActions( true, custom_actions );
                for ( std::list<std::string>::const_iterator it1 = custom_actions.begin();
                      it1 != custom_actions.end();
                      it1++ ) {
                    temp_names.insert( *it1 );

                    all_action_names_of_all_entries.insert( *it1 );

                    RunCustomAction::RunCustomActionDescr ad( *it1 );
                    auto action_list = tft->getActionList( true, ad );

                    if ( action_name_to_help_string.count( *it1 ) < 1 ) {
                        action_name_to_help_string[*it1] = getStringReprForList( action_list );
                    }
                }

                std::set_intersection( common_action_names_of_all_entries.begin(),
                                       common_action_names_of_all_entries.end(),
                                       temp_names.begin(),
                                       temp_names.end(),
                                       std::inserter( new_names, new_names.begin() ) );

                common_action_names_of_all_entries = new_names;
            }
        }
    }

    // first all common actions
    for ( std::set< std::string >::const_iterator it1 = common_action_names_of_all_entries.begin();
          it1 != common_action_names_of_all_entries.end();
          it1++ ) {
        m_current_popup_settings.popup_descr[descr_id] = RefCount<ActionDescr>( new RunCustomAction::RunCustomActionDescr( *it1 ) );

        e1.type = PopUpMenu::NORMAL;
        //e1.name = AGUIXUtils::formatStringToString( catalog.getLocale( 779 ), it1->c_str() );
        e1.name = it1->c_str();
        e1.id = descr_id++;
        e1.help = action_name_to_help_string[*it1];

        if ( highlight_user_action ) {
            e1.highlight_on_show = true;
            highlight_user_action = false;
        }
            
        m1.push_back( e1 );

        e1.highlight_on_show = false;
    }

    // now all of current entry
    if ( action_names_of_current_entry.size() > common_action_names_of_all_entries.size() ) {
        std::list<PopUpMenu::PopUpEntry> current_entry_menu;

        e1.type = PopUpMenu::SUBMENU;
        e1.name = catalog.getLocale( 970 );
        e1.id = descr_id++;
        m_current_popup_settings.entry_is_action_submenu[e1.id] = true;

        for ( std::set< std::string >::const_iterator it1 = action_names_of_current_entry.begin();
              it1 != action_names_of_current_entry.end();
              it1++ ) {

            if ( common_action_names_of_all_entries.count( *it1 ) < 1 ) {
                m_current_popup_settings.popup_descr[descr_id] = RefCount<ActionDescr>( new RunCustomAction::RunCustomActionDescr( *it1 ) );

                PopUpMenu::PopUpEntry e2;

                e2.type = PopUpMenu::NORMAL;
                e2.name = it1->c_str();
                e2.help = action_name_to_help_string[*it1];
                e2.id = descr_id++;
                current_entry_menu.push_back( e2 );
            }
        }

        e1.submenu = current_entry_menu;
        m1.push_back( e1 );
    }

    m_current_popup_settings.lv_popup_menu = new PopUpMenu( aguix, m1 );
    m_current_popup_settings.lv_popup_menu->setHelpCB( [this]( const std::string &str ) {
            updatePopUpHelp( str );
        } );
    m_current_popup_settings.lv_popup_menu->create();
}

void VirtualDirMode::lv_pressed( int row, bool open_under_mouse )
{
    if ( ce.get() == NULL || m_lv == NULL ) return;
    
    freePopUpMenus();

    if ( m_lv->isValidRow( row ) ) {
        makeRowActive( row );

        int ce_id = m_lv->getData( row, -1 );

        if ( ce->getUse( ce_id ) ) {
            const NWCEntrySelectionState *es = ce->getEntry( ce_id );

            if ( ! es ) return;

            const NWC::FSEntry *fse = es->getNWCEntry();

            if ( ! fse ) return;

            if ( ! es->getFiletype() ) {

                if ( fse->isDir( true ) == false ) {
                    addActiveRowForFTCheck();

                    m_delayed_filetype_action.cb = [this, row, open_under_mouse]() { if ( is_active_entry( m_delayed_filetype_action.path ) ) { lv_pressed( row, open_under_mouse ); } };

                    setDelayedFTAction( fse->getFullname() );

                    return;
                }
            }

            buildContextPopUpMenu( es );

            m_current_popup_settings.entry_for_popup = fse->getFullname();
        }
    }

    if ( m_current_popup_settings.lv_popup_menu == NULL ) {
        buildContextPopUpMenu( NULL );
        m_current_popup_settings.entry_for_popup = "";
    }

    if ( m_current_popup_settings.lv_popup_menu != NULL ) {
        if ( open_under_mouse == true ) {
            m_current_popup_settings.lv_popup_menu->show();
        } else {
            int tx, ty;
                
            aguix->getWidgetRootPosition( m_lv, &tx, &ty );
            tx += m_lv->getWidth() / 10;

            int row_y = m_lv->getRowYPosition( row );
            ty += row_y;
                
            m_current_popup_settings.lv_popup_menu->show( tx, ty, PopUpMenu::POPUP_IN_POSITION );
        }
    }
}

void VirtualDirMode::startLVPopUpAction( AGMessage *msg )
{
    if ( msg == NULL || ce.get() == NULL || m_lv == NULL ) return;

    int row = m_lv->getActiveRow();

    if ( ! m_lv->isValidRow( row ) ) {
        // no file select, check for basic non-file related entries
        if ( msg->popupmenu.recursive_ids != NULL &&
             msg->popupmenu.recursive_ids->empty() == false ) {
            if ( msg->popupmenu.recursive_ids->back() == m_current_popup_settings.filter_menu_id ) {
                std::list<int> entry_ids = *msg->popupmenu.recursive_ids;
                entry_ids.pop_back();
                handleFilterPopUp( entry_ids, msg );
            } else if ( msg->popupmenu.recursive_ids->back() == m_current_popup_settings.presets_menu_id ) {
                std::list<int> entry_ids = *msg->popupmenu.recursive_ids;
                entry_ids.pop_back();
                handleDirectoryPresetsPopUp( entry_ids, msg );
            } else if ( msg->popupmenu.recursive_ids->back() == m_current_popup_settings.paste_id ) {
                paste_files_from_clipboard();
            }
        }
        return;
    }

    int ce_id = m_lv->getData( row, -1 );

    if ( ! ce->getUse( ce_id ) ) return;

    const NWCEntrySelectionState *es = ce->getEntry( ce_id );

    if ( ! es ) return;

    const NWC::FSEntry *fse = es->getNWCEntry();

    if ( ! fse ) return;

    if ( m_current_popup_settings.entry_for_popup.empty() ) return;
    if ( m_current_popup_settings.entry_for_popup != fse->getFullname() ) return;
    // ok, still the same entry
    
    int id = msg->popupmenu.clicked_entry_id;

    if ( m_current_popup_settings.entry_is_action_submenu[ id ] == true) {
        if ( msg->popupmenu.recursive_ids != NULL &&
             ! msg->popupmenu.recursive_ids->empty() ) {
            id = msg->popupmenu.recursive_ids->front();
        }
    }
    
    if ( m_current_popup_settings.popup_descr.count( id ) > 0 ) {
        RefCount<ActionDescr> action_descr = m_current_popup_settings.popup_descr[id];

        if ( action_descr.get() != NULL ) {
            if ( dynamic_cast<DoubleClickAction::DoubleClickActionDescr*>( action_descr.get() ) != NULL ) {
                startAction( *es );
            } else {

                ActionMessage amsg( parentlister->getWorker() );

                prepareContext( amsg );

                WCFiletype *ft;
                ft = es->getFiletype();
                if ( ft == NULL ) {
                    if ( fse->isDir() == true ) {
                        ft = wconfig->getdirtype();
                    } else {
                        ft = wconfig->getnotyettype();
                    }
                }

                amsg.filetype = ft;

                amsg.m_action_descr = action_descr;
                auto action_list = ft->getActionList( true, *( action_descr ) );

                parentlister->getWorker()->interpret( action_list, &amsg );
            }
        }
    } else if ( id == m_current_popup_settings.filetype_entry_id ) {
        WCFiletype *ft;
        ft = es->getFiletype();
        if ( ft != NULL ) {
            if ( ft->getinternID() == UNKNOWNTYPE ) {
                WConfigInitialCommand cmd( WConfigInitialCommand::OPEN_NEW_FILE_TYPE );
                cmd.setUserData( es->getMimeType() );
                parentlister->getWorker()->issueConfigOpen( catalog.getLocale( 6 ),
                                                            cmd );
            } else {
                WConfigInitialCommand cmd( WConfigInitialCommand::SELECT_FILE_TYPE );
                cmd.setUserData( ft->getName() );
                parentlister->getWorker()->issueConfigOpen( catalog.getLocale( 6 ),
                                                            cmd );
            }
        }
    } else if ( id == m_current_popup_settings.cut_entry_id ||
                id == m_current_popup_settings.copy_entry_id ) {
        std::vector< std::string > l = { fse->getFullname() };
        aguix->copyFileListToClipboard( l, id == m_current_popup_settings.cut_entry_id );
    } else if ( id == m_current_popup_settings.cut_selected_id ||
                id == m_current_popup_settings.copy_selected_id ) {
        std::list< NM_specialsourceExt > filelist;

        getSelFiles( filelist, ListerMode::LM_GETFILES_ONLYSELECT );

        std::vector< std::string > l;

        for ( auto &sse : filelist ) {
            const FileEntry *tfe = sse.entry();

            if ( tfe ) {
                l.push_back( tfe->fullname );
            }
        }

        aguix->copyFileListToClipboard( l, id == m_current_popup_settings.cut_selected_id );
    } else if ( id == m_current_popup_settings.paste_id ) {
        paste_files_from_clipboard();
    } else {
        // no action so check other menu options
        if ( msg->popupmenu.recursive_ids != NULL &&
             msg->popupmenu.recursive_ids->empty() == false ) {
            if ( msg->popupmenu.recursive_ids->back() == m_current_popup_settings.label_menu_id ) {
                std::list<int> entry_ids = *msg->popupmenu.recursive_ids;
                entry_ids.pop_back();
                handleLabelPopUp( *es, entry_ids, msg );
            } else if ( msg->popupmenu.recursive_ids->back() == m_current_popup_settings.filter_menu_id ) {
                std::list<int> entry_ids = *msg->popupmenu.recursive_ids;
                entry_ids.pop_back();
                handleFilterPopUp( entry_ids, msg );
            } else if ( msg->popupmenu.recursive_ids->back() == m_current_popup_settings.presets_menu_id ) {
                std::list<int> entry_ids = *msg->popupmenu.recursive_ids;
                entry_ids.pop_back();
                handleDirectoryPresetsPopUp( entry_ids, msg );
            }
        }
    }
}

void VirtualDirMode::openContextMenu( const context_menu_settings &sets )
{
    if ( ce.get() == NULL || m_lv == NULL ) return;

    context_menu_settings previous_sets = m_context_menu_sets;

    m_context_menu_sets = sets;
    
    lv_pressed( m_lv->getActiveRow(), false );

    m_context_menu_sets = previous_sets;
}

std::list<PopUpMenu::PopUpEntry> VirtualDirMode::buildLabelPopUpData( const NWCEntrySelectionState &es )
{
    int descr_id = 0;
    std::list<PopUpMenu::PopUpEntry> m1;

    const NWC::FSEntry *fse = es.getNWCEntry();

    if ( ! fse ) return m1;

    if ( fse->isBrokenLink() == true ) return m1;
    if ( fse->getBasename() == ".." ) return m1;

    std::unique_ptr<BookmarkDBEntry> db_entry = Worker::getBookmarkDBInstance().getEntry( fse->getFullname() );

    PopUpMenu::PopUpEntry e1;
    e1.name = catalog.getLocale( 834 );
    if ( db_entry.get() != NULL ) {
        e1.type = PopUpMenu::NORMAL;
    } else {
        e1.type = PopUpMenu::GREYED_OUT;
    }
    e1.id = descr_id++;

    m_current_popup_settings.label_id_to_action[e1.id] = label_popup_table_t( label_popup_table_t::REMOVE_LABEL, "" );

    m1.push_back( e1 );

    e1.type = PopUpMenu::HLINE;
    m1.push_back( e1 );
    
    std::list<std::string> cats = Worker::getBookmarkDBInstance().getCats();
    const std::map<std::string, WConfig::ColorDef::label_colors_t> labels = wconfig->getColorDefs().getLabelColors();
    std::map<std::string, WConfig::ColorDef::label_colors_t>::const_iterator label_it;

    for ( label_it = labels.begin(); label_it != labels.end(); ++label_it ) {
        if ( label_it->first == "" ) continue;

        if ( db_entry.get() == NULL ||
             db_entry->getCategory() != label_it->first ) {
            e1.type = PopUpMenu::NORMAL;

            if ( label_it->first == Worker::getKVPStore().getStringValue( "last-selected-label", "" ) ) {
                e1.highlight_on_show = true;
            } else {
                e1.highlight_on_show = false;
            }
        } else {
            e1.type = PopUpMenu::GREYED_OUT;
        }
        e1.name = label_it->first;
        e1.id = descr_id++;

        e1.fg = label_it->second.normal_fg;
        e1.bg = label_it->second.normal_bg;

        m_current_popup_settings.label_id_to_action[e1.id] = label_popup_table_t( label_popup_table_t::ADD_LABEL, label_it->first );

        m1.push_back( e1 );
    }
    e1.fg = e1.bg = -1;

    bool first = true;
    for ( std::list<std::string>::iterator it1 = cats.begin();
          it1 != cats.end();
          ++it1 ) {
        if ( *it1 == "" ) continue;

        if ( labels.find( *it1 ) == labels.end() ) {

            if ( first == true ) {
                e1.type = PopUpMenu::HLINE;
                m1.push_back( e1 );
                first = false;
            }

            if ( db_entry.get() == NULL ||
                 db_entry->getCategory() != *it1 ) {
                e1.type = PopUpMenu::NORMAL;
            } else {
                e1.type = PopUpMenu::GREYED_OUT;
            }
            e1.name = *it1;
            e1.id = descr_id++;

            m_current_popup_settings.label_id_to_action[e1.id] = label_popup_table_t( label_popup_table_t::ADD_LABEL, *it1 );

            m1.push_back( e1 );
        }
    }

    if ( first == true ) {
        e1.type = PopUpMenu::HLINE;
        m1.push_back( e1 );
        first = false;
    }
    
    e1.type = PopUpMenu::EDITABLE;
    e1.name = catalog.getLocale( 910 );
    e1.id = descr_id++;
    
    m_current_popup_settings.label_id_to_action[e1.id] = label_popup_table_t( label_popup_table_t::ADD_CUSTOM_LABEL, "" );
    
    m1.push_back( e1 );

    e1.type = PopUpMenu::HLINE;
    m1.push_back( e1 );
    
    if ( db_entry.get() == NULL ||
         db_entry->getCategory() != "" ) {
        e1.type = PopUpMenu::NORMAL;
    } else {
        e1.type = PopUpMenu::GREYED_OUT;
    }
    e1.name = catalog.getLocale( 847 );
    e1.id = descr_id++;
    
    m_current_popup_settings.label_id_to_action[e1.id] = label_popup_table_t( label_popup_table_t::ADD_LABEL, "" );
    
    m1.push_back( e1 );
    
    return m1;
}

void VirtualDirMode::handleLabelPopUp( AGMessage *msg )
{
    if ( msg == NULL || ce.get() == NULL || m_lv == NULL ) return;

    if ( msg->popupmenu.menu != m_current_popup_settings.label_popup_menu.get() ) return;
    
    int row = m_lv->getActiveRow();

    if ( ! m_lv->isValidRow( row ) ) return;

    int ce_id = m_lv->getData( row, -1 );

    if ( ! ce->getUse( ce_id ) ) return;

    const NWCEntrySelectionState *es = ce->getEntry( ce_id );

    if ( ! es ) return;

    const NWC::FSEntry *fse = es->getNWCEntry();

    if ( ! fse ) return;

    if ( m_current_popup_settings.entry_for_popup.empty() ) return;
    if ( m_current_popup_settings.entry_for_popup != fse->getFullname() ) return;
    // ok, still the same entry
    
    std::list<int> entry_ids = *msg->popupmenu.recursive_ids;
    handleLabelPopUp( *es, entry_ids, msg );
}

void VirtualDirMode::handleLabelPopUp( const NWCEntrySelectionState &es, const std::list<int> &entry_ids, AGMessage *msg )
{
    if ( entry_ids.empty() == true ) return;
    if ( msg == NULL ) return;
    if ( ! ( msg->type == AG_POPUPMENU_CLICKED ||
             msg->type == AG_POPUPMENU_ENTRYEDITED ) ) return;

    m_current_popup_settings.label_entry_clicked = true;

    const NWC::FSEntry *fse = es.getNWCEntry();

    if ( ! fse ) return;

    int id = entry_ids.back();
    
    std::map<int, label_popup_table_t>::const_iterator it1 = m_current_popup_settings.label_id_to_action.find( id );
    if ( it1 != m_current_popup_settings.label_id_to_action.end() ) {
        // found entry

        BookmarkDBProxy &bookmarks = Worker::getBookmarkDBInstance();
        //TODO read necessary?
        bookmarks.read();

        std::unique_ptr<BookmarkDBEntry> entry = bookmarks.getEntry( fse->getFullname() );

        bool use_parent = true;

        if ( fse->isDir() ) use_parent = false;

        if ( it1->second.type == label_popup_table_t::REMOVE_LABEL ) {
            if ( entry.get() != NULL ) {
                bookmarks.delEntry( *entry );
                bookmarks.write();
            }
        } else if ( it1->second.type == label_popup_table_t::ADD_LABEL ) {
            if ( entry.get() != NULL ) {
                BookmarkDBEntry newentry( *entry );
                newentry.setCategory( it1->second.label );
                bookmarks.updateEntry( *entry, newentry );
            } else {
                BookmarkDBEntry newentry( it1->second.label, fse->getFullname(), "", use_parent );
                bookmarks.addEntry( newentry );
            }
            bookmarks.write();

            Worker::getKVPStore().setStringValue( "last-selected-label", it1->second.label );
        } else if ( it1->second.type == label_popup_table_t::ADD_CUSTOM_LABEL ) {
            if ( msg->popupmenu.edit_content != NULL ) {
                std::string custom_label = *msg->popupmenu.edit_content;
                if ( entry.get() != NULL ) {
                    BookmarkDBEntry newentry( *entry );
                    newentry.setCategory( custom_label );
                    bookmarks.updateEntry( *entry, newentry );
                } else {
                    BookmarkDBEntry newentry( custom_label, fse->getFullname(), "", use_parent );
                    bookmarks.addEntry( newentry );
                }
                bookmarks.write();
            }
        }
        
        updateOnBookmarkChange();
    }
}

void VirtualDirMode::buildLabelPopUpMenu( const NWCEntrySelectionState &es )
{
    freePopUpMenus();

    const NWC::FSEntry *fse = es.getNWCEntry();

    if ( ! fse ) return;

    if ( fse->isBrokenLink() == true ) return;
    if ( fse->getBasename() == ".." ) return;

    m_current_popup_settings.reset();

    std::list<PopUpMenu::PopUpEntry> labelmenu = buildLabelPopUpData( es );

    m_current_popup_settings.label_menu_id = -2;

    m_current_popup_settings.label_popup_menu = std::unique_ptr<PopUpMenu>( new PopUpMenu( aguix, labelmenu ) );
    m_current_popup_settings.label_popup_menu->create();
}

void VirtualDirMode::openLabelPopUp()
{
    if ( ce.get() == NULL || m_lv == NULL ) return;

    int row = m_lv->getActiveRow();

    if ( m_lv->isValidRow( row ) == false ) return;
    
    int ce_id = m_lv->getData( row, -1 );

    if ( ce->getUse( ce_id ) ) {
        const NWCEntrySelectionState *es = ce->getEntry( ce_id );

        if ( ! es ) return;

        const NWC::FSEntry *fse = es->getNWCEntry();

        if ( ! fse ) return;

        buildLabelPopUpMenu( *es );
        m_current_popup_settings.entry_for_popup = fse->getFullname();
        if ( m_current_popup_settings.label_popup_menu.get() != NULL ) {
            int tx, ty;
            
            aguix->getWidgetRootPosition( m_lv, &tx, &ty );
            tx += m_lv->getWidth() / 2;
            ty += m_lv->getHeight() / 2;
            m_current_popup_settings.label_popup_menu->show( tx, ty, PopUpMenu::POPUP_CENTER_IN_POSITION );
            //TODO better query the position of the active row from LV and use
            // this position
        }
    }
}

std::list< std::string > VirtualDirMode::getListOfMatchingFilters( const NWCEntrySelectionState &es )
{
    std::list< std::string > res;

    const NWC::FSEntry *fse = es.getNWCEntry();

    if ( ! fse ) return res;

    // first get all known filters that match the file
    auto setfilter_list = SetFilterOp::getHistory();

    StringMatcherFNMatch matcher;

    for ( auto &el : setfilter_list ) {
        if ( el != "*" ) {
            // check if filter matches current entry
            matcher.setMatchString( el );

            if ( matcher.match( fse->getBasename() ) ) {
                res.push_back( el );
            }
        }
    }

    auto filterselect_list = FilterSelectOp::getHistory();

    for ( auto &el : filterselect_list ) {
        if ( el != "*" ) {
            // check if filter matches current entry
            matcher.setMatchString( el );

            if ( matcher.match( fse->getBasename() ) ) {
                if ( std::find( res.begin(), res.end(),
                                el ) == res.end() ) {
                    res.push_back( el );
                }
            }
        }
    }

    // now get extension and add it if it is not already there
    std::string n = fse->getBasename();
    std::string::size_type p = n.rfind( '.' );

    if ( p != std::string::npos ) {
        std::string filter = "*" + std::string( n, p );

        if ( std::find( res.begin(), res.end(),
                        filter ) == res.end() ) {
            res.push_back( filter );
        }
    }

    return res;
}

std::list<PopUpMenu::PopUpEntry> VirtualDirMode::buildFilterPopUpData( const NWCEntrySelectionState *es )
{
    int descr_id = 0;
    std::list<PopUpMenu::PopUpEntry> m1;
    PopUpMenu::PopUpEntry e1;

    if ( es ) {
        auto list = getListOfMatchingFilters( *es );

        for ( auto &el : list ) {
            e1.name = AGUIXUtils::formatStringToString( catalog.getLocale( 1172 ), el.c_str() );
            e1.type = PopUpMenu::NORMAL;
            e1.id = descr_id++;

            m_current_popup_settings.filter_id_to_action[ e1.id ] = filter_popup_table_t( filter_popup_table_t::FILTER_ADD, el );

            m1.push_back( e1 );
        }
    }

    e1.type = PopUpMenu::EDITABLE;
    e1.name = catalog.getLocale( 1323 );
    e1.id = descr_id++;
    
    m_current_popup_settings.filter_id_to_action[e1.id] = filter_popup_table_t( filter_popup_table_t::ADD_CUSTOM_FILTER, "" );
    
    m1.push_back( e1 );

    e1.type = PopUpMenu::HLINE;
    m1.push_back( e1 );

    std::list<NM_Filter> filters = m_dir_filter_sets.getFilters();
    bool filters_active = false;

    for ( auto &el : filters ) {
        if ( el.getCheck() != NM_Filter::INACTIVE ) {
            filters_active = true;
            break;
        }
    }

    e1.name = catalog.getLocale( 1445 );
    if ( filters_active ) {
        e1.type = PopUpMenu::NORMAL;
    } else {
        e1.type = PopUpMenu::GREYED_OUT;
    }
    e1.id = descr_id++;

    m_current_popup_settings.filter_id_to_action[ e1.id ] = filter_popup_table_t( filter_popup_table_t::STASH_CURRENT_FILTERS, "" );

    m1.push_back( e1 );

    e1.type = PopUpMenu::SUBMENU;
    e1.name = catalog.getLocale( 1446 );
    std::list<PopUpMenu::PopUpEntry> stash_menu;
    const auto stash = parentlister->getWorker()->getFilterStash()->getStash();

    if ( ! stash.empty() ) {
        size_t stash_pos = 0;
        for ( const auto &stash_entry : stash ) {
            PopUpMenu::PopUpEntry stash_menu_entry;
            stash_menu_entry.type = PopUpMenu::SUBMENU;
            stash_menu_entry.name = AGUIXUtils::formatStringToString( "stash%lu", stash_pos );

            PopUpMenu::PopUpEntry stash_submenu_entry;

            stash_submenu_entry.type = PopUpMenu::NORMAL;
            stash_submenu_entry.name = catalog.getLocale( 1447 );
            stash_submenu_entry.id = descr_id++;
            m_current_popup_settings.filter_id_to_action[ stash_submenu_entry.id ] = filter_popup_table_t( filter_popup_table_t::APPLY_STASH, "", stash_pos );
            stash_menu_entry.submenu.push_back( stash_submenu_entry );
            
            stash_submenu_entry.type = PopUpMenu::NORMAL;
            stash_submenu_entry.name = catalog.getLocale( 1448 );
            stash_submenu_entry.id = descr_id++;
            m_current_popup_settings.filter_id_to_action[ stash_submenu_entry.id ] = filter_popup_table_t( filter_popup_table_t::APPLY_AND_REMOVE_STASH, "", stash_pos );
            stash_menu_entry.submenu.push_back( stash_submenu_entry );

            stash_submenu_entry.type = PopUpMenu::NORMAL;
            stash_submenu_entry.name = catalog.getLocale( 828 );
            stash_submenu_entry.id = descr_id++;
            m_current_popup_settings.filter_id_to_action[ stash_submenu_entry.id ] = filter_popup_table_t( filter_popup_table_t::REMOVE_STASH, "", stash_pos );
            stash_menu_entry.submenu.push_back( stash_submenu_entry );

            stash_submenu_entry.type = PopUpMenu::HLINE;
            stash_menu_entry.submenu.push_back( stash_submenu_entry );

            for ( const auto &filter : stash_entry ) {
                if ( filter.getCheck() == NM_Filter::INACTIVE ) {
                    continue;
                }

                stash_submenu_entry.type = PopUpMenu::HEADER;
                stash_submenu_entry.name = filter.getPattern();

                if ( filter.getCheck() == NM_Filter::EXCLUDE ) {
                    stash_submenu_entry.name += " (";
                    stash_submenu_entry.name += catalog.getLocale( 171 );
                    stash_submenu_entry.name += ")";
                } else if ( filter.getCheck() == NM_Filter::INCLUDE ) {
                    stash_submenu_entry.name += " (";
                    stash_submenu_entry.name += catalog.getLocale( 170 );
                    stash_submenu_entry.name += ")";
                }

                stash_submenu_entry.id = descr_id++;
                stash_menu_entry.submenu.push_back( stash_submenu_entry );
            }

            stash_menu.push_back( stash_menu_entry );

            stash_pos++;
        }
        e1.submenu = stash_menu;
    } else {
        e1.type = PopUpMenu::GREYED_OUT;
    }

    e1.id = descr_id++;

    m1.push_back( e1 );

    e1.name = catalog.getLocale( 1173 );
    if ( filters_active ) {
        e1.type = PopUpMenu::NORMAL;
    } else {
        e1.type = PopUpMenu::GREYED_OUT;
    }
    e1.id = descr_id++;

    m_current_popup_settings.filter_id_to_action[ e1.id ] = filter_popup_table_t( filter_popup_table_t::FILTER_REMOVE_ALL, "" );

    m1.push_back( e1 );

    for ( auto &el : filters ) {
        if ( el.getCheck() != NM_Filter::INACTIVE ) {
            e1.name = AGUIXUtils::formatStringToString( catalog.getLocale( 1174 ), el.getPattern() );
            e1.type = PopUpMenu::NORMAL;
            e1.id = descr_id++;

            m_current_popup_settings.filter_id_to_action[ e1.id ] = filter_popup_table_t( filter_popup_table_t::FILTER_REMOVE, el.getPattern() );

            m1.push_back( e1 );
        }
    }

    return m1;
}

void VirtualDirMode::handleFilterPopUp( const std::list<int> &entry_ids, AGMessage *msg )
{
    if ( entry_ids.empty() == true ) return;
    if ( msg == NULL ) return;
    if ( ! ( msg->type == AG_POPUPMENU_CLICKED ||
             msg->type == AG_POPUPMENU_ENTRYEDITED ) ) return;

    int id = entry_ids.front();

    std::map< int, filter_popup_table_t >::const_iterator it1 = m_current_popup_settings.filter_id_to_action.find( id );
    if ( it1 != m_current_popup_settings.filter_id_to_action.end() ) {
        // found entry

        if ( it1->second.type == filter_popup_table_t::FILTER_REMOVE ) {
            setFilter( it1->second.filter, VirtualDirMode::FILTER_UNSET );
        } else if ( it1->second.type == filter_popup_table_t::FILTER_REMOVE_ALL ) {
            unsetAllFilters();
        } else if ( it1->second.type == filter_popup_table_t::STASH_CURRENT_FILTERS ) {
            parentlister->getWorker()->getFilterStash()->stash( this );
            unsetAllFilters();
        } else if ( it1->second.type == filter_popup_table_t::APPLY_STASH ) {
            parentlister->getWorker()->getFilterStash()->applyEntry( this,
                                                                     it1->second.stash_pos );
        } else if ( it1->second.type == filter_popup_table_t::APPLY_AND_REMOVE_STASH ) {
            parentlister->getWorker()->getFilterStash()->applyEntry( this,
                                                                     it1->second.stash_pos );              
            parentlister->getWorker()->getFilterStash()->deleteEntry( it1->second.stash_pos );
        } else if ( it1->second.type == filter_popup_table_t::REMOVE_STASH ) {
            parentlister->getWorker()->getFilterStash()->deleteEntry( it1->second.stash_pos );
        } else if ( it1->second.type == filter_popup_table_t::FILTER_ADD ) {
            setFilter( it1->second.filter, VirtualDirMode::FILTER_EXCLUDE );
        } else if ( it1->second.type == filter_popup_table_t::ADD_CUSTOM_FILTER ) {
            if ( msg->popupmenu.edit_content != NULL ) {
                std::string custom_filter = *msg->popupmenu.edit_content;
                setFilter( custom_filter, VirtualDirMode::FILTER_EXCLUDE );
            }
        }
    }
}

bool VirtualDirMode::startdnd( DNDMsg *dm )
{
    bool returnvalue = false;

    if ( dm->getSourceWidget() == NULL && dm->getDestWidget() == m_lv ) {
        // that is an external drop

        if ( dm->getExternalData() == NULL ) return false;

        return handleExternalDrop( *dm->getExternalData() );
    }
    
    if ( ! ce.get() ) return false;

    if ( dm->getSourceWidget() == m_lv ) {
        returnvalue = true;

        int row = dm->getValue();
        NMRowData *rdp = dynamic_cast< NMRowData *>( dm->getRowDataP() );

        if ( rdp && m_lv->isValidRow( row ) ) {
    
            int ce_id = m_lv->getData( row, -1 );

            if ( ce->getUse( ce_id ) ) {
                const NWCEntrySelectionState *es = ce->getEntry( ce_id );

                if ( es ) {

                    const NWC::FSEntry *fse = es->getNWCEntry();

                    if ( fse && fse->getFullname() == rdp->getFullname() ) {

                        // found entry for dnd
                        // now use the type to call the dnd action

                        WCFiletype *ft;
                        ActionMessage amsg( parentlister->getWorker() );
                        amsg.startLister = parentlister;
                        amsg.mode = amsg.AM_MODE_DNDACTION;

                        if ( fse->isDir() == false ) {
                            ft = es->getFiletype();
                            if ( ft == NULL ) ft = wconfig->getnotyettype(); // not yet checked
                        } else {
                            ft = wconfig->getdirtype();
                        }

                        if ( ft == NULL ) ft = wconfig->getvoidtype();

                        if ( ft != NULL ) {
                            if ( ft->getDNDActions().empty() ) ft = wconfig->getvoidtype();
                            if ( ft != NULL ) {
                                amsg.dndmsg = dm;
                                amsg.filetype = ft;
                                amsg.m_action_descr = RefCount<ActionDescr>( new DNDAction::DNDActionDescr() );
                                parentlister->getWorker()->interpret( ft->getDNDActions(), &amsg );
                            }
                        }
                    }
                }
            }
        }
    }
    return returnvalue;
}

bool VirtualDirMode::currentDirIsReal() const
{
    if ( ce.get() ) return ce->isRealDir();

    return false;
}

void VirtualDirMode::toggleHidden()
{
    setShowHidden( ( m_dir_filter_sets.getShowHidden() == true ) ? false : true );
}

void VirtualDirMode::setShowHidden( bool nhf )
{
    m_search_mode_restore_hidden = false;

    finishsearchmode();
  
    m_dir_filter_sets.setShowHidden( nhf );

    //updateDirFilter();

    rebuildView();

    setName();

    if ( m_lv != NULL ) m_lv->showActive();
}

void VirtualDirMode::command_selectall()
{
    if ( ce.get() == NULL ) return;

    if ( m_lv->getElements() < ( m_show_dotdot ? 2 : 1 ) ) return;

    finishsearchmode();

    for ( int row = m_show_dotdot ? 1 : 0; row < m_lv->getElements(); row++ ) {

        bool state = m_lv->getSelect( row );

        if ( ! state ) {
            m_lv->setSelect( row, true );

            ce->modifyEntry( m_lv->getData( row, -1 ),
                             []( NWCEntrySelectionState &es ) { es.setSelected( true ); } );
        }
    }

    showCacheState();
}

void VirtualDirMode::command_selectnone()
{
    if ( ce.get() == NULL ) return;

    if ( m_lv->getElements() < ( m_show_dotdot ? 2 : 1 ) ) return;

    finishsearchmode();

    createSelectionCheckpoint();

    for ( int row = m_show_dotdot ? 1 : 0; row < m_lv->getElements(); row++ ) {

        bool state = m_lv->getSelect( row );

        if ( state ) {
            m_lv->setSelect( row, false );

            ce->modifyEntry( m_lv->getData( row, -1 ),
                             []( NWCEntrySelectionState &es ) { es.setSelected( false ); } );
        }
    }

    showCacheState();

    m_selection_checkpoint_dirty = true;
}

void VirtualDirMode::command_invertall()
{
    if ( ce.get() == NULL ) return;

    if ( m_lv->getElements() < ( m_show_dotdot ? 2 : 1 ) ) return;

    finishsearchmode();

    createSelectionCheckpoint();

    for ( int row = m_show_dotdot ? 1 : 0; row < m_lv->getElements(); row++ ) {

        bool state = m_lv->getSelect( row );

        state = state ? false : true;

        m_lv->setSelect( row, state );

        ce->modifyEntry( m_lv->getData( row, -1 ),
                         [state]( NWCEntrySelectionState &es ) { es.setSelected( state ); } );

        if ( state == false ) {
            m_selection_checkpoint_dirty = true;
        }
    }

    showCacheState();
}

void VirtualDirMode::switch_to_tab_direction( const std::list< RefCount< ArgClass > > &args )
{
    if ( ! args.empty() ) {
        RefCount< ArgClass > a1 = args.front();
        if ( a1.get() != NULL ) {
            IntArg *a2 = dynamic_cast< IntArg *>( a1.get() );
            if ( a2 != NULL ) {
                switchToTabDirection( a2->getValue() );
            } else if ( auto sa1 = dynamic_cast< StringArg *>( a1.get() ) ) {
                auto conv = sa1->convertToInt();

                if ( conv.second ) {
                    switchToTabDirection( conv.first.getValue() );
                }
            }
        }
    }
}

void VirtualDirMode::move_tab_direction( const std::list< RefCount< ArgClass > > &args )
{
    if ( ! args.empty() ) {
        RefCount< ArgClass > a1 = args.front();
        if ( a1.get() != NULL ) {
            IntArg *a2 = dynamic_cast< IntArg *>( a1.get() );
            if ( a2 != NULL ) {
                moveTabDirection( a2->getValue() );
            } else if ( auto sa1 = dynamic_cast< StringArg *>( a1.get() ) ) {
                auto conv = sa1->convertToInt();

                if ( conv.second ) {
                    moveTabDirection( conv.first.getValue() );
                }
            }
        }
    }
}

void VirtualDirMode::scrollListView( int dir )
{
    m_lv->setXOffset( m_lv->getXOffset() + dir * m_lv->getHScrollStep() );
    aguix->Flush();
}

void VirtualDirMode::scrollVertToAbs( int pos )
{
    m_lv->setYOffset( pos );
    aguix->Flush();
}

void VirtualDirMode::scrollHorizToAbs( int pos )
{
    m_lv->setXOffset( pos );
    aguix->Flush();
}

void VirtualDirMode::selectByFilter( const std::string &filter, bool select )
{
    std::string realfilter;
    bool usedir;
  
    if ( filter.empty() ) return;

    if ( ce.get() == NULL ) return;

    if ( m_lv->getElements() < ( m_show_dotdot ? 2 : 1 ) ) return;

    finishsearchmode();

    createSelectionCheckpoint();

    if ( filter[0] == '/' ) {
        usedir = true;
        realfilter = std::string( filter, 1 );
    } else {
        usedir = false;
        realfilter = filter;
    }
  
    StringMatcherFNMatch matcher;

    matcher.setMatchString( realfilter );

    for ( int row = m_show_dotdot ? 1 : 0; row < m_lv->getElements(); row++ ) {

        int ce_id = m_lv->getData( row, -1 );

        if ( ce->getUse( ce_id ) ) {
            const NWCEntrySelectionState *es = ce->getEntry( ce_id );

            if ( ! es ) continue;

            const NWC::FSEntry *fse = es->getNWCEntry();

            if ( ! fse ) continue;

            if ( fse->getBasename() == "..") continue;

            if ( matcher.match( fse->getBasename() ) ) {
                if ( fse->isDir( true ) == usedir ) {
                    bool state = m_lv->getSelect( row );

                    if ( state != select ) {
                        m_lv->setSelect( row, select );

                        ce->modifyEntry( m_lv->getData( row, -1 ),
                                         [select]( NWCEntrySelectionState &tes ) { tes.setSelected( select ); } );

                        if ( state == false ) {
                            m_selection_checkpoint_dirty = true;
                        }
                    }
                }
            }
        }
    }

    showCacheState();
}

void VirtualDirMode::make_dir()
{
    if ( ce.get() == NULL ) return;

    char *buttonstr, *textstr, *return_str;
    int erg;
    std::string basedir;

    basedir = getCurrentDirectory();

    if ( basedir.empty() ) return;
  
    finishsearchmode();
  
    textstr = dupstring( catalog.getLocale( 152 ) );
    buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 11 ) ) + 1 +
                                   strlen( catalog.getLocale( 8 ) ) + 1 );
    sprintf( buttonstr, "%s|%s", catalog.getLocale( 11 ),
             catalog.getLocale( 8 ) );
    erg = Worker::getRequester()->string_request( catalog.getLocale( 1268 ), textstr, "", buttonstr, &return_str );
    _freesafe( buttonstr );
    _freesafe( textstr );

    disableDirWatch();

    if ( erg == 0 && strlen( return_str ) > 0 ) {
        std::string destdir;

        destdir = NWC::Path::join( basedir, return_str );

        if ( worker_mkdir( destdir.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ) != 0 ) {
            textstr = (char*)_allocsafe( strlen( catalog.getLocale( 127 ) ) + strlen( destdir.c_str() ) + 1 );
            sprintf( textstr, catalog.getLocale( 127 ), destdir.c_str() );
            buttonstr = dupstring( catalog.getLocale( 11 ) );
            erg = Worker::getRequester()->request( catalog.getLocale( 347 ), textstr, buttonstr );
            _freesafe( textstr );
            _freesafe( buttonstr );
        } else {
            Worker::pushCommandLog( catalog.getLocale( 1423 ), destdir, "", destdir );
            if ( currentDirIsReal() ) {
                update();

                activateEntry( return_str, true );
            } else {
                ce->beginModification( DMCacheEntryNWC::MOD_INSERTION );

                ce->insertEntry( destdir );

                ce->endModification();

                activateEntry( return_str, true );

                setCurrentCE( ce );

                m_lv->showActive();
            }
        }
    }

    _freesafe( return_str );

    enableDirWatch();
}

void VirtualDirMode::renamef( struct NM_renameorder *renorder )
{
    int erg;
    bool skip, cancel, nameok;
    char *buttonstr, *textstr, *return_str;

    if ( ce.get() == NULL ) return;

    finishsearchmode();

    disableDirWatch();
  
    std::list< NM_specialsourceExt > renlist;

    switch ( renorder->source ) {
        case NM_renameorder::NM_SPECIAL:
            copySourceList( *( renorder->sources ),
                            renlist, false );
            break;
        case NM_renameorder::NM_ONLYACTIVE:
            getSelFiles( renlist, LM_GETFILES_ONLYACTIVE, false, CONSIDER_ALL_ELEMENTS );
            break;
        default:  // all selected entries
            getSelFiles( renlist, LM_GETFILES_SELORACT, false, CONSIDER_ALL_ELEMENTS );
            break;
    }
  
    cancel = skip = false;

    ce->beginModification( DMCacheEntryNWC::MOD_RENAMING );

    for ( auto &ss1 : renlist ) {
        if ( cancel == true ) break;
        // now rename ss1->entry

        int row = getRowForCEPos( ss1.getID() );

        auto es = ce->getEntry( ss1.getID() );

        if ( ! es ) continue;

        auto fse = es->getNWCEntry();

        if ( ! fse ) continue;

        if ( row >= 0 ) {
            m_lv->setVisMark( row, true );
            m_lv->showRow( row );
        }

        char *newname;

        if ( ! UTF8::isValidCharacterString( fse->getBasename().c_str() ) ) {
            newname = dupstring( UTF8::convertToValidCharacterString( fse->getBasename().c_str() ).c_str() );
        } else {
            newname = dupstring( fse->getBasename().c_str() );
        }
        
        std::string newfullname;

        for ( nameok = false; nameok == false; ) {
            buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 11 ) ) + 1 +
                                           strlen( catalog.getLocale( 225 ) ) + 1 +
                                           strlen( catalog.getLocale( 8 ) ) + 1 );
            sprintf( buttonstr, "%s|%s|%s", catalog.getLocale( 11 ),
                     catalog.getLocale( 225 ),
                     catalog.getLocale( 8 ) );
            textstr = (char*)_allocsafe( strlen( catalog.getLocale( 148 ) ) + strlen( fse->getBasename().c_str() ) + 1 );
            sprintf( textstr, catalog.getLocale( 148 ), fse->getBasename().c_str() );

            size_t select_characters = strlen( newname );
            if ( ! renorder->also_select_extension ) {
                const char *extension = strrchr( newname, '.' );
                if ( extension ) {
                    select_characters = extension - newname;
                }
            }

            Requester::request_options options;
            options.select_characters = select_characters;

            erg = Worker::getRequester()->string_request( catalog.getLocale( 149 ),
                                                          textstr,
                                                          newname,
                                                          buttonstr,
                                                          &return_str,
                                                          NULL,
                                                          Requester::REQUEST_NONE,
                                                          options );
            _freesafe( buttonstr );
            _freesafe( textstr );
            _freesafe( newname );

            newname = return_str;

            if ( erg == 2 ) {
                cancel = true;
                break;
            } else if ( erg == 1 ) {
                break;
            } else {
                if ( strcmp( newname, fse->getBasename().c_str() ) == 0 ) {
                    // same name as orig
                    // so skip this entry
                    break;
                } else if ( strlen( newname ) < 1 ) {
                    // empty string->repeat
                    continue;
                }

                newfullname = NWC::Path::join( fse->getDirname(),
                                               newname );

                if ( Datei::lfileExistsExt( newfullname.c_str() ) != Datei::D_FE_NOFILE ) {
                    textstr = (char*)_allocsafe( strlen( catalog.getLocale( 279 ) ) + strlen( newname ) + 1 );
                    sprintf( textstr, catalog.getLocale( 279 ), newname );
                    buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 230 ) ) + 1 +
                                                   strlen( catalog.getLocale( 225 ) ) + 1 +
                                                   strlen( catalog.getLocale( 8 ) ) + 1 );
                    sprintf( buttonstr, "%s|%s|%s", catalog.getLocale( 230 ),
                             catalog.getLocale( 225 ),
                             catalog.getLocale( 8 ) );
                    erg = Worker::getRequester()->request( catalog.getLocale( 123 ),
                                                           textstr,
                                                           buttonstr );
                    _freesafe( buttonstr );
                    _freesafe( textstr );

                    if ( erg == 1 ) break;
                    else if ( erg == 2 ) {
                        cancel = true;
                        break;
                    }

                    newfullname.clear();
                } else {
                    nameok = true;
                }
            }
        }
        if ( nameok == true && ! newfullname.empty() ) {
            erg = worker_rename( fse->getFullname().c_str(),
                                 newfullname.c_str() );
            if ( erg != 0 ) {
                textstr = dupstring( catalog.getLocale(151) );
                buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 11 ) ) + 1 +
                                               strlen( catalog.getLocale( 8 ) ) + 1 );
                sprintf( buttonstr, "%s|%s", catalog.getLocale( 11 ),
                         catalog.getLocale( 8 ) );
                erg = Worker::getRequester()->request( catalog.getLocale( 347 ),
                                                       textstr, buttonstr );
                _freesafe( buttonstr );
                _freesafe( textstr );
                if ( erg == 1 ) cancel = true;
            } else {
                Worker::pushCommandLog( catalog.getLocale( 1424 ),
                                        newfullname,
                                        fse->getFullname().c_str(),
                                        AGUIXUtils::formatStringToString( "%s to %s",
                                                                          fse->getFullname().c_str(),
                                                                          newfullname.c_str() ) );

                setEntrySelectionState( ss1.getID(), false );

                ce->changeBasenameOfEntry( newname,
                                           ss1.getID() );

                updateLVRow( row );
            }
        }

        _freesafe( newname );
        newfullname.clear();

        if ( row >= 0 ) {
            m_lv->setVisMark( row, false );
        }
    }
  
    ce->endModification();

    if ( currentDirIsReal() ) {
        update();
    } else {
        setCurrentCE( ce );

        m_lv->showActive();
    }

    enableDirWatch();
}

int VirtualDirMode::request_symlink_dest( const std::string &fullname,
                                          const std::string orig_symlink_dest,
                                          std::string &return_dest )
{
    AWindow *win = new AWindow( aguix, 10, 10, 10, 10, catalog.getLocale( 1278 ), AWindow::AWINDOW_DIALOG );
    win->create();
    win->setTransientForAWindow( aguix->getTransientWindow() );

    AContainer *ac1 = win->setContainer( new AContainer( win, 1, 4 ), true );
    ac1->setMinSpace( 0 );
    ac1->setMaxSpace( 0 );
    ac1->setBorderWidth( 5 );

    AContainerBB *ac1_texts = dynamic_cast<AContainerBB*>( ac1->add( new AContainerBB( win, 1, 1 ), 0, 0 ) );
    ac1_texts->setBorderWidth( 5 );

    std::string textstr = AGUIXUtils::formatStringToString( catalog.getLocale( 206 ),
                                                            NWC::Path::basename( fullname ).c_str() );
    
    ac1_texts->addWidget( new Text( aguix, 0, 0, textstr.c_str() ),
                          0, 0, AContainer::CO_INCW );

    AContainer *ac1_sg = ac1->add( new AContainer( win, 2, 1 ), 0, 1 );
    ac1_sg->setMinSpace( 0 );
    ac1_sg->setMaxSpace( 0 );
    ac1_sg->setBorderWidth( 0 );

    StringGadget *sg = ac1_sg->addWidget( new StringGadget( aguix, 0, 0, 200, orig_symlink_dest.c_str(), 0 ),
                                          0, 0, AContainer::CO_INCW );
    Button *toggleb = ac1_sg->addWidget( new Button( aguix, 0, 0, catalog.getLocale( 1091 ), 0 ),
                                          1, 0, AContainer::CO_FIX );
    sg->setAcceptFocus( true );

    ac1->setMinHeight( 5, 0, 2 );

    AContainer *ac1_buttons = ac1->add( new AContainer( win, 3, 1 ), 0, 3 );
    ac1_buttons->setMinSpace( 5 );
    ac1_buttons->setMaxSpace( -1 );
    ac1_buttons->setBorderWidth( 0 );

    Button *okb = ac1_buttons->addWidget( new Button( aguix, 0, 0, catalog.getLocale( 11 ), 0 ),
                                          0, 0, AContainer::CO_FIX );
    Button *skipb = ac1_buttons->addWidget( new Button( aguix, 0, 0, catalog.getLocale( 225 ), 0 ),
                                            1, 0, AContainer::CO_FIX );
    Button *cancelb = ac1_buttons->addWidget( new Button( aguix, 0, 0, catalog.getLocale( 8 ), 0 ),
                                              2, 0, AContainer::CO_FIX );
    okb->setAcceptFocus( true );
    skipb->setAcceptFocus( true );
    cancelb->setAcceptFocus( true );
 
    sg->takeFocus();
    win->useStippleBackground();
    win->setDoTabCycling( true );
    win->contMaximize( true );
    win->centerScreen();
    win->show();

    AGMessage *msg;
    int endmode = -1;

    for(; endmode == -1; ) {
        msg = aguix->WaitMessage( win );
        if ( msg != NULL ) {
            switch ( msg->type ) {
                case AG_CLOSEWINDOW:
                    if ( msg->closewindow.window == win->getWindow() ) {
                        endmode = 2;
                    }
                    break;
                case AG_BUTTONCLICKED:
                    if ( msg->button.button == okb ) {
                        endmode = 0;
                    } else if ( msg->button.button == skipb ) {
                        endmode = 1;
                    } else if ( msg->button.button == cancelb ) {
                        endmode = 2;
                    } else if ( msg->button.button == toggleb ) {
                        std::string current_dest = sg->getText();

                        if ( current_dest.length() > 0 && current_dest[0] != '/' ) {
                            std::string dirname_of_source = NWC::Path::get_real_path( NWC::Path::dirname( fullname ) );
                            if ( dirname_of_source.empty() ) dirname_of_source = NWC::Path::dirname( fullname );

                            std::string fulloldlink = NWC::Path::join( dirname_of_source,
                                                                       current_dest );
                            fulloldlink = NWC::Path::normalize( fulloldlink );

                            if ( ! fulloldlink.empty() ) {
                                sg->setText( fulloldlink.c_str() );
                            }
                        } else if ( current_dest.length() > 0 && current_dest[0] == '/' ) {
                            char *sldest = Datei::getRelativePathExt( current_dest.c_str(),
                                                                      NWC::Path::dirname( fullname ).c_str(),
                                                                      true );
                            if ( sldest != NULL ) {
                                sg->setText( sldest );

                                _freesafe( sldest );
                            }
                        }
                    }
                    break;
                case AG_STRINGGADGET_OK:
                    if ( msg->stringgadget.sg == sg ) endmode = 0;
                    break;
                case AG_STRINGGADGET_CANCEL:
                    if ( msg->stringgadget.sg == sg ) {
                        endmode = 2;
                    }
                    break;
                case AG_KEYPRESSED:
                    if ( win->isParent( msg->key.window, false ) == true ) {
                        switch ( msg->key.key ) {
                            case XK_KP_Enter:
                            case XK_Return:
                                if ( okb->getHasFocus() == true ) {
                                    endmode = 0;
                                } else if ( skipb->getHasFocus() == true ) {
                                    endmode = 1;
                                } else if ( cancelb->getHasFocus() == true ) {
                                    endmode = 2;
                                }
                                break;
                            case XK_Escape:
                                endmode = 2;
                                break;
                            case XK_F1:
                                endmode = 0;
                                break;
                            case XK_F2:
                                endmode = 1;
                                break;
                            case XK_F3:
                                endmode = 2;
                                break;
                        }
                    }
                    break;
            }
            aguix->ReplyMessage( msg );
        }
    }

    return_dest = sg->getText();

    delete win;

    return endmode;
}

void VirtualDirMode::changesymlink(struct NM_changesymlinkorder *chslorder)
{
    int erg;
    bool cancel;
    char *buttonstr, *textstr;

    if ( ce.get() == NULL ) return;

    finishsearchmode();
  
    disableDirWatch();
  
    std::list< NM_specialsourceExt > csllist;

    switch ( chslorder->source ) {
        case NM_changesymlinkorder::NM_SPECIAL:
            copySourceList( *( chslorder->sources ),
                            csllist, false );
            break;
        case NM_changesymlinkorder::NM_ONLYACTIVE:
            getSelFiles( csllist, LM_GETFILES_ONLYACTIVE, false );
            break;
        default:  // all selected entries
            getSelFiles( csllist, LM_GETFILES_SELORACT, false );
            break;
    }

    cancel = false;

    for ( auto &ss1 : csllist ) {
        if ( cancel == true ) break;
        // now rename ss1->entry

        int row = getRowForCEPos( ss1.getID() );

        auto es = ce->getEntry( ss1.getID() );

        if ( ! es ) continue;

        auto fse = es->getNWCEntry();

        if ( ! fse ) continue;

        if ( row >= 0 ) {
            m_lv->setVisMark( row, true );
            m_lv->showRow( row );
        }

        if ( fse->isLink() ) {
            char buffer[4096];

            int tx = worker_readlink( fse->getFullname().c_str(),
                                      buffer, 4095 );

            if ( tx >= 0 ) {
                std::string link_dest;
                std::string new_link_dest;

                buffer[tx] = 0;

                if ( chslorder->mode == chslorder->CSLO_MAKE_ABSOLUTE ) {
                    erg = 1;

                    if ( strlen( buffer ) > 0 && buffer[0] != '/' ) {
                        std::string dirname_of_source = NWC::Path::get_real_path( NWC::Path::dirname( fse->getFullname() ) );
                        if ( dirname_of_source.empty() ) dirname_of_source = NWC::Path::dirname( fse->getFullname() );

                        std::string fulloldlink = NWC::Path::join( dirname_of_source,
                                                                   buffer );
                        fulloldlink = NWC::Path::normalize( fulloldlink );

                        if ( ! fulloldlink.empty() ) {
                            erg = 0;
                            new_link_dest = fulloldlink;
                        }
                    }
                } else if ( chslorder->mode == chslorder->CSLO_MAKE_RELATIVE ) {
                    erg = 1;

                    if ( strlen( buffer ) > 0 && buffer[0] == '/' ) {
                        char *sldest = Datei::getRelativePathExt( buffer,
                                                                  NWC::Path::dirname( fse->getFullname() ).c_str(),
                                                                  true );
                        if ( sldest != NULL ) {
                            erg = 0;
                            new_link_dest = sldest;

                            _freesafe( sldest );
                        }
                    }
                } else {
                    if ( ! UTF8::isValidCharacterString( buffer ) ) {
                        link_dest = UTF8::convertToValidCharacterString( buffer );
                    } else {
                        link_dest = buffer;
                    }

                    new_link_dest = "";
                    erg = request_symlink_dest( fse->getFullname(),
                                                link_dest,
                                                new_link_dest );
                }

                if ( erg == 2 ) {
                    // cancel
                    cancel = true;
                } else if ( erg == 0 && ! new_link_dest.empty() ) {
                    // ok
                    if ( strcmp( link_dest.c_str(), new_link_dest.c_str() ) != 0 ) {
                        // only if new value differs
                        if ( worker_unlink( fse->getFullname().c_str() ) == 0 ) {
                            if ( worker_symlink( new_link_dest.c_str(), fse->getFullname().c_str() ) == 0 ) {
                                // deselect
                                setEntrySelectionState( ss1.getID(), false );

                                Worker::pushCommandLog( catalog.getLocale( 1425 ),
                                                        fse->getFullname(),
                                                        "",
                                                        AGUIXUtils::formatStringToString( catalog.getLocale( 1421 ),
                                                                                          fse->getFullname().c_str(),
                                                                                          new_link_dest.c_str() ) );
                            } else {
                                textstr = (char*)_allocsafe( strlen( catalog.getLocale( 205 ) ) +
                                                             strlen( fse->getFullname().c_str() ) + 1 );
                                sprintf( textstr, catalog.getLocale( 205 ), fse->getFullname().c_str() );
                                buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 11 ) ) + 1 +
                                                               strlen( catalog.getLocale( 8 ) ) + 1 );
                                sprintf( buttonstr, "%s|%s", catalog.getLocale( 11 ),
                                         catalog.getLocale( 8 ) );
                                erg = Worker::getRequester()->request( catalog.getLocale( 347 ),
                                                                       textstr, buttonstr );
                                _freesafe( buttonstr );
                                _freesafe( textstr );
                                if ( erg == 1 ) cancel = true;
                            }
                        } else {
                            textstr = (char*)_allocsafe( strlen( catalog.getLocale( 291 ) ) +
                                                         strlen( fse->getFullname().c_str() ) + 1 );
                            sprintf( textstr, catalog.getLocale( 291 ), fse->getFullname().c_str() );
                            buttonstr = dupstring( catalog.getLocale(11) );
                            Worker::getRequester()->request( catalog.getLocale( 347 ),
                                                             textstr, buttonstr );
                            _freesafe( textstr );
                            _freesafe( buttonstr );
                        }
                    }
                }
            }
        }

        if ( row >= 0 ) {
            m_lv->setVisMark( row, false );
        }
    }

    if ( currentDirIsReal() ) {
        update();
    } else {
        updateCE();

        setCurrentCE( ce );

        m_lv->showActive();
    }

    enableDirWatch();
}

void VirtualDirMode::createsymlink( struct NM_createsymlinkorder *cslorder )
{
    int erg;
    bool cancel, nameok;
    char *buttonstr, *textstr, *return_str;
    char *sldest;
    Lister *olister = NULL;
    ListerMode *lm = NULL;
    VirtualDirMode *target_vdm = NULL;

    if ( ce.get() == NULL ) return;

    finishsearchmode();
  
    disableDirWatch();

    if ( getCurrentDirectory() == cslorder->destdir ) {
        target_vdm = this;
    }

    olister = parentlister->getWorker()->getOtherLister( parentlister );
    if ( olister == NULL ) return;

    lm = olister->getActiveMode();
    if ( lm != NULL ) {
        if ( lm->getCurrentDirectory() == cslorder->destdir ) {
            target_vdm = dynamic_cast< VirtualDirMode * >( lm );
        }
    }

    if ( target_vdm != NULL && target_vdm != this ) {
        target_vdm->disableDirWatch();
    }
  
    std::list< NM_specialsourceExt > csllist;

    switch ( cslorder->source ) {
        case NM_createsymlinkorder::NM_SPECIAL:
            copySourceList( *( cslorder->sources ),
                            csllist, false );
            break;
        case NM_createsymlinkorder::NM_ONLYACTIVE:
            getSelFiles( csllist, LM_GETFILES_ONLYACTIVE, false, CONSIDER_ALL_ELEMENTS );
            break;
        default:  // all selected entries
            getSelFiles( csllist, LM_GETFILES_SELORACT, false, CONSIDER_ALL_ELEMENTS );
            break;
    }

    cancel = false;

    std::list< std::string > inserts;

    for ( auto &ss1 : csllist ) {
        if ( cancel == true ) break;
        // now rename ss1->entry

        int row = getRowForCEPos( ss1.getID() );

        auto es = ce->getEntry( ss1.getID() );

        if ( ! es ) continue;

        auto fse = es->getNWCEntry();

        if ( ! fse ) continue;

        if ( row >= 0 ) {
            m_lv->setVisMark( row, true );
            m_lv->showRow( row );
        }

        char *newname = dupstring( fse->getBasename().c_str() );
        std::string newfullname;

        for ( nameok = false; nameok == false; ) {
            buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 11 ) ) + 1 +
                                           strlen( catalog.getLocale( 225 ) ) + 1 +
                                           strlen( catalog.getLocale( 8 ) ) + 1 );
            sprintf( buttonstr, "%s|%s|%s", catalog.getLocale( 11 ),
                     catalog.getLocale( 225 ),
                     catalog.getLocale( 8 ) );
            textstr = (char*)_allocsafe( strlen( catalog.getLocale( 207 ) ) +
                                         strlen( fse->getBasename().c_str() ) + 1 );
            sprintf( textstr, catalog.getLocale( 207 ), fse->getBasename().c_str() );
            erg = Worker::getRequester()->string_request( catalog.getLocale( 1277 ),
                                                          textstr,
                                                          newname,
                                                          buttonstr,
                                                          &return_str );
            _freesafe( buttonstr );
            _freesafe( textstr );
            _freesafe( newname );

            newname = return_str;

            if ( erg == 2 ) {
                cancel = true;
                break;
            } else if ( erg == 1 ) {
                break;
            } else {
                newfullname = NWC::Path::join( cslorder->destdir,
                                               newname );

                if ( Datei::lfileExistsExt( newfullname.c_str() ) != Datei::D_FE_NOFILE ) {
                    textstr = (char*)_allocsafe( strlen( catalog.getLocale( 279 ) ) +
                                                 strlen( newname ) + 1 );
                    sprintf( textstr, catalog.getLocale( 279 ), newname );
                    buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 230 ) ) + 1 +
                                                   strlen( catalog.getLocale( 225 ) ) + 1 +
                                                   strlen( catalog.getLocale( 8 ) ) + 1 );
                    sprintf( buttonstr, "%s|%s|%s", catalog.getLocale( 230 ),
                             catalog.getLocale( 225 ),
                             catalog.getLocale( 8 ) );
                    erg = Worker::getRequester()->request( catalog.getLocale( 123 ),
                                                           textstr,
                                                           buttonstr );
                    _freesafe( buttonstr );
                    _freesafe( textstr );

                    if ( erg == 1 ) break;
                    else if ( erg == 2 ) {
                        cancel = true;
                        break;
                    }
                    newfullname.clear();
                } else {
                    nameok = true;
                }
            }
        }

        if ( nameok == true && ! newfullname.empty() ) {
            // now create symlink newfullname which points to fe->fullname or fe->name
            if ( cslorder->local == true ) {
                sldest = Datei::getRelativePathExt( fse->getFullname().c_str(), cslorder->destdir, true );
                if ( sldest == NULL ) {
                    //TODO: I currently can't imagine a situation but the call
                    //      can return NULL (shouldn't happen because I give
                    //      correct info) so in this case just use the fullname
                    //      But give the user a requester?
                    sldest = dupstring( fse->getFullname().c_str() );
                }
            } else {
                sldest = dupstring( fse->getFullname().c_str() );
            }

            erg = worker_symlink( sldest, newfullname.c_str() );

            std::string symlink_dest = sldest;

            _freesafe( sldest );

            if ( erg != 0 ) {
                textstr = (char*)_allocsafe( strlen( catalog.getLocale( 205 ) ) +
                                             strlen( newfullname.c_str() ) + 1 );
                sprintf( textstr, catalog.getLocale( 205 ), newfullname.c_str() );
                buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 11 ) ) + 1 +
                                               strlen( catalog.getLocale( 8 ) ) + 1 );
                sprintf( buttonstr, "%s|%s", catalog.getLocale( 11 ),
                         catalog.getLocale( 8 ) );
                erg = Worker::getRequester()->request( catalog.getLocale( 347 ),
                                                       textstr,
                                                       buttonstr );
                _freesafe( buttonstr );
                _freesafe( textstr );

                if ( erg == 1 ) cancel = true;
            } else {
                setEntrySelectionState( ss1.getID(), false );

                inserts.push_back( newfullname );

                Worker::pushCommandLog( catalog.getLocale( 1426 ),
                                        newfullname,
                                        "",
                                        AGUIXUtils::formatStringToString( catalog.getLocale( 1421 ),
                                                                          newfullname.c_str(),
                                                                          symlink_dest.c_str() ) );
            }
        }
        _freesafe( newname );

        if ( row >= 0 ) {
            m_lv->setVisMark( row, false );
        }
    }

    if ( target_vdm && ! target_vdm->currentDirIsReal() ) {
        target_vdm->addEntries( inserts );
    }
  
    if ( target_vdm != NULL ) target_vdm->update();

    enableDirWatch();

    if ( target_vdm != NULL && target_vdm != this ) {
        target_vdm->enableDirWatch();
    }
}

int VirtualDirMode::addEntries( const std::list< std::string > &entries )
{
    if ( ce.get() != NULL &&
         ! currentDirIsReal() ) {
        ce->beginModification( DMCacheEntryNWC::MOD_INSERTION );

        for ( auto &f : entries ) {
            ce->insertEntry( f );
        }

        ce->endModification();

        setCurrentCE( ce );

        m_lv->showActive();
    }

    return 0;
}

class DirSizeWalkCallBack : public NWC::DirWalkCallBack
{
public:
    DirSizeWalkCallBack( AGUIX *aguix, Lister *lister ) :
        m_size( 0 ),
        m_aguix( aguix ),
        m_lister( lister ),
        m_last_statebar_update( (time_t)0 ) {}
    ~DirSizeWalkCallBack() {}

    int visit( NWC::File &file, NWC::Dir::WalkControlObj &cobj )
    {
        time_t now;

        now = time( NULL );
        if ( m_last_statebar_update != now ) {
            FileNameShrinker fs;
            AFontWidth fw( m_aguix, NULL );
            std::string shrinked_text;
      
            shrinked_text = fs.shrink( file.getFullname(),
                                       m_lister->getWorker()->getStatebarWidth(),
                                       fw );

            m_lister->setStatebarText( shrinked_text.c_str() );
            m_last_statebar_update = now;
        }

        m_size += file.stat_size();

        m_aguix->doXMsgs( NULL, false );

        if ( m_aguix->getLastKeyRelease() == XK_Escape ) {
            cobj.setMode( NWC::Dir::WalkControlObj::ABORT );
        }

        return 0;
    }

    loff_t getSize() const
    {
        return m_size;
    }
private:
    loff_t m_size;
    AGUIX *m_aguix;
    Lister *m_lister;
    time_t m_last_statebar_update;
};


void VirtualDirMode::dirsize( struct NM_dirsizeorder *dsorder )
{
    if ( dsorder == NULL ) return;

    if ( ce.get() == NULL ) return;

    finishsearchmode();
  
    std::list< NM_specialsourceExt > dslist;

    switch ( dsorder->source ) {
        case NM_dirsizeorder::NM_SPECIAL:
            copySourceList( *( dsorder->sources ),
                            dslist, false );
            break;
        case NM_dirsizeorder::NM_ONLYACTIVE:
            getSelFiles( dslist, LM_GETFILES_ONLYACTIVE, false, CONSIDER_ALL_ELEMENTS );
            break;
        default:  // all selected entries
            getSelFiles( dslist, LM_GETFILES_SELORACT, false, CONSIDER_ALL_ELEMENTS );
            break;
    }

    parentlister->getWorker()->setWaitCursor();
    aguix->getLastKeyRelease();
    aguix->getLastMouseRelease();

    bool cancel = false;

    for ( auto &ss1 : dslist ) {
        if ( cancel == true ) break;
        // now rename ss1->entry

        int row = getRowForCEPos( ss1.getID() );

        auto es = ce->getEntry( ss1.getID() );

        if ( ! es ) continue;

        auto fse = es->getNWCEntry();

        if ( ! fse ) continue;

        if ( fse->isDir() ) {
            loff_t size = 0;

            DirSizeWalkCallBack cb( aguix, parentlister );

            NWC::Dir tdir( *fse );
            tdir.readDir( false );

            NWC::Dir::WalkControlObj wco = tdir.walk( cb, NWC::Dir::RECURSIVE_DEMAND );

            if ( wco.getMode() == NWC::Dir::WalkControlObj::NORMAL ) {
                size = cb.getSize();
            } else {
                cancel = true;
            }

            ce->modifyEntry( ss1.getID(),
                             [size]( NWCEntrySelectionState &tes ) {
                                 NWC::FSEntry *entry = tes.getNWCEntry();

                                 if ( NWC::Dir *dir = dynamic_cast< NWC::Dir * >( entry ) ) {
                                     dir->setBytesInDir( size );
                                 }
                             } );

            if ( m_lv->isValidRow( row ) == true ) {
                updateLVRow( row );
                m_lv->simpleRedrawUpdatedRows();
                m_lv->showRow( row );
            }
        }
    }

    showCacheState();

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

    parentlister->getWorker()->unsetWaitCursor();
}

void VirtualDirMode::chmodf( struct NM_chmodorder *cmorder )
{
    if ( cmorder == NULL ) return;

    if ( ce.get() == NULL ) return;

    finishsearchmode();
  
    disableDirWatch();
 
    ChModOwnCore changecore( cmorder, NULL, aguix );

    std::list< NM_specialsourceExt > cmlist;

    switch ( cmorder->source ) {
        case NM_chmodorder::NM_SPECIAL:
            copySourceList( *( cmorder->sources ),
                            cmlist, false );
            break;
        case NM_chmodorder::NM_ONLYACTIVE:
            getSelFiles( cmlist, LM_GETFILES_ONLYACTIVE, false,
                         cmorder->recursive ? CONSIDER_PREFIX_ELEMENTS_ONLY : CONSIDER_ALL_ELEMENTS );
            break;
        default:  // all selected entries
            getSelFiles( cmlist, LM_GETFILES_SELORACT, false,
                         cmorder->recursive ? CONSIDER_PREFIX_ELEMENTS_ONLY : CONSIDER_ALL_ELEMENTS );
            break;
    }

    for ( auto &it : cmlist ) {
        if ( it.entry() ) {
            changecore.registerFEToChange( *it.entry(), it.getID() );
        }
    }

    changecore.setPostChangeCallback( [this]( const FileEntry &fe, int id,
                                              ChModOwnCore::nm_change_t res )
                                      {
                                          if ( res == ChModOwnCore::NM_CHANGE_OK ) {
                                              auto es = ce->getEntry( id );

                                              if ( es ) {

                                                  auto fse = es->getNWCEntry();

                                                  if ( fse && fse->getFullname() == fe.fullname ) {
                                                      setEntrySelectionState( id, false );
                                                  }
                                              }
                                          }
                                      } );

    changecore.executeChMod();

    if ( currentDirIsReal() ) {
        update();
    } else {
        updateCE();

        setCurrentCE( ce );

        m_lv->showActive();
    }

    enableDirWatch();
}

void VirtualDirMode::chownf( struct NM_chownorder *coorder )
{
    if ( coorder == NULL ) return;

    if ( ce.get() == NULL ) return;

    finishsearchmode();
  
    disableDirWatch();
  
    ChModOwnCore changecore( NULL, coorder, aguix );

    std::list< NM_specialsourceExt > colist;

    switch ( coorder->source ) {
        case NM_chownorder::NM_SPECIAL:
            copySourceList( *( coorder->sources ),
                            colist, false );
            break;
        case NM_chownorder::NM_ONLYACTIVE:
            getSelFiles( colist, LM_GETFILES_ONLYACTIVE, false,
                         coorder->recursive ? CONSIDER_PREFIX_ELEMENTS_ONLY : CONSIDER_ALL_ELEMENTS );
            break;
        default:  // all selected entries
            getSelFiles( colist, LM_GETFILES_SELORACT, false,
                         coorder->recursive ? CONSIDER_PREFIX_ELEMENTS_ONLY : CONSIDER_ALL_ELEMENTS );
            break;
    }

    for ( auto &it : colist ) {
        if ( it.entry() ) {
            changecore.registerFEToChange( *it.entry(), it.getID() );
        }
    }

    changecore.setPostChangeCallback( [this]( const FileEntry &fe, int id,
                                              ChModOwnCore::nm_change_t res )
                                      {
                                          if ( res == ChModOwnCore::NM_CHANGE_OK ) {
                                              auto es = ce->getEntry( id );

                                              if ( es ) {

                                                  auto fse = es->getNWCEntry();

                                                  if ( fse && fse->getFullname() == fe.fullname ) {
                                                      setEntrySelectionState( id, false );
                                                  }
                                              }
                                          }
                                      } );

    changecore.executeChOwn();

    if ( currentDirIsReal() ) {
        update();
    } else {
        updateCE();

        setCurrentCE( ce );

        m_lv->showActive();
    }

    enableDirWatch();
}

void VirtualDirMode::unsetAllFilters()
{
    m_dir_filter_sets.unsetAllFilters();

    //updateDirFilter();

    rebuildView();

    setName();

    if ( m_lv != NULL ) m_lv->showActive();
}

void VirtualDirMode::setFilter( const std::string &filter, vdm_filter_t mode )
{
    NM_Filter::check_t newmode;

    switch ( mode ) {
        case FILTER_EXCLUDE:
            newmode = NM_Filter::EXCLUDE;
            break;
        case FILTER_UNSET:
            newmode = NM_Filter::INACTIVE;
            break;
        default:
            newmode = NM_Filter::INCLUDE;
            break;
    }
    m_dir_filter_sets.setFilter( filter.c_str(), newmode );
    
    //updateDirFilter();

    rebuildView();

    setName();

    if ( m_lv != NULL ) m_lv->showActive();
}

VirtualDirMode::vdm_filter_t VirtualDirMode::checkFilter( const std::string &filter )
{
    NM_Filter::check_t newmode = NM_Filter::INACTIVE;
    try {
        newmode = m_dir_filter_sets.checkFilter( filter.c_str() );
    } catch ( int i ) {
        throw i;
    }

    switch ( newmode ) {
        case NM_Filter::EXCLUDE:
            return FILTER_EXCLUDE;
            break;
        case NM_Filter::INCLUDE:
            return FILTER_INCLUDE;
            break;
        default:
            break;
    }
    return FILTER_UNSET;
}

void VirtualDirMode::setBookmarkFilter( DirFilterSettings::bookmark_filter_t v )
{
    finishsearchmode();
  
    m_dir_filter_sets.setBookmarkFilter( v );

    //updateDirFilter();

    rebuildView();

    setName();

    if ( m_lv != NULL ) m_lv->showActive();
}

DirFilterSettings::bookmark_filter_t VirtualDirMode::getBookmarkFilter() const
{
    return m_dir_filter_sets.getBookmarkFilter();
}

void VirtualDirMode::setSpecificBookmarkLabel( const std::string &l )
{
    finishsearchmode();
    
    m_dir_filter_sets.setSpecificBookmarkLabel( l );

    //updateDirFilter();

    rebuildView();

    setName();

    if ( m_lv != NULL ) m_lv->showActive();
}

const std::string &VirtualDirMode::getSpecificBookmarkLabel() const
{
    return m_dir_filter_sets.getSpecificBookmarkLabel();
}

int VirtualDirMode::queryLVDimensions( int &tx, int &ty,
                                       int &tw, int &th )
{
    if ( m_lv != NULL ) {
        aguix->getWidgetRootPosition( m_lv, &tx, &ty );
        tw = m_lv->getWidth();
        th = m_lv->getHeight();
        return 0;
    }
    return 1;
}

void VirtualDirMode::setFilters( const std::list<NM_Filter> &filters )
{
    m_dir_filter_sets.setFilters( filters );

    rebuildView();

    setName();

    if ( m_lv != NULL ) m_lv->showActive();
}

std::list< NM_Filter > VirtualDirMode::getFilters() const
{
    return m_dir_filter_sets.getFilters();
}

void VirtualDirMode::setHighlightBookmarkPrefix( bool nv )
{
    m_dir_filter_sets.setHighlightBookmarkPrefix( nv );

    rebuildView();

    setName();

    if ( m_lv != NULL ) m_lv->showActive();
}

bool VirtualDirMode::getHighlightBookmarkPrefix() const
{
    return m_dir_filter_sets.getHighlightBookmarkPrefix();
}

void VirtualDirMode::recenterTopBottom()
{
    if ( ce.get() == NULL ) return;
    
    finishsearchmode();

    int row = m_lv->getActiveRow();

    if ( ! m_lv->isValidRow( row ) ) return;

    if ( m_lv->getYOffset() != m_recenter_state.last_yoffset ||
         m_lv->getActiveRow() != m_recenter_state.last_active_row ) {
        // changed row, so start from beginning
        m_recenter_state.last_state = 0;
    } else {
        // some row, so jump to next state

        switch ( m_recenter_state.last_state ) {
            case 1:
                m_recenter_state.last_state = 2;
                break;
            case 2:
                m_recenter_state.last_state = 0;
                break;
            default:
                m_recenter_state.last_state = 1;
                break;
        }
    }

    if ( m_recenter_state.last_state == 2 ) {
        // switch to bottom_row
        m_lv->setYOffset( row - m_lv->getMaxDisplayV() + 1 );
    } else if ( m_recenter_state.last_state == 0 ) {
        // switch to center
        m_lv->setYOffset( row - m_lv->getMaxDisplayV() / 2 );
    } else if ( m_recenter_state.last_state == 1 ) {
        // switch to top row
        m_lv->setYOffset( row );
    }

    m_recenter_state.last_yoffset = m_lv->getYOffset();
    m_recenter_state.last_active_row = m_lv->getActiveRow();
}

void VirtualDirMode::makeListViewRowActive( int row )
{
    if ( m_lv && parentlister->isActive() == true ) {
        m_lv->setActiveRow( row );
    }
}

std::unique_ptr< NWC::Dir > VirtualDirMode::getCurrentDir()
{
    if ( ! ce.get() ) return std::unique_ptr< NWC::Dir >( nullptr );

    if ( ce->getNWCDir() == NULL ) return std::unique_ptr< NWC::Dir >();

    NWC::FSEntry *fse = ce->getNWCDir()->clone();

    if ( ! fse ) {
        return std::unique_ptr< NWC::Dir >( nullptr );
    }

    NWC::Dir *d = dynamic_cast< NWC::Dir * >( fse );

    if ( ! d ) {
        delete fse;
        return std::unique_ptr< NWC::Dir >( nullptr );
    }
    
    return std::unique_ptr< NWC::Dir >( d );
}

RefCount< DMCacheEntryNWC > VirtualDirMode::findCacheEntry( const std::string &name,
                                                            const std::string &common_prefix,
                                                            bool backward_match )
{
    RefCount< DMCacheEntryNWC > res;
    std::vector< std::string > components;
    std::vector< std::string > compare_components;

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

    std::string old_name = components.at( 0 );
    std::string compare_prefix = common_prefix;

    if ( ! AGUIXUtils::ends_with( compare_prefix, "/" ) ) {
        compare_prefix += "/";
    }

    size_t candidate_length = 0;

    AGUIXUtils::split_string( compare_components,
                              compare_prefix, '/' );

    if ( ! compare_components.empty() &&
         compare_components[compare_components.size() - 1].empty() ) {
        compare_components.resize( compare_components.size() - 1 );
    }

    for ( auto &cache_entry_pair : m_cache ) {
        components.clear();

        AGUIXUtils::split_string( components, cache_entry_pair.second->getNameOfDir(), '/' );

        std::string test_prefix = cache_entry_pair.second->getCommonPrefix();

        if ( ! AGUIXUtils::ends_with( test_prefix, "/" ) ) {
            test_prefix += "/";
        }

        if ( backward_match == true ) {
            // search for entry that is not larger then given prefix and is the longest prefix
            std::vector< std::string > test_components;

            AGUIXUtils::split_string( test_components,
                                      test_prefix, '/' );

            if ( ! test_components.empty() &&
                 test_components[test_components.size() - 1].empty() ) {
                test_components.resize( test_components.size() - 1 );
            }

            if ( test_components.size() <= compare_components.size() ) {
                bool mismatch = false;

                for ( size_t i = 0; i < test_components.size(); i++ ) {
                    if ( test_components[i] != compare_components[i] ) {
                        mismatch = true;
                        break;
                    }
                }

                if ( mismatch == false ) {
                    if ( candidate_length == 0 ||
                         test_components.size() > candidate_length ) {
                        res = cache_entry_pair.second;
                        candidate_length = test_components.size();
                    }
                }
            }
        } else {
            // search for entry that is longer but the shortest of all matches
            if ( components.at( 0 ) == old_name &&
                 AGUIXUtils::starts_with( test_prefix, compare_prefix ) ) {
                if ( candidate_length == 0 ||
                     test_prefix.length() < candidate_length ) {
                    res = cache_entry_pair.second;
                    candidate_length = test_prefix.length();
                }
            }
        }
    }

    return res;
}

std::unique_ptr< NWC::Dir > VirtualDirMode::createSubVDir( RefCount< DMCacheEntryNWC > base_ce,
                                                           const std::string &common_prefix )
{
    std::string old_name = base_ce->getNameOfDir();
    std::vector< std::string > components;

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

    std::string name = AGUIXUtils::formatStringToString( "%s/%d",
                                                         components.at( 0 ).c_str(),
                                                         s_vdir_refine_number++ );

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

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

    std::string matching_prefix = common_prefix;

    if ( ! AGUIXUtils::ends_with( matching_prefix, "/" ) ) {
        matching_prefix += "/";
    }

    for ( int pos = 0;
          pos < (int)base_ce->getSize();
          pos++ ) {
        auto es = base_ce->getEntry( pos );

        if ( ! es ) continue;

        auto fse = es->getNWCEntry();

        if ( ! fse ) continue;

        if ( AGUIXUtils::starts_with( fse->getFullname(),
                                      matching_prefix ) ) {
            d->add( *fse );
        }
    }

    return d;
}

void VirtualDirMode::enter_active()
{
    if ( ! ce.get() ) return;

    auto es = ce->getEntry( ce->getActiveEntryPos() );

    if ( ! es ) return;

    const NWC::FSEntry *fse = es->getNWCEntry();

    if ( fse == NULL ) return;

    if ( currentDirIsReal() ) {
        if ( fse->isDir( true ) ) {
            showDir( fse->getFullname() );
        }
        return;
    }

    const std::string entry_dirname = fse->getFullname();
    std::string vdir_name = m_common_prefix;

    if ( m_common_prefix.length() >= entry_dirname.length() ) return;

    std::string::size_type pos = entry_dirname.find( '/', m_common_prefix.length() );

    if ( pos == m_common_prefix.length() ) {
        pos = entry_dirname.find( '/', m_common_prefix.length() + 1 );
    }

    if ( pos == std::string::npos ) {
        // no other directory in between

        if ( fse->isDir( true ) ) {
            showDir( fse->getFullname() );
        }
        
        return;
    }

    std::string new_dirname( entry_dirname, 0, pos );

    RefCount< DMCacheEntryNWC > res = findCacheEntry( ce->getNameOfDir(),
                                                      new_dirname, false );

    if ( res.get() ) {
        // found entry
        setCurrentCE( res );

        activateEntry( entry_dirname, false );
    } else {
        // create new vdir with matching entry
        std::unique_ptr< NWC::Dir > d = createSubVDir( ce,
                                                       new_dirname );
        showDir( d );

        activateEntry( entry_dirname, false );
    }
}

class FlattenDirWCB : public NWC::DirWalkCallBack
{
public:
    FlattenDirWCB( std::unique_ptr<NWC::Dir> &res ) : m_res( res ) {}
    ~FlattenDirWCB() {}
    FlattenDirWCB( const FlattenDirWCB &other );
    FlattenDirWCB &operator=( const FlattenDirWCB &other );

    int visit( NWC::File &file, NWC::Dir::WalkControlObj &cobj )
    {
        m_res->add( NWC::FSEntry( file.getFullname() ) );
        return 0;
    }

private:
    std::unique_ptr<NWC::Dir> &m_res;
};

void VirtualDirMode::flatten_dir( bool follow_symlinks )
{
    if ( ! ce.get() ) return;

    std::string name = AGUIXUtils::formatStringToString( "flatdir%d", s_flatdir_number++ );

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

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

    FlattenDirWCB wcb( flatdir );

    if ( ce->isRealDir() ) {
        std::string base_dir = ce->getNameOfDir();

        NWC::Dir d( base_dir );
        d.setFollowSymlinks( follow_symlinks );
        d.readDir( false );

        d.walk( wcb, NWC::Dir::RECURSIVE_DEMAND );
    } else if ( ce->getNWCDir() ) {
        NWC::FSEntry *fse = ce->getNWCDir()->clone();
  
        if ( fse ) {
            NWC::VirtualDir *d = dynamic_cast< NWC::VirtualDir *>( fse );

            if ( d ) {
                d->walk( wcb, NWC::Dir::RECURSIVE_DEMAND );
            }

            delete fse;
        }
    }

    showDir( flatdir );
}

void VirtualDirMode::finalizeBGOps()
{
    wait_for_bg_copies();
}

void VirtualDirMode::go_to_previous_dir()
{
    finishsearchmode();

    m_go_to_previous_dir_active = true;

    m_previous_dir_pos++;

    if ( m_previous_dir_pos < (int)m_cache.size() ) {

        auto it = m_cache.begin();

        int pos = 0;

        while ( pos < m_previous_dir_pos && it != m_cache.end() ) {
            it++;
            pos++;
        }

        if ( it != m_cache.end() ) {
            RefCount< DMCacheEntryNWC > tce = it->second;

            tce->reload();

            setCurrentCE( tce );
        }
    }

    m_go_to_previous_dir_active = false;
}

void VirtualDirMode::setShowDotDot( bool nv )
{
    m_show_dotdot = nv;
}

void VirtualDirMode::setShowBreadcrumb( bool nv )
{
    m_breadcrumb.enabled = nv;

    update_breadcrumb_gui();
}

int VirtualDirMode::build_path_components( const std::string &path,
                                           std::vector< std::string > &path_components )
{
    std::vector< std::string > components;

    path_components.clear();

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

    if ( ! components.empty() &&
         components.at( 0 ).empty() ) {
        path_components.push_back( "/" );
    }

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

    return 0;
}

int VirtualDirMode::update_breadcrumb( const std::string &path, bool skip_gui_update )
{
    std::vector< std::string > components;

    if ( build_path_components( path, components ) != 0 ) return 1;

    std::size_t i;

    for ( i = 0;
          i < components.size() && i < m_breadcrumb.current_path_components.size();
          i++ ) {
        if ( components[i] != m_breadcrumb.current_path_components[i] ) {
            break;
        }
    }

    if ( i != components.size() ) {
        m_breadcrumb.current_path_components = components;
        m_tab_store.tab_entries.at( m_tab_store.active_tab ).path = path;
    }

    if ( components.size() > 0 ) {
        m_breadcrumb.current_path_component = components.size() - 1;
    }

    if ( ! skip_gui_update ) {
        update_breadcrumb_gui();
    }

    return 0;
}

int VirtualDirMode::update_breadcrumb_gui()
{
    std::size_t i = 0;
    int lw, lh, lx, ly;
    int currently_used_width = 0;
    int co_min_space = 2;

    if ( ! m_breadcrumb.enabled ) {

        for ( auto &b : m_breadcrumb.buttons ) {
            delete b;
        }
        m_breadcrumb.buttons.clear();

        delete m_breadcrumb.current_co;

        m_breadcrumb.current_co = NULL;

        if ( m_cont ) {
            m_cont->setMaxHeight( 0, 0 ,0 );
        }

        parentawindow->updateCont();

        return 0;
    }

    parentlister->getGeometry( &lx, &ly, &lw, &lh );

    for ( i = m_breadcrumb.current_path_components.size();
          i < m_breadcrumb.buttons.size();
          i++ ) {
        delete m_breadcrumb.buttons[i];
    }

    m_breadcrumb.buttons.resize( m_breadcrumb.current_path_components.size() );

    delete m_breadcrumb.current_co;

    m_breadcrumb.current_co = m_cont->add( new AContainer( parentawindow, m_breadcrumb.current_path_components.size() + 1, 1 ),
                                           0, 0 );
    m_breadcrumb.current_co->setBorderWidth( 2 );
    m_breadcrumb.current_co->setMinSpace( co_min_space );
    m_breadcrumb.current_co->setMaxSpace( 4 );

    currently_used_width = 2 + 2;

    i = 0;

    for ( auto &p : m_breadcrumb.current_path_components ) {
        if ( m_breadcrumb.buttons[i] == NULL ) {
            Button *b = m_breadcrumb.current_co->addWidget( new Button( aguix, 0, 0,
                                                                        p.c_str(), 0 ),
                                                            i, 0,
                                                            AContainer::ACONT_MAXW + AContainer::ACONT_MINH + AContainer::ACONT_MAXH );

            b->setAcceptFocus( false );

            m_breadcrumb.buttons[i] = b;

            m_breadcrumb.buttons[i]->show();
        } else {
            m_breadcrumb.buttons[i]->setText( 0, p.c_str() );
            m_breadcrumb.buttons[i]->resize( m_breadcrumb.buttons[i]->getMaximumWidth(),
                                             m_breadcrumb.buttons[i]->getHeight() );

            m_breadcrumb.current_co->addWidget( m_breadcrumb.buttons[i],
                                                i, 0,
                                                AContainer::ACONT_MAXW + AContainer::ACONT_MINH + AContainer::ACONT_MAXH );
        }

        if ( i == m_breadcrumb.current_path_component ) {
            m_breadcrumb.buttons[i]->setFG( 0, "button-special1-fg" );
            m_breadcrumb.buttons[i]->setBG( 0, "button-special1-bg" );

            int w = m_breadcrumb.buttons[i]->getMaximumWidth();

            // limit current path element to not use more than half of the list view width
            if ( w > lw / 2 ) {
                w = lw / 2;
            }

            m_breadcrumb.current_co->setMinWidth( w,
                                                  i, 0 );

            currently_used_width += co_min_space + w;
        } else {
            m_breadcrumb.buttons[i]->setFG( 0, "button-fg" );
            m_breadcrumb.buttons[i]->setBG( 0, "button-bg" );

            currently_used_width += co_min_space + 8;
        }

        i++;
    }

    if ( currently_used_width > lw ) {
        m_breadcrumb.current_co->setMinSpace( 0 );
   }

    parentawindow->updateCont();

    return 0;
}

std::string VirtualDirMode::mergeBreadcrumbComponents( std::size_t pos )
{
    std::string res;

    for ( std::size_t i = 0; i <= pos && i < m_breadcrumb.current_path_components.size(); i++ ) {
        if ( i != 0 ) {
            res += "/";
        }
        res += m_breadcrumb.current_path_components[i];
    }

    return res;
}

bool VirtualDirMode::checkBreadcrumb( AGMessage *msg )
{
    if ( ! msg ||
         msg->type != AG_BUTTONCLICKED ) {
        return false;
    }

    if ( m_breadcrumb.enabled ) {
        std::size_t i;

        for ( i = 0; i < m_breadcrumb.buttons.size(); i++ ) {
            if ( m_breadcrumb.buttons[i] == msg->button.button ) {
                std::string p = mergeBreadcrumbComponents( i );

                if ( ! p.empty() ) {
                    showDir( p );

                    return true;
                }
            }
        }
    }

    return false;
}

int VirtualDirMode::update_info_line()
{
    std::string info_line;
    const NWC::FSEntry *fse = NULL;
    const NWCEntrySelectionState *es = NULL;
    const WCFiletype *ft = NULL;

    if ( ! m_enable_info_line ) {
        return 0;
    }

    if ( ce.get() ) {

        es = ce->getEntry( ce->getActiveEntryPos() );

        if ( es ) {

            fse = es->getNWCEntry();

            if ( fse ) {
                if ( fse->isDir( true ) == false ) {
                    ft = es->getFiletype();
                    if ( ft == NULL ) ft = wconfig->getnotyettype();
                } else {
                    ft = wconfig->getdirtype();
                }
            }
        }
    }

    if ( ft == NULL ) ft = wconfig->getvoidtype();

    if ( m_last_info_line_fse == fse && m_last_info_line_ft == ft ) {
        return 0;
    }

    m_last_info_line_fse = fse;
    m_last_info_line_ft = ft;

    if ( fse == NULL ) {
        if ( strlen( m_info_line_sg->getText() ) != 0 ) {
            m_info_line_sg->setText( "" );
        }

        return 1;
    }

    std::string dest;

    fse->getDestination( dest );

    std::string file_type_name;

    if ( ft != NULL ) {
        file_type_name = ft->getName();

        if ( ft->nameContainsFlags() ) {
            auto fpr = std::make_shared< NWCFlagProducer >( es, nullptr );
            FlagReplacer rpl( fpr );

            file_type_name = rpl.replaceFlags( file_type_name, false );
        }
    }

    if ( ft == NULL || ft == wconfig->getunknowntype() ) {
        if ( ! es->getFiletypeFileOutput().empty() ) {
            file_type_name += " / ";
            file_type_name += es->getFiletypeFileOutput();
        }
    }

    std::stringstream buf;
    buf << fse->stat_size();
    std::string file_size = buf.str();
    std::string user = ugdb->getUserNameS( fse->stat_userid() );
    std::string group = ugdb->getGroupNameS( fse->stat_groupid() );

    if ( m_info_line_lua_mode ) {
        if ( m_lua_instance ) {
            m_lua_instance.setBoolVariable( "is_symlink", fse->isLink() );
            m_lua_instance.setStringVariable( "basename", fse->getBasename() );
            m_lua_instance.setStringVariable( "fullname", fse->getFullname() );
            m_lua_instance.setStringVariable( "filesize", file_size );
            m_lua_instance.setStringVariable( "filetype", file_type_name );
            m_lua_instance.setStringVariable( "symlink_dest", dest );
            m_lua_instance.setStringVariable( "user", user );
            m_lua_instance.setStringVariable( "group", group );

            if ( m_lua_instance.evalCommand( m_info_line_content, 1 ) == 0 ) {
                if ( m_lua_instance.getStringReturnValue( info_line ) != 0 ) {
                    info_line = m_lua_instance.getErrorString();
                }
            } else {
                info_line = m_lua_instance.getErrorString();
            }
        }
    } else {
        auto fl = std::make_shared< FlagReplacer::FlagProducerMap >();

        fl->registerFlag( "f", fse->getBasename() );
        fl->registerFlag( "F", fse->getFullname() );
        fl->registerFlag( "filetype", file_type_name );
        fl->registerFlag( "filesize", file_size );
        fl->registerFlag( "symlink_dest", dest );
        fl->registerFlag( "user", user );
        fl->registerFlag( "group", group );

        FlagReplacer rpl( fl );
  
        info_line = rpl.replaceFlags( m_info_line_content, false );
    }

    if ( UTF8::isValidCharacterString( info_line.c_str() ) ) {
        m_info_line_sg->setText( info_line.c_str() );
    } else {
        std::string fixed_info_line = UTF8::convertToValidCharacterString( info_line.c_str() );
        m_info_line_sg->setText( fixed_info_line.c_str() );
    }

    return 0;
}

void VirtualDirMode::insertInfoLineFlag( StringGadget *sg, bool lua_mode )
{
    if ( ! sg ) return;

    FlagReplacer::FlagHelp flags;

    if ( lua_mode ) {
        flags.registerFlag( "basename", catalog.getLocale( 589 ) );
        flags.registerFlag( "fullname", catalog.getLocale( 590 ) );
        flags.registerFlag( "filetype", catalog.getLocale( 942 ) );
        flags.registerFlag( "filesize", catalog.getLocale( 583 ) );
        flags.registerFlag( "symlink_dest", catalog.getLocale( 1055 ) );
        flags.registerFlag( "is_symlink", catalog.getLocale( 1056 ) );
        flags.registerFlag( "user", catalog.getLocale( 1060 ) );
        flags.registerFlag( "group", catalog.getLocale( 1061 ) );
    } else {
        flags.registerFlag( "{f}", catalog.getLocale( 589 ) );
        flags.registerFlag( "{F}", catalog.getLocale( 590 ) );
        flags.registerFlag( "{filetype}", catalog.getLocale( 942 ) );
        flags.registerFlag( "{filesize}", catalog.getLocale( 583 ) );
        flags.registerFlag( "{symlink_dest}", catalog.getLocale( 1055 ) );
        flags.registerFlag( "{user}", catalog.getLocale( 1060 ) );
        flags.registerFlag( "{group}", catalog.getLocale( 1061 ) );
    }

    std::string str1 = FlagReplacer::requestFlag( flags );

    if ( str1.length() > 0 ) {
        sg->insertAtCursor( str1.c_str() );
    }
}

void VirtualDirMode::setEnableInfoLine( bool nv )
{
    if ( nv == m_enable_info_line ) return;

    m_enable_info_line = nv;

    if ( m_info_line_sg ) {
        if ( m_enable_info_line ) {
            m_info_line_sg->show();
            m_cont->setMinHeight( m_sg->getHeight(), 0, 4 );
            m_cont->setMaxHeight( m_sg->getHeight(), 0, 4 );
        } else {
            m_info_line_sg->hide();
            m_cont->setMinHeight( 0, 0, 4 );
            m_cont->setMaxHeight( 0, 0, 4 );
        }

        parentawindow->updateCont();
    }
}

void VirtualDirMode::setInfoLineLUAMode( bool nv )
{
    m_info_line_lua_mode = nv;
}

void VirtualDirMode::setInfoLineContent( const std::string &content )
{
    m_info_line_content = content;
}

void VirtualDirMode::write_current_tabs( bool force )
{
    bool ask_for_storing = false;
    bool store = false;

    if ( force ) {
        store = true;
    } else if ( wconfig->getStoreTabsMode() == WConfig::STORE_TABS_ASK ) {
        int store_val = Worker::getKVPStore().getIntValue( "ask-for-store-tabs", 0 );
        
        if ( store_val == 0 ) {
            ask_for_storing = true;
        } else if ( store_val == 1 ) {
            store = true;
        }

        if ( ask_for_storing ) {
            std::string buttonstr = catalog.getLocale( 11 );
            buttonstr += "|";
            buttonstr += catalog.getLocale( 225 );

            if ( Worker::getRequester()->request( catalog.getLocale( 123 ),
                                                  catalog.getLocale( 1073 ),
                                                  buttonstr.c_str() ) == 0 ) {
                Worker::getKVPStore().setIntValue( "ask-for-store-tabs", 1 );
                store = true;
            } else {
                Worker::getKVPStore().setIntValue( "ask-for-store-tabs", 2 );
            }
        }
    } else if ( wconfig->getStoreTabsMode() == WConfig::STORE_TABS_ALWAYS ) {
        store = true;
    } else if ( wconfig->getStoreTabsMode() == WConfig::STORE_TABS_AS_EXIT_STATE &&
                wconfig->getSaveWorkerStateOnExit() ) {
        store = true;
    }

    if ( store ) {
        std::string filename, pathname;

        filename = AGUIXUtils::formatStringToString( "last_tabs_%d", parentlister->getSide() );

        pathname = Worker::getWorkerConfigDir();
        pathname = NWC::Path::join( pathname, filename );

        std::ofstream ofile( pathname.c_str() );

        ofile << m_tab_store.active_tab << std::endl;

        for ( auto &it : m_tab_store.tab_entries ) {
            if ( it.ce.get() && it.ce->isRealDir() ) {
                std::string p = it.ce->getNameOfDir();

                ofile << p << std::endl;
            }
        }
    }
}

std::pair< int, std::vector< std::string > > VirtualDirMode::read_current_tabs()
{
    std::string filename, pathname;

    filename = AGUIXUtils::formatStringToString( "last_tabs_%d", parentlister->getSide() );

    pathname = Worker::getWorkerConfigDir();
    pathname = NWC::Path::join( pathname, filename );

    std::ifstream ifile( pathname.c_str() );
    std::string line;
    int active_tab = -2;
    std::vector< std::string > paths;
    int line_no = 0;

    if ( ifile.is_open() ) {
        while ( std::getline( ifile, line ) ) {

            if ( line_no == 0 ) {
                if ( ! AGUIXUtils::convertFromString( line, active_tab ) ) {
                    active_tab = -1;
                }
            } else {
                paths.push_back( line );
            }

            line_no++;
        }
    }

    return std::make_pair( active_tab, paths );
}

void VirtualDirMode::restore_tabs()
{
    bool in_init = false;

    if ( Worker::getKVPStore().getIntValue( "init-state", 0 ) == 1 ) {
        in_init = true;
    }

    if ( in_init ) {
        bool ask_for_loading = false;
        bool load = false;

        if ( Worker::getKVPStore().getIntValue( "cla-restore-tabs", 0 ) == 1 ) {
            // command line argument to not restore tabs
            return;
        } else if ( Worker::getKVPStore().getIntValue( "cla-restore-tabs", 0 ) == 2 ) {
            // command line argument to force loading
            load = true;
        } else if ( wconfig->getRestoreTabsMode() == WConfig::RESTORE_TABS_ASK ) {
            int load_val = Worker::getKVPStore().getIntValue( "init-ask-for-load-tabs", 0 );
        
            if ( load_val == 0 ) {
                ask_for_loading = true;
            } else if ( load_val == 1 ) {
                load = true;
            }

            if ( ask_for_loading ) {
                std::string buttonstr = catalog.getLocale( 11 );
                buttonstr += "|";
                buttonstr += catalog.getLocale( 225 );

                if ( Worker::getRequester()->request( catalog.getLocale( 123 ),
                                                      catalog.getLocale( 1063 ),
                                                      buttonstr.c_str() ) == 0 ) {
                    Worker::getKVPStore().setIntValue( "init-ask-for-load-tabs", 1 );
                    load = true;
                } else {
                    Worker::getKVPStore().setIntValue( "init-ask-for-load-tabs", 2 );
                }
            }
        } else if ( wconfig->getRestoreTabsMode() == WConfig::RESTORE_TABS_ALWAYS ) {
            load = true;
        }

        if ( load ) {
            auto res = read_current_tabs();

            if ( res.first >= 0 ) {
                for ( size_t p = 0; p < res.second.size(); p++ ) {
                    showDir( res.second[p] );
                    if ( p + 1 < res.second.size() ) {
                        newTab();
                    }
                }
                switchToTab( res.first );
            }

            m_tabs_loaded = true;
        }
    }
}

bool VirtualDirMode::is_active_entry( const std::string &path )
{
    if ( ! ce.get() ) return false;

    auto es = ce->getEntry( ce->getActiveEntryPos() );

    if ( es ) {

        const NWC::FSEntry *fse = es->getNWCEntry();

        if ( fse ) {
            if ( fse->getFullname() == path ) return true;
        }
    }

    return false;
}

void VirtualDirMode::setDelayedFTAction( const std::string &path )
{
    m_delayed_filetype_action.path = path;
    m_delayed_filetype_action.pending = true;

    parentlister->getWorker()->removeStatebarText( m_delayed_filetype_action.info_text );

    m_delayed_filetype_action.info_text = TimedText( catalog.getLocale( 1074 ),
                                                     time( NULL ),
                                                     time( NULL ) + 60 );

    parentlister->getWorker()->pushStatebarText( m_delayed_filetype_action.info_text );
}

void VirtualDirMode::updateExprFilterHelp( const std::list< std::string > &list_of_expected_strings )
{
    std::string res;

    for ( auto &s : list_of_expected_strings ) {
        if ( ! res.empty() ) {
            res += ", ";
        }

        res += s;
    }

    res = catalog.getLocale( 1075 ) + res;
                
    parentlister->getWorker()->removeStatebarText( m_expr_filter_help );

    m_expr_filter_help = TimedText( res,
                                    time( NULL ) + 1,
                                    time( NULL ) + 1 + 5 );

    parentlister->getWorker()->pushStatebarText( m_expr_filter_help );
}

void VirtualDirMode::chtimef( struct chtimeorder *ctorder )
{
    if ( ! ctorder ) return;

    if ( ! ce.get() ) return;

    finishsearchmode();
  
    disableDirWatch();
  
    ChTimeCore changecore( ctorder, aguix );

    std::list< NM_specialsourceExt > ctlist;

    switch ( ctorder->source ) {
        case NM_chownorder::NM_SPECIAL:
            copySourceList( *( ctorder->sources ),
                            ctlist, false );
            break;
        case NM_chownorder::NM_ONLYACTIVE:
            getSelFiles( ctlist, LM_GETFILES_ONLYACTIVE, false,
                         ctorder->recursive ? CONSIDER_PREFIX_ELEMENTS_ONLY : CONSIDER_ALL_ELEMENTS );
            break;
        default:  // all selected entries
            getSelFiles( ctlist, LM_GETFILES_SELORACT, false,
                         ctorder->recursive ? CONSIDER_PREFIX_ELEMENTS_ONLY : CONSIDER_ALL_ELEMENTS );
            break;
    }

    for ( auto &it : ctlist ) {
        if ( it.entry() ) {
            changecore.registerFEToChange( *it.entry(), it.getID() );
        }
    }

    changecore.setPostChangeCallback( [this]( const FileEntry &fe, int id,
                                              ChTimeCore::change_t res )
                                      {
                                          if ( res == ChTimeCore::CHANGE_OK ) {
                                              auto es = ce->getEntry( id );

                                              if ( es ) {

                                                  auto fse = es->getNWCEntry();

                                                  if ( fse && fse->getFullname() == fe.fullname ) {
                                                      setEntrySelectionState( id, false );
                                                  }
                                              }
                                          }
                                      } );

    changecore.execute();

    if ( currentDirIsReal() ) {
        update();
    } else {
        updateCE();

        setCurrentCE( ce );

        m_lv->showActive();
    }

    enableDirWatch();
}

void VirtualDirMode::updateLVSortHint()
{
    int side = parentlister->getSide();
    const std::vector<WorkerTypes::listcol_t> *dis;

    if ( m_use_custom_columns ) {
        dis = &m_custom_columns;
    } else {
        dis = wconfig->getVisCols( side );
    }

    int mode = m_dir_sort_sets.getSortMode();
    bool reverse = ( mode & SORT_REVERSE ) ? true : false;

    mode &= 0xff;

    int column = 0;
    bool found = false;

    for ( const auto &ct : *dis ) {
        if ( mode == SORT_NAME &&
             ct == WorkerTypes::LISTCOL_NAME ) {
            found = true;
            break;
        } else if ( mode == SORT_SIZE &&
             ct == WorkerTypes::LISTCOL_SIZE ) {
            found = true;
            break;
        } else if ( mode == SORT_TYPE &&
             ct == WorkerTypes::LISTCOL_TYPE ) {
            found = true;
            break;
        } else if ( mode == SORT_PERMISSION &&
             ct == WorkerTypes::LISTCOL_PERM ) {
            found = true;
            break;
        } else if ( mode == SORT_OWNER &&
             ct == WorkerTypes::LISTCOL_OWNER ) {
            found = true;
            break;
        } else if ( mode == SORT_MODTIME &&
             ct == WorkerTypes::LISTCOL_MOD ) {
            found = true;
            break;
        } else if ( mode == SORT_ACCTIME &&
             ct == WorkerTypes::LISTCOL_ACC ) {
            found = true;
            break;
        } else if ( mode == SORT_CHGTIME &&
             ct == WorkerTypes::LISTCOL_CHANGE ) {
            found = true;
            break;
        } else if ( mode == SORT_INODE &&
             ct == WorkerTypes::LISTCOL_INODE ) {
            found = true;
            break;
        } else if ( mode == SORT_NLINK &&
             ct == WorkerTypes::LISTCOL_NLINK ) {
            found = true;
            break;
        } else if ( mode == SORT_EXTENSION &&
                    ct == WorkerTypes::LISTCOL_EXTENSION ) {
            found = true;
            break;
        } else if ( mode == SORT_CUSTOM_ATTRIBUTE &&
                    ct == WorkerTypes::LISTCOL_CUSTOM_ATTR ) {
            found = true;
            break;
        }

        column++;
    }

    if ( found ) {
        m_lv->setSortHint( column, reverse ? FieldListView::HEADER_SORT_HINT_UP : FieldListView::HEADER_SORT_HINT_DOWN );
    } else {
        m_lv->setSortHint( -1, FieldListView::HEADER_SORT_HINT_NONE );
    }
}

int VirtualDirMode::getNrOfInvisibleEntries()
{
    int invisible_entries = 0;

    if ( ce.get() == NULL ) return -1;

    for ( int pos = 0;
          pos < (int)ce->getSize();
          pos++ ) {
        if ( ce->getUse( pos ) ) {
            auto es = ce->getEntry( pos );

            if ( ! es ) continue;

            auto fse = es->getNWCEntry();

            if ( ! fse ) continue;

            if ( fse->getBasename() != ".." &&
                 es->getSelected() ) {
                if ( isPosVisible( pos ) == false ) {
                    invisible_entries++;
                }
            }
        }
    }

    return invisible_entries;
}

std::vector<WorkerTypes::listcol_t> VirtualDirMode::getCustomColumns() const
{
    return m_custom_columns;
}

bool VirtualDirMode::getUseCustomColumns() const
{
    return m_use_custom_columns;
}

void VirtualDirMode::setCustomColumns( const std::vector<WorkerTypes::listcol_t> &nv,
                                       bool use_custom_columns )
{
    m_use_custom_columns = use_custom_columns;
    m_custom_columns = nv;

    setupLVFields();
    rebuildView();
}

void VirtualDirMode::unsetCustomColumns()
{
    setCustomColumns( m_custom_columns, false );
}

void VirtualDirMode::updatePopUpHelp( const std::string &help )
{
    parentlister->getWorker()->removeStatebarText( m_popup_help_text );

    if ( ! help.empty() ) {
        std::string s = AGUIXUtils::formatStringToString( catalog.getLocale( 1134 ),
                                                          help.c_str() );

        m_popup_help_text = TimedText( s,
                                       time( NULL ),
                                       time( NULL ) + 60 );

        parentlister->getWorker()->pushStatebarText( m_popup_help_text );
    }
}

std::unique_ptr< DirFilterSettings > VirtualDirMode::getDirFilterSettings()
{
    return std::unique_ptr< DirFilterSettings >( new DirFilterSettings( m_dir_filter_sets ) );
}

bool VirtualDirMode::handleExternalDrop( const std::string &data )
{
    std::vector< std::string > lines;

    std::string name = AGUIXUtils::formatStringToString( "dnd_vdir%d", s_genericvdir_number++ );

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

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

    AGUIXUtils::split_string( lines, data, '\n' );

    for ( auto &l : lines ) {
        if ( AGUIXUtils::starts_with( l, "file://" ) ) {
            std::string fullname( l, 7, std::string::npos );

            AGUIXUtils::rstrip( fullname, "\n\r" );

            fullname = AGUIXUtils::unescape_uri( fullname );

            if ( AGUIXUtils::starts_with( fullname, "/" ) ) {
                selvdir->add( NWC::FSEntry( fullname ) );
            }
        }
    }

    newTab();
    showDir( selvdir );

    return true;
}

std::vector< std::string > VirtualDirMode::get_tabs()
{
    std::vector< std::string > res;

    for ( auto &it : m_tab_store.tab_entries ) {
        if ( it.ce.get() && it.ce->isRealDir() ) {
            std::string p = it.ce->getNameOfDir();

            res.push_back( p );
        }
    }

    return res;
}

int VirtualDirMode::open_tabs( const std::vector< std::string > &tabs )
{
    size_t opened_tabs = m_tab_store.tab_entries.size();

    while ( opened_tabs > tabs.size() ) {
        closeTab( 0 );
        opened_tabs--;
    }

    for ( size_t p = 0; p < tabs.size(); p++ ) {
        if ( p >= opened_tabs ) {
            newTab();
            opened_tabs++;
        }

        switchToTab( p );
        showDir( tabs[p] );
    }

    switchToTab( 0 );

    return 0;
}

void VirtualDirMode::rebuildDirectoryPresets()
{
    m_directory_presets_pt = PathTree< std::string >( "/" );

    auto presets = wconfig->getDirectoryPresets();

    for ( auto &p : presets ) {
        // use the path as key again as the PathTree can't easily
        // return the original path of the matched entry
        m_directory_presets_pt.addPath( p.first, p.first );
    }
}

void VirtualDirMode::handleDirectoryPresetsPopUp( const std::list<int> &entry_ids, AGMessage *msg )
{
    if ( entry_ids.empty() == true ) return;
    if ( msg == NULL ) return;
    if ( ! ( msg->type == AG_POPUPMENU_CLICKED ||
             msg->type == AG_POPUPMENU_ENTRYEDITED ) ) return;

    int id = entry_ids.back();

    if ( id == 0 ) {
        if ( ce.get() &&
             ce->isRealDir() ) {
            const std::string &path = ce->getNameOfDir();

            struct directory_presets preset { .sortmode = m_dir_sort_sets.getSortMode(),
                                              .show_hidden = m_dir_filter_sets.getShowHidden(),
                                              .filters = m_dir_filter_sets.getFilters() };

            std::map< std::string, struct directory_presets > presets = wconfig->getDirectoryPresets();
            presets[ path ] = preset;
            wconfig->setDirectoryPresets( presets );
            wconfig->save();
        }
    } else if ( id == 1 ) {
        // issue open command into worker
        parentlister->getWorker()->issueConfigOpen( catalog.getLocale( 1332 ) );
    }
}

void VirtualDirMode::setEnableCustomLVBLine( bool nv )
{
    m_enable_custom_lvb_line = nv;
}

void VirtualDirMode::setCustomLVBLine( const std::string &line )
{
    m_custom_lvb_line = line;
}

void VirtualDirMode::setCustomeDirectoryInfoCommand( const std::string &line )
{
    m_custom_directory_info_command = line;
}

std::string VirtualDirMode::getCustomDirectoryInfo()
{
    if ( ce.get() ) {
        return ce->getCustomDirectoryInfo();
    }

    return "";
}

void VirtualDirMode::updateCDI()
{
    if ( ! m_enable_custom_lvb_line ) {
        return;
    }

    if ( ! ce.get() ) {
        return;
    }

    if ( m_custom_directory_info_command.empty() ) {
        return;
    }

    auto fl = std::make_shared< FlagReplacer::FlagProducerMap >();
    fl->registerFlag( "p", ce->getCommonPrefix() );

    std::string scriptdir = Worker::getDataDir();
    scriptdir += "/scripts";
    fl->registerFlag( "scripts", scriptdir );

    FlagReplacer rpl( fl );
  
    std::string exestr = rpl.replaceFlags( m_custom_directory_info_command );

    std::unique_ptr< ExeClass > ec( new ExeClass() );
    ec->cdDir( NWC::Path::get_last_local_path( ce->getBasicDirname() ).c_str() );
    ec->addCommand( "%s", exestr.c_str() );

    auto f = std::async( std::launch::async,
                         [ ec = std::move( ec ) ] () -> std::string {
                             int exeerror = 1;
                             int ret = ec->getReturnCode( &exeerror );

                             if ( ret == 0 ) {
                                 std::string output;
                                 if ( ec->readOutput( output ) == 0 ) {
                                     auto newline_pos = output.find( '\n' );

                                     if ( newline_pos != std::string::npos ) {
                                         output.erase( newline_pos );
                                     }

                                     return output;
                                 }
                             }

                             return "";
                         } );

    ce->setCustomDirectoryInfoFuture( std::move( f ) );
}

void VirtualDirMode::pasteFileList( const std::tuple< int, std::string, std::vector< std::string > > &paste_data )
{
    if ( std::get<0>( paste_data ) != 0 ||
         std::get<2>( paste_data ).empty() ) {
        Worker::getRequester()->request( catalog.getLocale( 124 ),
                                         catalog.getLocale( 1462 ),
                                         catalog.getLocale( 11 ) );
        return;
    }

    std::string buttons = catalog.getLocale( 1249 );
    buttons += "|";
    buttons += catalog.getLocale( 1463 );
    buttons += "|";
    buttons += catalog.getLocale( 8 );
    int res = Worker::getRequester()->request( catalog.getLocale( 123 ),
                                               catalog.getLocale( 1464 ),
                                               buttons.c_str() );

    if ( res == 2 ) {
        return;
    } else if ( res == 1 ) {
        std::string name = AGUIXUtils::formatStringToString( "vdir%d", s_genericvdir_number++ );

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

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

        for ( const auto &f : std::get<2>( paste_data ) ) {
            selvdir->add( NWC::FSEntry( f ) );
        }

        showDir( selvdir );
        return;
    }

    // copy
    auto co = std::make_shared< struct copyorder >();
    auto destination_directory = getCurrentDirectory();

    if ( destination_directory.empty() ) {
        return;
    }

    co->source = co->COPY_SPECIAL;

    for ( const auto &f : std::get<2>( paste_data ) ) {
        FileEntry fe( f );
        co->sources.push_back( new NM_specialsourceExt( &fe ) );
    }

    if ( std::get<1>( paste_data ) == "cut" ) {
        co->move = true;
    } else {
        co->move = false;
    }

    co->destdir = destination_directory;

    co->cowin = new CopyOpWin( aguix,
                               co->move );

    co->external_sources = true;
    copy( co );
    update();
}

void VirtualDirMode::createSelectionCheckpoint()
{
    if ( ce.get() == NULL ) return;

    std::string dir = getCurrentDirectory();

    if ( dir.empty() ) return;

    if ( m_pending_selection_checkpoint ) return;

    auto data = std::make_shared< selection_checkpoint_data >();
    data->first = dir;

    for ( int pos = 0;
          pos < (int)ce->getSize();
          pos++ ) {
        auto es = ce->getEntry( pos );

        if ( ! es ) continue;

        auto fse = es->getNWCEntry();

        if ( ! fse ) continue;

        if ( fse->getBasename() != ".." &&
             es->getSelected() ) {

            data->second.push_back( fse->getFullname() );
        }
    }

    m_pending_selection_checkpoint = data;
    m_selection_checkpoint_dirty = false;
}

void VirtualDirMode::revertSelectionCheckpoint( const std::shared_ptr< selection_checkpoint_data > &data )
{
    finishsearchmode();

    if ( ce.get() == NULL ||
         getCurrentDirectory() != data->first ) {
        // find ce from cache or call showDir if it is a real dir
        bool found = false;
        
        for ( auto &tce : m_cache ) {
            if ( tce.second->getVisibleName() == data->first ) {
                setCurrentCE( tce.second );
                found = true;
                break;
            }
        }

        if ( ! found ) {
            if ( ! data->first.empty() &&
                 data->first[0] == '/' ) {
                showDir( data->first );
                found = true;
            }
        }

        if ( ! found ) {
            Worker::getRequester()->request( catalog.getLocale( 124 ),
                                             catalog.getLocale( 1473 ),
                                             catalog.getLocale( 11 ) );
            return;
        }
    }

    std::set< std::string > file_names;

    for ( const auto &f : data->second ) {
        file_names.insert( f );
    }

    for ( int pos = 0;
          pos < (int)ce->getSize();
          pos++ ) {
        auto es = ce->getEntry( pos );

        if ( ! es ) continue;

        auto fse = es->getNWCEntry();

        if ( ! fse ) continue;

        if ( fse->getBasename() == ".." ) continue;

        bool state = file_names.count( fse->getFullname() ) > 0 ? true : false;

        ce->modifyEntry( pos,
                         [state]( NWCEntrySelectionState &es ) {
                             es.setSelected( state );
                         } );

        int row = m_ce_pos_to_view_row.at( pos );
        if ( m_lv->isValidRow( row ) ) {
            m_lv->setSelect( row, state );
        }
    }

    showCacheState();
}

void VirtualDirMode::interpretFinalize()
{
    if ( ! m_pending_selection_checkpoint ) {
        return;
    }

    if ( ! m_selection_checkpoint_dirty ) {
        m_pending_selection_checkpoint.reset();
        return;
    }

    if ( m_selection_checkpoints.size() >= 10 ) {
        m_selection_checkpoints.pop_back();
    }

    m_selection_checkpoints.push_front( m_pending_selection_checkpoint );
    m_pending_selection_checkpoint.reset();
    m_selection_checkpoint_dirty = false;
}

void VirtualDirMode::setCustomAttribute( const std::string &fullname,
                                         const std::string &basename,
                                         int pos,
                                         const std::string &content )
{
    if ( ! ce.get() ) return;

    std::string filename;
    if ( ! fullname.empty() ) {
        filename = fullname;
    } else {
        auto cwd = getCurrentDirectory();
        filename = NWC::Path::join( cwd, basename );
    }

    const auto& [ es, actual_pos ] = ce->getEntry( filename, pos, false );

    if ( ! es || actual_pos < 0 ) return;

    ce->modifyEntry( actual_pos, [&content]( auto &es ) {
        es.setCustomAttribute( content );
    });

    int row = getRowForCEPos( actual_pos );

    if ( m_lv->isValidRow( row ) == true ) {
        updateLVRow( row );
        m_lv->simpleRedrawUpdatedRows();
    }

    m_lv->redraw();

    if ( ! content.empty() ) {
        if ( m_field_name_to_column.count( "LISTCOL_CUSTOM_ATTR" ) == 0 ) {
            parentlister->getWorker()->updateStatebarText( TimedText( catalog.getLocale( 1539 ),
                                                                      time( NULL ),
                                                                      time( NULL ) + 10 ),
                                                           Worker::UPDATE_ON_TIME_CHANGE);
        }
    }

    return;
}

int VirtualDirMode::processCustomAttribute( ActionMessage *am,
                                            process_custom_attribute_settings sets,
                                            const FileEntry *primary_fe,
                                            int primary_fe_id )
{
    if ( ! am ||
         ! am->getWorker() ||
         ! am->getWorker()->getCurrentWPU() ) {
        return -EINVAL;
    }

    std::list< NM_specialsourceExt > filelist;

    if ( am->mode == am->AM_MODE_SPECIAL ) {
        if ( am->getFE() ) {
            filelist.push_back( NM_specialsourceExt( am->getFE(), -1 ) );
        }
    } else {
        getSelFiles( filelist, ListerMode::LM_GETFILES_SELORACT );

        if ( primary_fe ) {
            filelist.insert( filelist.begin(),
                             NM_specialsourceExt( primary_fe, primary_fe_id ) );
        }
    }

    for ( const auto &se : filelist ) {
        if ( ! se.entry() ) continue;

        if ( se.entry() != filelist.begin()->entry() &&
             strcmp( se.entry()->fullname,
                     filelist.begin()->entry()->fullname ) == 0 ) {
            continue;
        }

        RefCount< NWC::VirtualDir > vd( new NWC::VirtualDir( "context", false, false, false ) );

        vd->add( NWC::FSEntry( se.entry()->fullname ) );

        if ( ! vd->empty() ) {
            ActionMessage am2( am->getWorker() );
            am2.setEntriesToConsider( vd );

            // use a separate instance to be able to control the files
            // considered, but share the WPUStack
            WPUContext wpu2( *am->getWorker()->getCurrentWPU(),
                             &am2 );

            std::unique_ptr< ExeClass > ec( new ExeClass() );

            std::string parsed_str;
            auto res = wpu2.parse( sets.command_str.c_str(),
                                   parsed_str,
                                   a_max( EXE_STRING_LEN - 1024, 256 ),
                                   true,
                                   WPUContext::PERSIST_NOTHING );

            if ( res != WPUContext::PARSE_SUCCESS ||
                 parsed_str.empty() ) {
                continue;
            }

            if ( ! sets.dont_cd ) {
                ec->cdDir( wpu2.getBaseDir() );
            }

            if ( sets.global_command ) {
                ec->addCommand( "%s", parsed_str.c_str() );
            } else {
                ec->addCommand( "./%s", parsed_str.c_str() );
            }

            int ret_error;
            am->getWorker()->setWaitCursor();
            char *tstr2 = ec->getOutput( &ret_error );
            am->getWorker()->unsetWaitCursor();
            if ( ret_error == 0 && tstr2 != NULL ) {
                setCustomAttribute( se.entry()->fullname,
                                    "",
                                    se.getID(),
                                    tstr2 );
            }

            _freesafe( tstr2 );

            if ( ! wpu2.filelistModified( false, true ) ) {
                break;
            }

            if ( ! sets.separate_each_entry ) {
                break;
            }
        }
    }

    return 0;
}

std::unique_ptr< NWC::Dir > VirtualDirMode::get_selected_entries_as_vdir( ListerMode::lm_getfiles_t sel_mode )
{
    std::string name = AGUIXUtils::formatStringToString( "sel_vdir%d", s_selvdir_number++ );

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

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

    std::list< NM_specialsourceExt > filelist;

    getSelFiles( filelist, sel_mode );

    for ( auto &sse : filelist ) {
        const FileEntry *tfe = sse.entry();

        if ( tfe ) {
            selvdir->add( NWC::FSEntry( tfe->fullname ) );
        }
    }

    return selvdir;
}
