/* virtualdirmode.hh
 * 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
 */

#ifndef VIRTUALDIRMODE_HH
#define VIRTUALDIRMODE_HH

#include "wdefines.h"
#include "listermode.h"
#include "nwc_dir.hh"
#include "aguix/refcount.hh"
#include "aguix/popupmenu.hh"
#include "dirfiltersettings.hh"
#include "dirsortsettings.hh"
#include "dirbookmarkssettings.hh"
#include "dmcacheentrynwc.hh"
#include "verzeichnis.hh"
#include "nm_filetype_thread.hh"
#include "stringmatcher_fnmatch.hh"
#include "functionproto.h"
#include "stringmatcher_flexiblematch.hh"
#include "stringmatcher_flexibleregex.hh"
#include "copybgmessage.hh"
#include <mutex>
#include "luascripting.hh"
#include "timedtext.hh"
#include "pathtree.hh"
#include "wconfig_types.hh"
#include "flagreplacer.hh"

class Lister;
class AContainer;
class FieldListViewDND;
class Text;
class StringGadget;
class Button;
class KarteiButton;
class ActionMessage;
class CopyState;

#define DEFAULT_INFO_LINE_CONTENT_LUA "if ( is_symlink ) then return basename .. \" -> \" .. symlink_dest .. \": \" .. filetype; else return basename .. \": \" .. filetype .. \" (\" .. filesize .. \" bytes)\"; end"
#define DEFAULT_INFO_LINE_CONTENT_NOLUA "{f} {symlink_dest} ({filesize} bytes)"

class VirtualDirMode : public ListerMode
{
public:
    VirtualDirMode( Lister * );
    virtual ~VirtualDirMode();
    VirtualDirMode( const VirtualDirMode &other );
    VirtualDirMode &operator=( const VirtualDirMode &other );

    void messageHandler( AGMessage * );
    void messageHandlerInactive( AGMessage * );
    void on();
    void off();
    void activate();
    void deactivate();
    bool isType( const char * );
    const char *getType();
    int configure();
    void cyclicfunc( cyclicfunc_mode_t mode );
    void cyclicfuncInactive( cyclicfunc_mode_t mode );
    const char* getLocaleName();
    int load();
    bool save( Datei * );
    void relayout();
  
    static const char *getStaticLocaleName();
    static const char *getStaticType();

    void command_up();
    void command_up( bool keep_search_mode, bool wrap_around );
    void command_down();
    void command_down( bool keep_search_mode, bool wrap_around );
    void command_first();
    void command_last();
    void command_pageup();
    void command_pagedown();
    void command_selectentry();
    void command_selectentry(  const std::list< RefCount< ArgClass > > &args );
    void command_pathinput();
    void command_parentdir( bool force );
    void enter_dir( const std::list< RefCount< ArgClass > > &args );
    void enter_active();
    void activate_entry( const std::list< RefCount< ArgClass > > &args );
    void command_simdd();
    void command_selectall();
    void command_selectnone();
    void command_invertall();
    void show_cache_entry( const std::list< RefCount< ArgClass > > &args );
    void switch_to_tab_direction( const std::list< RefCount< ArgClass > > &args );
    void move_tab_direction( const std::list< RefCount< ArgClass > > &args );
    void flatten_dir( bool follow_symlinks );
    void vdir_from_selected();
    void vdir_add_selected_from_other_side();
    void vdir_from_script_stack( const std::list< RefCount< ArgClass > > &args );
    void vdir_add_entry( const std::list< RefCount< ArgClass > > &args );
    void open_current_tab_menu();
    void show_entry( const std::list< RefCount< ArgClass > > &args );
    void copy_files_to_clipboard( const std::list< RefCount< ArgClass > > &args );
    void copy_filecontent_to_clipboard();
    void paste_files_from_clipboard();
    void revert_selection( const std::list< RefCount< ArgClass > > &args );
    void set_custom_attribute( const std::list< RefCount< ArgClass > > &args );
    void clear_custom_attributes();

    void newTab();

    enum showdir_store_type {
                             SHOWDIR_PERS_STORE_PATH,
                             SHOWDIR_DONT_PERS_STORE_PATH
    };

    int showDir( const NWC::Dir &dir_list );
    int showDir( std::unique_ptr< NWC::Dir > &dir_list );
    int showDir( const std::string &path, showdir_store_type store_type = SHOWDIR_PERS_STORE_PATH );

    int getSelFiles( std::list< NM_specialsourceExt > &return_list,
                     lm_getfiles_t get_mode,
                     bool unselect = false,
                     int options = 0 ) override;

    std::string getCurrentDirectory();

    bool setEntrySelectionState( const std::string &filename,
                                 bool state );

    bool isyours( Widget *elem );

    void setSortmode( int nmode );

    void setFlexibleMatchingMode( bool nv );
    bool getFlexibleMatchingMode() const;
    bool filteredSearchActive() const;
    void deactivateFilteredSearch();

    void searchentry( bool ignore_case, bool reverse_search, bool infix_search,
                      const std::string &initial_search_value,
                      bool apply_only );

    void updateOnBookmarkChange();

    std::list< std::string > getNameOfCacheEntries();

    void lvbDoubleClicked();

    bool pathsChanged( const std::set< std::string > changed_paths );

    void enableDirWatch();
    void disableDirWatch( bool clear_path = false );

    void update( bool reset_dirsizes = false,
                 bool keep_filetypes = true );

    void bookmarksChanged();

    void openTabPopUp( int tab );

    class context_menu_settings
    {
    public:
        context_menu_settings() : m_highlight_user_action( false ) {}

        void setHighlightUserAction( bool nv )
        {
            m_highlight_user_action = nv;
        }

        bool getHighlightUserAction() const
        {
            return m_highlight_user_action;
        }
    private:
        bool m_highlight_user_action;
    };
    
    void openContextMenu( const context_menu_settings &sets = context_menu_settings() );

    bool startdnd( DNDMsg *dm );

    void copy( std::shared_ptr< struct copyorder > co );
    void deletef( struct deleteorder *delorder );

    bool currentDirIsReal() const;

    void toggleHidden();
    void setShowHidden( bool nhf );

    void scrollListView( int dir );
    void scrollVertToAbs( int pos );
    void scrollHorizToAbs( int pos );

    void selectByFilter( const std::string &filter, bool select );

    void make_dir();

    void renamef( struct NM_renameorder *renorder );
    void changesymlink( struct NM_changesymlinkorder *chslorder );
    void createsymlink( struct NM_createsymlinkorder *cslorder );

    int addEntries( const std::list< std::string > &entries );

    void dirsize( struct NM_dirsizeorder *dsorder );

    void chmodf( struct NM_chmodorder *cmorder );
    void chownf( struct NM_chownorder *coorder );
    void chtimef( struct chtimeorder *ctorder );

    typedef enum { FILTER_INCLUDE, FILTER_EXCLUDE, FILTER_UNSET } vdm_filter_t;
    void unsetAllFilters();
    void setFilter( const std::string &filter, vdm_filter_t mode );
    void setFilters( const std::list<NM_Filter> &filters );
    vdm_filter_t checkFilter( const std::string &filter );

    void setBookmarkFilter( DirFilterSettings::bookmark_filter_t v );
    DirFilterSettings::bookmark_filter_t getBookmarkFilter() const;

    void setSpecificBookmarkLabel( const std::string &l );
    const std::string &getSpecificBookmarkLabel() const;

    int queryLVDimensions( int &x, int &y,
                           int &w, int &h );

    std::unique_ptr< NWC::Dir > getCurrentDir();

    void finalizeBGOps();

    std::vector<WorkerTypes::listcol_t> getCustomColumns() const;
    bool getUseCustomColumns() const;
    void setCustomColumns( const std::vector<WorkerTypes::listcol_t> &nv,
                           bool use_custom_columns = true );
    void unsetCustomColumns();

    std::unique_ptr< DirFilterSettings > getDirFilterSettings();

    std::vector< std::string > get_tabs();
    int open_tabs( const std::vector< std::string > &tabs );

    std::list< NM_Filter > getFilters() const;

    void interpretFinalize() override;

    /**
     * either fullname or basename must be non-empty, fullname wins
     * over basename
     */
    void setCustomAttribute( const std::string &fullname,
                             const std::string &basename,
                             int pos,
                             const std::string &content );

    struct process_custom_attribute_settings {
        std::string command_str;
        bool dont_cd = false;
        bool global_command = true;
        bool separate_each_entry = false;
    };
    
    int processCustomAttribute( ActionMessage *am,
                                process_custom_attribute_settings sets,
                                const FileEntry *primary_fe,
                                int primary_fe_id );

    std::unique_ptr< NWC::Dir > get_selected_entries_as_vdir( ListerMode::lm_getfiles_t sel_mode = ListerMode::LM_GETFILES_SELORACT );
private:
    static const char *type;
    void setName();

    void setupLVFields();
    void updateLV();
    void updateLVRow( int row );
    void updateLVSortHint();
    void lv_select_handler( FieldListView *lv, int element );
    void rebuildView();
    void updateTabs( bool update_all = false );
    void setTabTitle( int pos );
    void closeCurrentTab();
    void closeTab( int tab );
    void switchToTab( int tab );
    void switchToTabDirection( int direction );
    void lockTab( int tab );
    void unlockTab( int tab );
    void toggleLockTab( int tab );
    void toggleLockCurrentTab();
    void moveTabDirection( int direction );
    void updateCE();
    void showCacheState();
    void updateName();
    void cleanupCache();
    void setCurrentCE( RefCount< DMCacheEntryNWC > new_ce );

    void startAction( const NWCEntrySelectionState &es );
    int prepareContext( ActionMessage &amsg );

    int getRowForCEPos( int pos,
                        bool dont_check_lv_data = false );
    bool isPosVisible( int pos );

    bool setEntrySelectionState( int pos, bool state );

    bool activateEntry( const std::string &filename,
                        bool only_compare_basename = false );

    void updateColor( int row );

    void ft_request_list_clear();
    void ft_result_list_clear();
    void ft_list_clear();
    void ft_list_update();

    void updateFiletypeCheck();
    void gatherFiletypeResults();
    void fillFiletypeRequests();
    void insert_CE_element_into_FT_request( int ce_pos );
    void addActiveRowForFTCheck();

    int findPosition( int pos, const std::string &fullname );

    void changeSortModeForField( int field );

    void startsearchmode( bool use_previous_search,
                          const std::string &search_value );
    void finishsearchmode();
    bool shownextentry( const char *tstr, bool reverse_search = false, bool start_at_same = false );

    // this class contains the filter string, but hides it
    // to emphasize on different meanings of this string
    // (plain file name filter, expression filter)
    class filter_search_string
    {
    public:
        filter_search_string() = default;

        filter_search_string( const std::string &filter ) : m_filter( filter )
        {}

        const std::string &getFilterString() const
        {
            return m_filter;
        }

        void setFilterString( const std::string &filter )
        {
            m_filter = filter;
        }

        bool isExprFilter() const
        {
            return AGUIXUtils::starts_with( m_filter, "(" );
        }
    private:
        std::string m_filter;
    };
    
    void setEntrySearchString( const filter_search_string &filter, bool update_lv = true );
    filter_search_string getEntrySearchString();
    void updateDirFilter();
    void setFilteredSearchEnabled( bool nv );
    void resetToLastSearchString();
    std::string getFilterStringFromSG();
    void showSearchModeCompletion( const std::string &str );
    void makeRowActive( int row );
    void changeCacheEntry( int direction );

    int getStoredFieldWidth( const std::string &str1 );
    int storeFieldWidths();

    void openLabelPopUp();

    void enableDirWatch( const std::string &path );
    void setWatchMode( bool nv );

    void showFreeSpace( bool force );
    int updateFreeSpaceStr( int ti );
    void setShowFreeSpace( bool v );
    void setUpdatetime( int nv );
    void updateSpaceTimeout( loff_t time_ms );

    std::list<PopUpMenu::PopUpEntry> buildTabPopUpData( int tab );
    void handleTabPopUp( AGMessage *msg );
    std::list<PopUpMenu::PopUpEntry> buildLabelPopUpData( const NWCEntrySelectionState &es );
    std::list<PopUpMenu::PopUpEntry> buildFilterPopUpData( const NWCEntrySelectionState *es );
    void handleLabelPopUp( AGMessage *msg );
    void handleLabelPopUp( const NWCEntrySelectionState &es, const std::list<int> &entry_ids, AGMessage *msg );
    void handleFilterPopUp( const std::list<int> &entry_ids, AGMessage *msg );
    void handleDirectoryPresetsPopUp( const std::list<int> &entry_ids, AGMessage *msg );

    void lv_pressed( int row, bool open_under_mouse = true );
    void buildContextPopUpMenu( const NWCEntrySelectionState *es );
    void buildContextPopUpMenuFilterAndPreset( std::list<PopUpMenu::PopUpEntry> &menulist,
                                               int &descr_id,
                                               const NWCEntrySelectionState *es );
    void buildLabelPopUpMenu( const NWCEntrySelectionState &es );
    void freePopUpMenus();
    void startLVPopUpAction( AGMessage *msg );

    int copySourceList( const std::list< NM_specialsourceExt * > &inlist,
                        std::list< NM_specialsourceExt > &outlist,
                        const bool external_sources );

    typedef enum {
        CONSIDER_PREFIX_ELEMENTS_ONLY,
        CONSIDER_ALL_ELEMENTS
    } vdm_get_sel_mode_t;

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

    int getDirSize( const std::string &fullname,
                    loff_t &return_size,
                    bool &cancel );

    void setLVC4Filter( FieldListView *filv, int row, NM_Filter &fi );
    int configureFilter( NM_Filter &fi );

    void setHighlightBookmarkPrefix( bool nv );
    bool getHighlightBookmarkPrefix() const;
    void recenterTopBottom();

    void makeListViewRowActive( int row );
    void setActivateSearchModeOnKeyPress( bool nv );
    void setShowDotDot( bool nv );
    void setShowBreadcrumb( bool nv );

    RefCount< DMCacheEntryNWC > findCacheEntry( const std::string &name,
                                                const std::string &common_prefix,
                                                bool backward_match );
    std::unique_ptr< NWC::Dir > createSubVDir( RefCount< DMCacheEntryNWC > new_ce,
                                               const std::string &common_prefix );

    void handle_bg_copy_ops( bool skip_message_handling = true );
    void handle_bg_copy_ops( AGMessage *msg );
    void finalizeCopy( std::shared_ptr< CopyState > cs );
    void process_bg_messages();
    void wait_for_bg_copies();
    bool check_for_bg_copy_collision( const std::string &fullname,
                                      std::shared_ptr< CopyState > cs );

    void go_to_previous_dir();

    bool checkBreadcrumb( AGMessage *msg );
    std::string mergeBreadcrumbComponents( std::size_t pos );

    void pasteFileList( const std::tuple< int, std::string, std::vector< std::string > > &paste_data );

    FieldListViewDND *m_lv;
    StringGadget *m_sg;

    AContainer *m_cont, *m_cont2, *m_tab_cont;
    Button *m_parent_b;
    Button *m_cache_select_b[2];
    KarteiButton *m_tab_b;
    Button *m_tab_new, *m_tab_close;
    StringGadget *m_info_line_sg;

    RefCount< DMCacheEntryNWC > ce;
    RefCount< TextShrinker > m_shrinker;

    DirFilterSettings m_dir_filter_sets;
    DirSortSettings m_dir_sort_sets;
    DirBookmarksSettings m_dir_bookmarks_sets;

    std::string m_common_prefix;
    std::string m_visible_dirname;

    bool m_bookmarks_has_been_changed;

    std::list< std::pair< std::string, RefCount< DMCacheEntryNWC > > > m_cache;

    struct tab_info {
        RefCount< DMCacheEntryNWC > ce;
        std::string path;
        bool locked;
    };
    
    struct tab_store {
        tab_store() : active_tab( -1 )
        {}
        std::vector< struct tab_info > tab_entries;
        int active_tab;
    } m_tab_store;

    static int maxfilestrlen, maxdirstrlen, maxbytesstrlen, maxwbytesstrlen, maxnochanges;
    static int maxhiddenfilesstrlen, maxhiddendirsstrlen;

    std::string m_lvb_indicators, m_lvb_modeinfo;

    int m_max_cache_size;

    enum ssh_allow_t { SSH_ASK, SSH_NEVER, SSH_ALWAYS } ssh_allow;

    struct deselect_info {
        deselect_info() : last_pos( -1 ) {}
        int last_pos;
    } m_deselect_info;

    std::vector< int > m_ce_pos_to_view_row;

    class ft_request_list
    {
    public:
        struct ft_request_list_entry {
            std::string fullname;
            int pos;
        };

        ft_request_list();
        ~ft_request_list();
        ft_request_list( const ft_request_list &other );
        ft_request_list &operator=( const ft_request_list &other );

        bool isFull();
        bool isEmpty();
        int put( const ft_request_list_entry &elem );
        ft_request_list_entry remove();
        ft_request_list_entry gettop();

        bool contains( const std::string &fullname );
    private:
        std::list< ft_request_list_entry > m_elements;
        std::set< std::string > m_elements_set;
        size_t m_max_elements;
    };

    ft_request_list m_request_list;
    NM_Filetype_Thread m_ft_thread;

    bool m_vis_changed;
    int m_old_lv_yoffset;
    int m_busy_flag;
    time_t m_last_busy_flag_update;

    struct filtered_search_state
    {
        filtered_search_state() : flexible_matching_enabled( true ),
                                  searchmode_on( false ),
                                  flexible_matching_active( false ),
                                  infix_search( false ),
                                  non_empty_filter_active( false ),
                                  m_case_sensitive( false ),
                                  m_initial_call( false ),
                                  m_contains_type_expression( false )
        {}

        bool flexible_matching_enabled;
        std::string old_sg_content;
        std::string last_search;
        bool searchmode_on;
        bool flexible_matching_active;
        bool infix_search;

        std::string last_completion;
        std::string cached_previous_match_string;
        std::string cached_cleaned_match_string;
        bool non_empty_filter_active;

        void setFilter( const filter_search_string &filter )
        {
            m_filter = filter;
        }

        filter_search_string getFilter() const
        {
            return m_filter;
        }

        void setCaseSensitive( bool nv )
        {
            m_case_sensitive = nv;
        }

        bool getCaseSensitive() const
        {
            return m_case_sensitive;
        }

        void setLastValidExprFilter( const std::string &v )
        {
            m_last_valid_expr_filter = v;
        }

        std::string getLastValidExprFilter() const
        {
            return m_last_valid_expr_filter;
        }

        void setInitialCall( bool nv )
        {
            m_initial_call = nv;
        }

        bool getInitialCall() const
        {
            return m_initial_call;
        }

        void storeSearchFilterForNextActivation( const std::string &s )
        {
            m_search_filter_from_previous_search_mode = s;
        }

        std::string getSearchFilterFromPreviousActivation() const
        {
            return m_search_filter_from_previous_search_mode;
        }

        void setContainsTypeExpression( bool nv )
        {
            m_contains_type_expression = nv;
        }

        bool getContainsTypeExpression() const
        {
            return m_contains_type_expression;
        }
    private:
        filter_search_string m_filter;
        bool m_case_sensitive;
        std::string m_last_valid_expr_filter;
        bool m_initial_call;
        std::string m_search_filter_from_previous_search_mode;
        bool m_contains_type_expression;
    } m_filtered_search_state;

    std::map< std::string, int > m_field_name_to_column;
    std::map< std::string, int > m_field_name_to_width;

    struct dirwatcher {
        dirwatcher() : path_watch_enabled( false ),
                       last_update( 0 ),
                       timer_enabled( false ),
                       watch_mode( true ) {}

        std::string watched_path;
        bool path_watch_enabled;

        time_t last_update;
        bool timer_enabled;

        bool watch_mode;
    } m_dirwatcher;

    bool m_show_free_space;
    int m_update_time;
    time_t m_last_fs_update;
    std::string m_free_space_str;
    bool m_last_eagain;
    loff_t m_current_space_update_ms;

    class VDMBookmarkCB *m_bookmark_cb;

    typedef struct _label_popup_table_t {
        typedef enum { REMOVE_LABEL, ADD_LABEL, ADD_CUSTOM_LABEL } label_popup_t;

        _label_popup_table_t() : type( REMOVE_LABEL ),
                                 label( "" )
        {}
        _label_popup_table_t( label_popup_t t, const std::string &l ) : type( t ),
                                                                        label( l )
        {}

        label_popup_t type;
        std::string label;
    } label_popup_table_t;

    typedef struct _tab_popup_table_t {
        typedef enum { NEW_TAB,
                       DEL_TAB,
                       TAB_TO_OTHER_SIDE,
                       TAB_TO_OTHER_SIDE_AS_NEW,
                       LOCK_TAB,
                       UNLOCK_TAB,
                       MOVE_TAB_LEFT,
                       MOVE_TAB_RIGHT
        } tab_popup_t;

        _tab_popup_table_t() : type( NEW_TAB )
        {}
        _tab_popup_table_t( tab_popup_t t ) : type( t )
        {}

        tab_popup_t type;
    } tab_popup_table_t;

    typedef struct _filter_popup_table_t {
        typedef enum { FILTER_ADD, FILTER_REMOVE, FILTER_REMOVE_ALL,
            ADD_CUSTOM_FILTER,
            STASH_CURRENT_FILTERS,
            APPLY_STASH,
            APPLY_AND_REMOVE_STASH,
            REMOVE_STASH
        } filter_popup_t;

        _filter_popup_table_t() : type( FILTER_REMOVE_ALL )
        {}
        _filter_popup_table_t( filter_popup_t t,
                               const std::string &filter,
                               size_t stash_pos = 0 ) :
            type( t ),
            filter( filter ),
            stash_pos( stash_pos )
        {}

        filter_popup_t type;
        std::string filter;
        size_t stash_pos = 0;
    } filter_popup_table_t;

    class PopUpSettings {
    public:
        PopUpSettings();
        PopUpSettings( const PopUpSettings &other );
        PopUpSettings &operator=( const PopUpSettings &rhs );

        void reset();

        int label_menu_id = -1;
        int filter_menu_id = -1;
        int presets_menu_id = -1;
        int filetype_entry_id = -1;
        int copy_entry_id = -1;
        int copy_selected_id = -1;
        int cut_entry_id = -1;
        int cut_selected_id = -1;
        int paste_id = -1;
        std::map<int, label_popup_table_t> label_id_to_action;
        std::map<int, tab_popup_table_t> tab_id_to_action;
        std::map< int, filter_popup_table_t > filter_id_to_action;
        std::unique_ptr<PopUpMenu> label_popup_menu;
        std::unique_ptr<PopUpMenu> tab_popup_menu;

        std::map<int, RefCount<ActionDescr> > popup_descr;
        std::string entry_for_popup;
        PopUpMenu *lv_popup_menu;
        int m_tab;
        std::map< int, bool > entry_is_action_submenu;

        bool label_entry_clicked;
    };

    PopUpSettings m_current_popup_settings;

    struct recenter_state {
        recenter_state() : last_state( 0 ),
                           last_yoffset( -1 ),
                           last_active_row( - 1 ) {}

        int last_state; // 0 == center, 1 == top, 2 == bottom
        int last_yoffset, last_active_row;
    } m_recenter_state;

    bool m_lv_ce_valid;
    bool m_apply_matcher;
    StringMatcherFlexibleMatch m_flexible_matcher;
    StringMatcherFlexibleRegEx m_nonflexible_matcher;

    bool m_activate_search_on_keypress;

    static int s_vdir_refine_number;
    static int s_flatdir_number;
    static int s_selvdir_number;
    static int s_genericvdir_number;

    struct bg_copy_status {
        bg_copy_status() : last_vismark_row( -1 ),
                           timeout_enabled( false )
        {}

        std::list< std::shared_ptr< CopyState > > active_copy_ops;
        std::list< CopyOpWin * > finished_but_unclosed_wins;

        struct {
            std::mutex mutex;
            std::list< CopyBGMessage > messages;
            std::list< std::tuple< std::string, std::string, bool > > pers_path_store_relocates;
        } post_copy_cb_replies;

        int last_vismark_row;
        bool timeout_enabled;
    } m_bg_copy_status;

    bool m_path_entry_on_top;

    int m_previous_dir_pos;
    bool m_go_to_previous_dir_active;

    NWC::FSEntry m_dummy_dotdot_fse;

    bool m_show_dotdot;

    struct breadcrumb_navigation {
        breadcrumb_navigation() : current_co( NULL ),
                                  current_path_component( 0 ),
                                  enabled( true )
        {}

        AContainer *current_co;
        std::vector< Button * > buttons;
        std::vector< std::string > current_path_components;
        std::size_t current_path_component;

        bool enabled;
    } m_breadcrumb;

    LUAScripting m_lua_instance;

    bool m_enable_info_line;
    std::string m_info_line_content;
    bool m_info_line_lua_mode;
    const NWC::FSEntry *m_last_info_line_fse;
    const WCFiletype *m_last_info_line_ft = NULL;

    bool m_enable_custom_lvb_line;
    std::string m_custom_lvb_line;
    std::string m_custom_directory_info_command;

    static int build_path_components( const std::string &path,
                                      std::vector< std::string > &path_components );

    int update_breadcrumb( const std::string &path, bool skip_gui_update = false );

    int update_breadcrumb_gui();

    int update_info_line();

    void setEnableInfoLine( bool nv );
    void setInfoLineLUAMode( bool nv );
    void setInfoLineContent( const std::string &content );
    void insertInfoLineFlag( StringGadget *sg, bool lua_mode );

    void write_current_tabs( bool force = false );
    std::pair< int, std::vector< std::string > > read_current_tabs();
    void restore_tabs();

    void setEnableCustomLVBLine( bool nv );
    void setCustomLVBLine( const std::string &line );
    void setCustomeDirectoryInfoCommand( const std::string &line );

    void showListOfIncompleteEntries( const std::vector< std::string > &entries );

    bool m_tabs_loaded;

    TimedText m_expr_filter_help;

    struct delayed_filetype_action {
        delayed_filetype_action() : pending( false ) {}
        bool pending;
        std::string path;
        std::function< void() > cb;
        TimedText info_text;
    } m_delayed_filetype_action;

    int m_last_no_of_unchecked_entries;

    context_menu_settings m_context_menu_sets;

    bool is_active_entry( const std::string &path );
    void setDelayedFTAction( const std::string &path );

    void updateExprFilterHelp( const std::list< std::string > &list_of_expected_strings );

    int request_symlink_dest( const std::string &fullname,
                              const std::string orig_symlink_dest,
                              std::string &return_dest );

    int getNrOfInvisibleEntries();
    void updatePopUpHelp( const std::string &help );

    std::list< std::string > getListOfMatchingFilters( const NWCEntrySelectionState &es );
    bool handleExternalDrop( const std::string &data );

    typedef std::pair< std::string, std::vector< std::string > > selection_checkpoint_data;

    void createSelectionCheckpoint();
    void revertSelectionCheckpoint( const std::shared_ptr< selection_checkpoint_data > &data );

    bool m_use_custom_columns;
    std::vector<WorkerTypes::listcol_t> m_custom_columns;

    TimedText m_popup_help_text;

    PathTree< std::string > m_directory_presets_pt;

    struct directory_presets m_last_explicit_directory_presets;

    void checkDirectoryPresets( RefCount< DMCacheEntryNWC > &new_ce,
                                RefCount< DMCacheEntryNWC > &old_ce,
                                bool &update_directory_settings,
                                struct directory_presets &new_settings );
    void rebuildDirectoryPresets();
    void updateLastDirSettings( const struct directory_presets &old_settings );

    std::string getCustomDirectoryInfo();
    void updateCDI();

    void revert_selection_from_menu();

    void resetState();

    std::shared_ptr< FlagReplacer::FlagProducerCallback > m_lvb_flag_producer;
    std::shared_ptr< FlagReplacer > m_lvb_flag_replacer;

    bool m_search_mode_hidden_enabled = false;
    bool m_search_mode_restore_hidden = false;

    std::list< std::shared_ptr< selection_checkpoint_data > > m_selection_checkpoints;
    std::shared_ptr< selection_checkpoint_data > m_pending_selection_checkpoint;
    bool m_selection_checkpoint_dirty = false;

    bool m_expression_filter_interrupted = false;
};

#endif
