/* worker.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2001-2025 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 "worker.h"
#include "lister.h"
#include "aguix/util.h"
#include "aguix/lowlevelfunc.h"
#include "view_command_log_op.hh"
#include "worker_locale.h"
#include "wconfig.h"
#include "wcdoubleshortkey.hh"
#include "wcpath.hh"
#include "filereq.h"
#include "grouphash.h"
#include "wdefines.h"
#include "showimagemode.h"
#include "informationmode.h"
#include <memory>
#include <string>
#include <list>
#include "wpucontext.h"

#ifdef HAVE_SIGACTION
#include <signal.h>
#endif

#include "execlass.h"
#include "configtokens.h"
#include "configheader.h"
#include "configparser.hh"
#include "parentactionop.h"
#include "aguix/textstorage.h"
#include "wchotkey.hh"
#include "wcbutton.hh"
#include "aguix/utf8.hh"
#include "workerversion.h"
#include "nwc_path.hh"
#include <iostream>
#include "aguix/popupmenu.hh"
#include "magic_db.hh"
#include "bookmarkdb.hh"
#include "bookmarkdbproxy.hh"
#include <algorithm>
#include "nwc_os.hh"
#include "hw_volume.hh"
#include "nwc_fsentry.hh"
#include "volumemanagerop.hh"
#include "volumemanagerui.hh"
#include "waitthread.hh"
#include "eventqueue.hh"
#include "aguix/timeoutstore.hh"
#include "workerinitialsettings.hh"
#include "about.hh"
#include <sys/select.h>
#include "dnd.h"
#include "partspace.h"
#include "aguix/button.h"
#include "aguix/fieldlistview.h"
#include "aguix/slider.h"
#include "simplelist.hh"
#include "datei.h"
#include "stringcomparator.hh"
#include "persdeeppathstore.hh"
#include "pers_string_list.hh"
#include "hintdb.hh"
#include "pathname_watcher.hh"
#include "virtualdirmode.hh"
#include "argclass.hh"
#include "menutreeui.hh"
#include "menutree.hh"
#include "commandmenuop.hh"
#include "pers_kvp.hh"
#include "prefixdb.hh"
#include "wcfiletype.hh"
#include <fstream>
#include "aguix/icons.hh"
#include "aguix/textview.h"
#include "helpop.hh"
#include "worker_net_wm_icon.h"
#include <time.h>
#include "filterstash.hh"
#include "error_count.hh"

#ifdef HAVE_GETOPT_LONG
#  ifndef _GNU_SOURCE
#    define _GNU_SOURCE
#    include <getopt.h>
#    undef _GNU_SOURCE
#  else
#    include <getopt.h>
#  endif
#endif

#define DBUS_POLL_TIME 5
#define HINT_UPDATE_TIME 60
#define HINT_VALID_TIME 30

Requester *Worker::req_static=NULL;
int Worker::argc = 0;
char **Worker::argv = NULL;
int Worker::m_optind = -1;
std::string Worker::startcwd = "/";
AGUIX *Worker::aguix = NULL;
int Worker::workerInstances = 0;
mode_t Worker::m_process_umask = 0077;

std::unique_ptr<BookmarkDBProxy> Worker::m_bookmarkproxy;
std::unique_ptr< PersistentKVP > Worker::m_pers_kvp_store;
KVPStore Worker::m_kvp_store;

std::unique_ptr< FileCommandLog > Worker::m_command_log;
std::mutex Worker::m_command_log_mutex;

short usr1signal = 0;

#ifdef HAVE_SIGACTION
static int sigchld_pipe[2] = { -1, -1 };

static void worker_sighandler( int s )
{
    if ( s == SIGUSR1 ) {
        usr1signal = 1;
    } else if ( s == SIGCHLD ) {
        char buf[1] = { '\0' };
        int tres __attribute__((unused)) = write( sigchld_pipe[1], &buf[0], 1 );
    } else if ( s == SIGALRM ) {
        char buf[1] = { '\1' };
        int tres __attribute__((unused)) = write( sigchld_pipe[1], &buf[0], 1 );
    }
}
#endif

Worker::Worker( int targc, char **targv, int toptind )
{
  workerInstances++;

  if ( workerInstances == 1 ) {
      m_process_umask = umask( 0 );
      umask( m_process_umask );
  }

  m_timeouts = std::make_shared< TimeoutStore >();

  if ( aguix == NULL ) {
    aguix = new AGUIX( targc, targv, "Worker" );

    if ( NWC::FSEntry( getOverrideXIMPath() ).entryExists() ) {
        aguix->setOverrideXIM( true );
    }

    aguix->initX();

    aguix->setExternalTimeoutStore( m_timeouts );

    aguix->setStandardIconData( worker_net_wm_icon_data,
                                sizeof( worker_net_wm_icon_data ) / sizeof( worker_net_wm_icon_data[0] ) );
  }
  mainwin=NULL;
  aboutb=NULL;
  configureb=NULL;
  clockbar=NULL;
  statebar=NULL;
  pathbs=NULL;
  buttons=NULL;
  button_bank_slider = NULL;
  lister[0]=NULL;
  lister[1]=NULL;
  listerwin[0] = NULL;
  listerwin[1] = NULL;
  maincont = NULL;
  m_worker_menu = NULL;
  curRows = 0;
  curColumns = 0;
  m_main_window_width = 1000;
  m_main_window_height = 700;
  m_main_window_last_absolute_x = 0;
  m_main_window_last_absolute_y = 0;
  m_main_window_last_maximized_x = false;
  m_main_window_last_maximized_y = false;
  pbanknr=0;
  bbanknr=0;
  runningmode=WORKER_NORMAL;
  this->argc=targc;
  this->argv=targv;
  m_optind = optind;

  startcwd = NWC::OS::getCWD();
  
  dm=new DNDMsg(this);
  lasttimep=0;
  
  if(req_static==NULL) {
    // only if not initialized
    req_static=new Requester(aguix);
  }
  freesp = new PartSpace();

  if ( geteuid() == 0 )
    isRoot = true;
  else
    isRoot = false;
  
  lastkey = 0;
  lastmod = 0;
  lastkey_time = 0;

  keyhashtable = NULL;
  keyhashtable_size = 1000;
  waitcursorcount = 0;
  restart = RS_NONE;

  m_interpret_recursion_depth = 0;
  m_use_custom_layout = false;

  m_current_clock_update_ms = 0;
  m_current_dbus_update_ms = 0;
  m_current_statebar_update_ms = 0;

  m_hint_time = 0;

  m_pathname_watcher.reset( new PathnameWatcher() );

  m_ssh_allow = SSH_ASK;

  m_menu_active = false;

  m_commandmenu_original_button_banknr = 0;
  m_commandmenu_original_path_banknr = 0;

  m_path_jump_in_progress = false;

  m_key_chain_cand_active = false;
}

Worker::~Worker()
{
  // free tokenlist
  // it doesn't hurt if we need it again, it will
  // be recreated but that's the easiest way to release
  // the memory
  cleanUpTokens();
  lexer_cleanup();

  if(req_static!=NULL) {
    delete req_static;
    req_static=NULL;
  }

  workerInstances--;
  if ( ( workerInstances < 1 ) && ( aguix != NULL ) ) {
    delete aguix;
    aguix = NULL;
  }
  delete dm;
  delete freesp;
}

void Worker::run()
{
  int i,j;
  time_t lastConfigCheck;
  bool found;
  int waitcount=0;
  int erg;
  time_t lastConfig = 0;
  std::string str1;
  time_t lastVolumeCheck = 0;
  time_t now;
  time_t last_process_update = 0;

  getKVPStore().setIntValue( "init-state", 1 );

  updateSettingsFromWConfig();

  str1 = WorkerInitialSettings::getInstance().getConfigBaseDir();
  str1 = NWC::Path::join( str1, "directory-history" );
#ifdef USEOWNCONFIGFILES
  str1 += ".dev";
#endif
  m_pers_path_store = std::unique_ptr< PersDeepPathStore >( new PersDeepPathStore( str1 ) );

  auto archive_filename = str1;
  archive_filename += ".archive";
  m_pers_path_store_archive = std::unique_ptr< PersDeepPathStore >( new PersDeepPathStore( archive_filename ) );

  std::string cfgfile = Worker::getWorkerConfigDir();
#ifdef USEOWNCONFIGFILES
  cfgfile = NWC::Path::join( cfgfile, "directory-history.prob2" );
#else
  cfgfile = NWC::Path::join( cfgfile, "directory-history.prob" );
#endif

  m_pdb = std::unique_ptr< PrefixDB >( new PrefixDB( cfgfile ) );

  str1 = Worker::getWorkerConfigDir();
  str1 = NWC::Path::join( str1, "filter-stash" );
#ifdef USEOWNCONFIGFILES
  str1 += ".dev";
#endif
  m_filter_stash = std::unique_ptr< FilterStash >( new FilterStash( str1 ) );

  restart = RS_NONE;

  loadWindowGeometry();
  aguix->setFont( wconfig->getFont( 0 ).c_str() );
  createMainWin();
  setupMainWin();
  initWaitThread();

  lister[0] = new Lister( this, 0 );
  lister[1] = new Lister( this, 1 );
  initListerState();
  AGMessage *msg;
  bool handleSub;
  int rows,columns;
  rows=wconfig->getRows();
  columns=wconfig->getColumns();
  int pathact=-1;

  buildtable();

  lastConfigCheck = time( NULL );

  initVolumeManagerStuff();
#if defined(HAVE_HAL_DBUS) or defined(HAVE_DBUS)
  setDbusUpdate( DBUS_POLL_TIME * 1000 );
#endif

  updateHintDB();

  showNextHint();

  m_menu_ui.reset( new MenuTreeUI( aguix ) );
  prepareMenuTree( *m_menu_ui );

  getKVPStore().setIntValue( "init-state", 2 );

  while(runningmode!=WORKER_QUIT) {
    msg=aguix->GetMessage(NULL);
    handleSub=true;
    found=false;
    if(msg!=NULL) {
      switch(msg->type) {
        case AG_CLOSEWINDOW:
            if ( msg->size.window == mainwin->getWindow() ) {
                quit(1);
                handleSub=false;
            }
            break;
        case AG_SIZECHANGED:
          if(msg->size.window==mainwin->getWindow()) {
            m_main_window_width = msg->size.neww;
            m_main_window_height = msg->size.newh;
            if ( m_main_window_width < 300 || m_main_window_height < 200 ) {
              if ( m_main_window_width < 300 ) m_main_window_width = 300;
              if ( m_main_window_height < 200 ) m_main_window_height = 200;
              // user resized the window to too little values
              // (perhaps the WM ignores minsize)
              // at the moment I won't resize the win again to valid values
              // because it's the users problem when the window is too small
              // mainwin->resize( w, h );
            }
            maincont->resize( m_main_window_width, m_main_window_height );
            maincont->rearrange();
          }
          break;
        case AG_BUTTONPRESSED:
          if(msg->button.state==2) {
            for(i=0;i<rows;i++) {
              if(pathbs[i]==msg->button.button) {
                for(j=0;j<i;j++) pathbs[j]->setState(2);
                for(j=i+1;j<rows;j++) pathbs[j]->setState(2);
                pathact=i;
                handleSub=false;
                found = true;
                break;
              }
            }
          }
          if ( ( msg->button.keystate & ControlMask ) && found == false ) {
              for ( i = 0; i < rows; i++ ) {
                  for ( j = 0; j < columns; j++ ) {
                      if ( msg->button.button == buttons[ i * columns + j ] ) {
                          showButtonCommandHelp( i, j, msg->button.state );
                          found = true;
                          break;
                      }
                  }
              }
          }
          break;
        case AG_BUTTONRELEASED:
          if(msg->button.button==aboutb) {
            about();
            handleSub=false;
          } else if(msg->button.button==configureb) {
            configure();
            handleSub=false;
          } else if(msg->button.button==clockbar) {
            if( msg->button.state == 1 ) {
              shuffleButton( 1 );
            } else if ( msg->button.state == 2 ) {
              shuffleButton( -1 );
            } else if ( msg->button.state == 3 ) { // mouse wheel up
              shuffleButton( -1, true );
            } else if ( msg->button.state == 4 ) { // mouse wheel down
              shuffleButton( 1, true );              
            }
            handleSub=false;
          } else if ( msg->button.button == statebar ) {
              if ( msg->button.state == 2 ) {
                  openWorkerMenu();
              }
              handleSub = false;
          } else {
            for(i=0;i<rows;i++) {
              if(pathbs[i]==msg->button.button) {
                if(msg->button.state==2) {
                  shufflePath();
                } else {
                  // Path setzen
                  setPath(i+pbanknr*rows);
                }
                found=true;
                break;
              }
            }
            if(found==false) {
              for(i=0;i<rows;i++) {
                for(j=0;j<columns;j++) {
                  if(msg->button.button==buttons[i*columns+j]) {
                    activateButton(i,j,msg->button.state);
                    found=true;
                  }
                }
              }
            } else handleSub=false;
          }
          break;
        case AG_MOUSERELEASED:
          if(pathact>=0) {
            for(j=0;j<pathact;j++) pathbs[j]->setState(0);
            for(j=pathact+1;j<rows;j++) pathbs[j]->setState(0);
            pathact=-1;
            handleSub=false;
          }
          break;
        case AG_DND_END:
          // End of DND; safe the message and find the mode
          dm->setEnd(&(msg->dnd));
          if ( dm->getSourceLister() != NULL ) {
              dm->getSourceLister()->startdnd( dm );
          } else if ( dm->getDestLister() != NULL ) {
              dm->getDestLister()->startdnd( dm );
          }
          break;
        case AG_KEYRELEASED:
            if ( msg->key.key == XK_Control_L ||
                 msg->key.key == XK_Control_R ) {
                removeStatebarText( m_button_help_text );
            }
            break;
        case AG_KEYPRESSED:
          if ( mainwin->isParent( msg->key.window, false ) == true ) {
              if ( ! isIgnoredKey( msg->key.key ) ) {
                  if ( activateShortkey(msg) != 0 ) {
                      handleSub = false;
                  } else if ( getKeyChainCandidateActive() ) {
                      handleSub = false;
                  }
              }
          } else handleSub = false;
          break;
        case AG_POPUPMENU_CLICKED:
            if ( msg->popupmenu.menu == m_worker_menu ) {
                startMenuAction( msg );
                handleSub = false;
            } else {
                handlePopUps( msg );
            }
            break;
        case AG_SLIDER_CHANGED:
            if ( msg->slider.slider == button_bank_slider ) {
                switchToButtonBank( msg->slider.offset );
            }
          case AG_CLIENT_MESSAGE:
              if ( msg->clientmessage.cm_type == CM_XIM_STUCK ) {
                  std::string str1 = catalog.getLocale( 11 );
                  str1 += "|";
                  str1 += catalog.getLocale( 8 );
                  static bool asked = false;

                  if ( ! asked ) {
                      int res = req_static->request( catalog.getLocale( 125 ),
                                                     "There seems to be a problem with the X Input Method.|You can choose to switch to simple XIM mode next time you|start worker. You can change this setting with|CLI option --override_xim=yes/no.",
                                                     str1.c_str() );

                      if ( res == 0 ) {
                          setOverrideXIM( true );
                          aguix->setSkipFilterEvent( true );
                      }

                      asked = true;
                  }
              }
        default:
          break;
      }
      if(handleSub==true) {
        lister[0]->messageHandler(msg);
        lister[1]->messageHandler(msg);
      }
      aguix->executeBGHandlers( *msg );
      aguix->ReplyMessage(msg);
    } else {
      updateTime();
      updateStatebar();
      lister[0]->cyclicfunc(CYCLICFUNC_MODE_NORMAL);
      lister[1]->cyclicfunc(CYCLICFUNC_MODE_NORMAL);
      waitcount++;

      now = time( NULL );

      if ( difftime( now, lastConfigCheck ) > 60 ) {
        if ( wconfig->checkForNewerConfig() == true ) {
          // config is newer then used config
          // now check if this config is even newer then the
          // last decided
          if ( difftime( wconfig->getFileConfigMod(), lastConfig ) > 0 ) {
            // config changed after last check
            str1 = catalog.getLocale( 511 );
            str1 += "|";
            str1 += catalog.getLocale( 512 );
            erg = req_static->request( catalog.getLocale( 123 ),
                                       catalog.getLocale( 510 ),
                                       str1.c_str() );
            if ( erg == 0 ) {
              wconfig->load();
              wconfig->applyColorList(wconfig->getColors());
              wconfig->applyLanguage();
              restart = RS_RECONFIG;
            }
            lastConfig = wconfig->getFileConfigMod();
          }
        }
        lastConfigCheck = now;
      }

      if ( difftime( now, lastVolumeCheck ) >= DBUS_POLL_TIME ) {
          checkNewVolumes();
          lastVolumeCheck = now;
      }

      checkFutures();

      waitForEvent();
      if ( m_event_store.check_children == true ||
           ( ! m_process_handler.empty() &&
             difftime( now, last_process_update ) >= 60 ) ) {
          m_process_handler.checkChildren();
          last_process_update = now;
          m_event_store.check_children = false;
      }

      fireSeenEvents();

      if ( ! m_changed_paths.empty() ) {
          for ( std::set< std::string >::const_iterator it1 = m_changed_paths.begin();
                it1 != m_changed_paths.end();
                ++it1 ) {
              bool active = false;
              std::set< std::string > temp_set;

              temp_set.insert( *it1 );

              if ( lister[0]->pathsChanged( temp_set ) == true ) active = true; 
              if ( lister[1]->pathsChanged( temp_set ) == true ) active = true;

              if ( ! active ) {
                  // no one cared for the changed path
                  // could be some forgotten unwatch
                  // so just unwatch those paths here
                  m_pathname_watcher->unwatchPath( *it1, true );
              }
          }

          m_changed_paths.clear();
      }

      checkErrnoCount();
    }

    if ( ! m_pending_config_open.empty() ) {
        configure( m_pending_config_open );
        m_pending_config_open.clear();
    }

    if ( restart == RS_RECONFIG ) {

        updateSettingsFromWConfig();

        buildtable();

        //TODO
        aguix->setFont( wconfig->getFont( 0 ).c_str() );
        setupMainWin();
        rows=wconfig->getRows();
        columns=wconfig->getColumns();
        lister[0]->cyclicfunc(CYCLICFUNC_MODE_RESTART);
        lister[1]->cyclicfunc(CYCLICFUNC_MODE_RESTART);
        lister[0]->relayout();
        lister[1]->relayout();

        updateVolumeManagerConfig();

        updateHintDB();

        prepareMenuTree( *m_menu_ui );

        lasttimep=0;
    } else if ( restart == RS_RELAYOUT ) {
        setupMainWin();
        lister[0]->relayout();
        lister[1]->relayout();
    }
    restart = RS_NONE;
  }

  lister[0]->finalizeBGOps();
  lister[1]->finalizeBGOps();

  m_process_handler.clearProcesses();

  cleanFutures();

  shutdownWaitThread();

  if ( wconfig->getSaveWorkerStateOnExit() == true ) {
      saveWorkerState();
  }
  delete lister[0];
  delete lister[1];

  closeMainWin();
  deletetable();

  m_hint_db = NULL;
}

AGUIX *Worker::getAGUIX()
{
  return aguix;
}

int main(int argc,char **argv)
{
  bool showversion,showhelp;
  int override_state = 0;
  char *setlang;
  int restore_tabs = 0;

#ifdef HAVE_SIGACTION
  struct sigaction sig_ac;
  
  memset( &sig_ac, 0, sizeof( sig_ac ) );
  sig_ac.sa_handler = worker_sighandler;
  sigemptyset( &sig_ac.sa_mask );

  sigaction( SIGUSR1, &sig_ac, NULL );
  sigaction( SIGCHLD, &sig_ac, NULL );
  sigaction( SIGALRM, &sig_ac, NULL );

  if ( pipe( sigchld_pipe ) == -1 ) {
      fprintf( stderr, "Worker: can't create pipe\n" );
      exit( EXIT_FAILURE );
  }
#endif

  srand( time( NULL ) );
  srand48( time( NULL ) );

  tzset();

  showversion=false;
  showhelp=false;

  while ( true ) {
#ifdef HAVE_GETOPT_LONG
      int option_index;
      static struct option long_options[] = {
          { "help", no_argument, NULL, 'h' },
          { "version", no_argument, NULL, 'V' },
          { "config", required_argument, NULL, 'c' },
          { "override-xim", required_argument, NULL, 'x' },
          { "restore-tabs", optional_argument, NULL, 't' },
          { NULL, 0, NULL, 0 }
      };
#endif

#ifdef HAVE_GETOPT_LONG
      int c = getopt_long( argc, argv, "hVc:", long_options, &option_index );
#else
      int c = getopt( argc, argv, "hVc:" );
#endif

      if ( c == -1 ) break;
      
      switch( c ) {
          case 'c':
              if ( optarg != NULL ) {
                  std::string configbasedir = optarg;
                  if ( NWC::Path::isAbs( configbasedir ) ) {
                      WorkerInitialSettings::getInstance().setConfigBaseDir( configbasedir );
                  } else {
                      fprintf( stderr, "Worker: relative path %s not allowed for config base directory\n", configbasedir.c_str() );
                  }
              }
              break;
          case 'x':
              if ( optarg != NULL ) {
                  std::string value = optarg;

                  if ( value == "yes" ) {
                      override_state = 2;
                  } else if ( value == "no" ) {
                      override_state = 1;
                  } else {
                      fprintf( stderr, "Worker: choose either --override-xim=yes or no\n" );
                      showhelp = true;
                  }
              }
              break;
          case 't':
              if ( optarg != NULL ) {
                  std::string value = optarg;

                  if ( value == "yes" ) {
                      restore_tabs = 2;
                  } else if ( value == "no" ) {
                      restore_tabs = 1;
                  } else {
                      fprintf( stderr, "Worker: choose either --restore-tabs=yes or no\n" );
                      showhelp = true;
                  }
              } else {
                  restore_tabs = 2;
              }
              break;
          case 'h':
              showhelp = true;
              break;
          case 'V':
              showversion = true;
              break;
          default:
              break;
      }
  }

  if(showversion==true) {
    printf("Worker Version: %d.%d.%d%s\n",WORKER_MAJOR,WORKER_MINOR,WORKER_PATCH,WORKER_COMMENT);
    printf("  Author: Ralf Hoffmann <ralf@boomerangsworld.de>\n");
    printf("  Homepage: http://www.boomerangsworld.de/worker\n\n");
    exit(0);
  }
  if(showhelp==true) {
    printf("Worker Filemanager\n");
    printf("  Version: %d.%d.%d%s\n",WORKER_MAJOR,WORKER_MINOR,WORKER_PATCH,WORKER_COMMENT);
    printf("  Author: Ralf Hoffmann <ralf@boomerangsworld.de>\n");
    printf("  Homepage: http://www.boomerangsworld.de/worker\n\n");
    printf("  Options: -V, --version\tPrint the program version and exit\n");
    printf("           -h, --help\t\tPrint this help and exit\n");
    printf("           -c, --config=<path>\tUse given path for configuration data\n\n");
    printf("               --override-xim=<yes/no>\tOverride global XIM setting to use simple method\n");
    printf("               --restore-tabs=<yes/no>\tRestore tabs from previous session (or don't)\n\n");
    printf("           <dir>\t\tShows the dir on the left side\n");
    printf("           <dir1> <dir2>\tShows dir1 on the left side, dir2 on the right side\n\n");
    exit(0);
  }
 
  if ( worker_tmpdir() == NULL ) {
    exit( 0 );
  }

  ugdb=new GroupHash();
  Worker *worker = new Worker( argc, argv, optind );

  if ( override_state == 2 ) {
      worker->setOverrideXIM( true );
  } else if ( override_state == 1 ) {
      worker->setOverrideXIM( false );
  }

  worker->getKVPStore().setIntValue( "cla-restore-tabs", restore_tabs );

  if(worker->getAGUIX()->checkX()==True) {
    wconfig=new WConfig(worker);
    wconfig->applyColorList(wconfig->getColors());
    
    // now check for existence of ".worker" and if not try to create it and copy an example config
    setlang = NULL;
    worker->checkfirststart( &setlang );
    
    wconfig->load();
    
    if ( setlang != NULL ) {
      wconfig->setLang( setlang );
    }

    wconfig->applyColorList(wconfig->getColors());
    wconfig->applyLanguage();

    if ( setlang != NULL ) {
      wconfig->save();
      _freesafe( setlang );
    }

    worker_version last_running_version = worker->getLastRunningVersion();

    worker->checkForUpdates( last_running_version,
                             wconfig );

    wconfig->applyKVPPool();
    
    MagicDB::getInstance().init();

    worker->run();
    
    MagicDB::getInstance().fini();
    
    delete wconfig;
  } else {
    printf("Worker: Can't connect to X!\n");
  }

  delete worker;
  delete ugdb;

  WorkerInitialSettings::destroyInstance();

  catalog.freeLanguage();
  return 0;
}

AWindow *Worker::getMainWin()
{
  return mainwin;
}

void Worker::setupMainWin()
{
  int rows, columns;
  int th;
  unsigned int i;
  int i2;
  AGUIXFont *font;
  const int cfix = AContainer::ACONT_MINH +
                   AContainer::ACONT_MINW +
                   AContainer::ACONT_MAXH +
                   AContainer::ACONT_MAXW;
  const int conlyh = AContainer::ACONT_MINH +
                     AContainer::ACONT_MAXH;
  bool bv, lvv, statebarfound;
  int e1, pos;
  std::list<LayoutSettings::layoutID_t> orders;
  std::list<LayoutSettings::layoutID_t>::iterator it1;
  AContainer *sbc = NULL;
  AContainer *clbc = NULL;
  AContainer *bc = NULL;
  AContainer *lc = NULL;
  AContainer *lc2 = NULL;
  int listviewsCount = 0;
  int x,y;
  int statebarCount, clockbarCount, buttonsCount, bllCount, blCount;
  int l;
  int lv_weight[2] = { 5, 5 };
  bool weight_rel_to_active = false;
  int slider_size = 10;
  
  rows=wconfig->getRows();
  columns=wconfig->getColumns();

  if ( maincont != NULL ) delete maincont;
  
  if ( m_use_custom_layout == true ) {
      bv = m_custom_layout.getButtonVert();
      lvv = m_custom_layout.getListViewVert();
      orders = m_custom_layout.getOrders();
      lv_weight[0] = m_custom_layout.getListViewWeight();
      weight_rel_to_active = m_custom_layout.getWeightRelToActive();
  } else {
      bv = wconfig->getLayoutButtonVert();
      lvv = wconfig->getLayoutListviewVert();
      orders = wconfig->getLayoutOrders();
      lv_weight[0] = wconfig->getLayoutListViewWeight();
      weight_rel_to_active = wconfig->getLayoutWeightRelToActive();
  }

  if ( lv_weight[0] < 1 || lv_weight[0] > 9 ) lv_weight[0] = 5;
  lv_weight[1] = 10 - lv_weight[0];

  if ( weight_rel_to_active == true ) {
      if ( lister[0] && lister[0]->isActive() == false ) {
          int tw = lv_weight[0];
          lv_weight[0] = lv_weight[1];
          lv_weight[1] = tw;
      }
  }
  
  if ( orders.size() < 1 ) {
    // default values for empty orders
    bv = lvv = false;
    orders.push_back( LayoutSettings::LO_STATEBAR );
    orders.push_back( LayoutSettings::LO_LISTVIEWS );
    orders.push_back( LayoutSettings::LO_BUTTONS );
    orders.push_back( LayoutSettings::LO_CLOCKBAR );
  }
  
  if ( ( bv == true ) && ( lvv == true ) ) e1 = 3;
  else if ( ( bv == false ) && ( lvv == true ) ) e1 = 5;
  else if ( ( bv == true ) && ( lvv == false ) ) e1 = 3;
  else e1 = 4;
  
  if ( orders.size() < (unsigned int)e1 ) e1 = orders.size();
  if ( e1 < 1 ) e1 = 1;
  
  statebarfound = false;
  for ( it1 = orders.begin(); it1 != orders.end(); it1++ ) {
    if ( *it1 == LayoutSettings::LO_STATEBAR ) {
      statebarfound = true;
      break;
    }
  }
  if ( statebarfound == false ) e1++;

  maincont = mainwin->setContainer( new AContainer( mainwin, 0, e1 ) );
  maincont->setMaxSpace( 0 );
  maincont->setMinSpace( 0 );
  maincont->setBorderWidth( 0 );
  
  statebarCount = 0;
  clockbarCount = 0;
  buttonsCount = 0;
  bllCount = 0;
  blCount = 0;

  if ( button_bank_slider != NULL ) {
      mainwin->remove( button_bank_slider );
      delete button_bank_slider;
      button_bank_slider = NULL;
  }

  // TODO use better default value depending on dpi
  slider_size = 12;

  for ( pos = 0, it1 = orders.begin(); ( pos < e1 ) && ( it1 != orders.end() ); pos++ ) {
    if ( ( statebarfound == false ) ||
         ( *it1 == LayoutSettings::LO_STATEBAR ) ) {
      if ( statebarCount < 1 ) {
        sbc = maincont->add( new AContainer( mainwin, 3, 1 ), 0, pos );
        sbc->setMaxSpace( 0 );
        sbc->setMinSpace( 0 );
        sbc->setBorderWidth( 0 );
        sbc->add( aboutb, 0, 0, cfix );
        sbc->add( configureb, 1, 0, cfix );
        sbc->add( statebar, 2, 0, conlyh );
        statebarCount++;
        
        if ( statebarfound == false ) {
          statebarfound = true;
          continue;
        }
      }
    } else if ( *it1 == LayoutSettings::LO_CLOCKBAR ) {
      if ( clockbarCount < 1 ) {
        clbc = maincont->add( new AContainer( mainwin, 1, 1 ), 0, pos );
        clbc->setMaxSpace( 0 );
        clbc->setMinSpace( 0 );
        clbc->setBorderWidth( 0 );
        clbc->add( clockbar, 0, 0, conlyh );
        clockbarCount++;
      }
    } else if ( *it1 == LayoutSettings::LO_BUTTONS ) {
      if ( ( bv == false ) && ( buttonsCount < 1 ) ) {
          AContainer *tc = maincont->add( new AContainer( mainwin, 2, 1 ), 0, pos );
          tc->setMaxSpace( 0 );
          tc->setMinSpace( 0 );
          tc->setBorderWidth( 0 );

          button_bank_slider = (Slider*)tc->add( new Slider( aguix, 0, 0, slider_size, slider_size, true, 0 ),
                                                 1, 0, AContainer::CO_INCH );

          bc = tc->add( new AContainer( mainwin, columns + 1, rows ), 0, 0 );
          bc->setMaxSpace( 0 );
          bc->setMinSpace( 0 );
          bc->setBorderWidth( 0 );
          buttonsCount++;
      }
    } else if ( *it1 == LayoutSettings::LO_LISTVIEWS ) {
      if ( bv == false ) {
        if ( ( listviewsCount < 1 ) && ( lvv == false ) ) {
          lc = maincont->add( new AContainer( mainwin, 2, 1 ), 0, pos );
          lc->setMaxSpace( 0 );
          lc->setMinSpace( 0 );
          lc->setBorderWidth( 0 );
          lc->add( listerwin[0], 0, 0 );
          lc->setWWeight( lv_weight[0], 0, 0 );
          lc->add( listerwin[1], 1, 0 );
          lc->setWWeight( lv_weight[1], 1, 0 );
          listviewsCount++;
        } else if ( ( listviewsCount < 2 ) && ( lvv == true ) ) {
          lc = maincont->add( new AContainer( mainwin, 1, 1 ), 0, pos );
          lc->setMaxSpace( 0 );
          lc->setMinSpace( 0 );
          lc->setBorderWidth( 0 );
          lc->add( listerwin[listviewsCount], 0, 0 );
          maincont->setHWeight( lv_weight[listviewsCount], 0, pos );
          listviewsCount++;
        }
      }
    } else if ( *it1 == LayoutSettings::LO_BLL ) {
      if ( ( bv == true ) && ( lvv == false ) && ( bllCount < 1 ) ) {
        lc = maincont->add( new AContainer( mainwin, 3, 1 ), 0, pos );
        lc->setMaxSpace( 0 );
        lc->setMinSpace( 0 );
        lc->setBorderWidth( 0 );
        lc->add( listerwin[0], 1, 0 );
        lc->add( listerwin[1], 2, 0 );
        
        bc = lc->add( new AContainer( mainwin, 1, ( columns + 1 ) * rows + 1 ), 0, 0 );
        bc->setMaxSpace( 0 );
        bc->setMinSpace( 0 );
        bc->setBorderWidth( 0 );
        bllCount++;
        
        button_bank_slider = (Slider*)bc->add( new Slider( aguix, 0, 0, slider_size, slider_size, false, 0 ),
                                               0, ( columns + 1 ) * rows, AContainer::CO_INCW );

        lc->setWWeight( 2 * lv_weight[0], 1, 0 );
        lc->setWWeight( 2 * lv_weight[1], 2, 0 );

        lc->setWWeight( 1 * 5, 0, 0 );

        l = getMaxButtonsWidth() + 4;
        lc->setMaxWidth( l, 0, 0 );
      }
    } else if ( *it1 == LayoutSettings::LO_LBL ) {
      if ( ( bv == true ) && ( lvv == false ) && ( bllCount < 1 ) ) {
        lc = maincont->add( new AContainer( mainwin, 3, 1 ), 0, pos );
        lc->setMaxSpace( 0 );
        lc->setMinSpace( 0 );
        lc->setBorderWidth( 0 );
        lc->add( listerwin[0], 0, 0 );
        lc->add( listerwin[1], 2, 0 );
        
        bc = lc->add( new AContainer( mainwin, 1, ( columns + 1 ) * rows + 1 ), 1, 0 );
        bc->setMaxSpace( 0 );
        bc->setMinSpace( 0 );
        bc->setBorderWidth( 0 );
        bllCount++;
        
        button_bank_slider = (Slider*)bc->add( new Slider( aguix, 0, 0, slider_size, slider_size, false, 0 ),
                                               0, ( columns + 1 ) * rows, AContainer::CO_INCW );

        lc->setWWeight( 2 * lv_weight[0], 0, 0 );
        lc->setWWeight( 2 * lv_weight[1], 2, 0 );

        lc->setWWeight( 1 * 5, 1, 0 );

        l = getMaxButtonsWidth() + 4;
        lc->setMaxWidth( l, 1, 0 );
      }
    } else if ( *it1 == LayoutSettings::LO_LLB ) {
      if ( ( bv == true ) && ( lvv == false ) && ( bllCount < 1 ) ) {
        lc = maincont->add( new AContainer( mainwin, 3, 1 ), 0, pos );
        lc->setMaxSpace( 0 );
        lc->setMinSpace( 0 );
        lc->setBorderWidth( 0 );
        lc->add( listerwin[0], 0, 0 );
        lc->add( listerwin[1], 1, 0 );
        
        bc = lc->add( new AContainer( mainwin, 1, ( columns + 1 ) * rows + 1 ), 2, 0 );
        bc->setMaxSpace( 0 );
        bc->setMinSpace( 0 );
        bc->setBorderWidth( 0 );
        bllCount++;
        
        button_bank_slider = (Slider*)bc->add( new Slider( aguix, 0, 0, slider_size, slider_size, false, 0 ),
                                               0, ( columns + 1 ) * rows, AContainer::CO_INCW );

        lc->setWWeight( 2 * lv_weight[0], 0, 0 );
        lc->setWWeight( 2 * lv_weight[1], 1, 0 );

        lc->setWWeight( 1 * 5, 2, 0 );

        l = getMaxButtonsWidth() + 4;
        lc->setMaxWidth( l, 2, 0 );
      }
    } else if ( *it1 == LayoutSettings::LO_BL ) {
      if ( ( bv == true ) && ( lvv == true ) && ( blCount < 1 ) ) {
        lc = maincont->add( new AContainer( mainwin, 2, 1 ), 0, pos );
        lc->setMaxSpace( 0 );
        lc->setMinSpace( 0 );
        lc->setBorderWidth( 0 );
        lc2 = lc->add( new AContainer( mainwin, 1, 2 ), 1, 0 );
        lc2->setMaxSpace( 0 );
        lc2->setMinSpace( 0 );
        lc2->setBorderWidth( 0 );
        lc2->add( listerwin[0], 0, 0 );
        lc2->setHWeight( lv_weight[0], 0, 0 );
        lc2->add( listerwin[1], 0, 1 );
        lc2->setHWeight( lv_weight[1], 0, 1 );
        
        bc = lc->add( new AContainer( mainwin, 1, ( columns + 1 ) * rows + 1 ), 0, 0 );
        bc->setMaxSpace( 0 );
        bc->setMinSpace( 0 );
        bc->setBorderWidth( 0 );
        blCount++;
        
        button_bank_slider = (Slider*)bc->add( new Slider( aguix, 0, 0, slider_size, slider_size, false, 0 ),
                                               0, ( columns + 1 ) * rows, AContainer::CO_INCW );

        lc->setWWeight( 4, 1, 0 );
        l = getMaxButtonsWidth() + 4;
        lc->setMaxWidth( l, 0, 0 );
      }
    } else if ( *it1 == LayoutSettings::LO_LB ) {
      if ( ( bv == true ) && ( lvv == true ) && ( blCount < 1 ) ) {
        lc = maincont->add( new AContainer( mainwin, 2, 1 ), 0, pos );
        lc->setMaxSpace( 0 );
        lc->setMinSpace( 0 );
        lc->setBorderWidth( 0 );
        lc2 = lc->add( new AContainer( mainwin, 1, 2 ), 0, 0 );
        lc2->setMaxSpace( 0 );
        lc2->setMinSpace( 0 );
        lc2->setBorderWidth( 0 );
        lc2->add( listerwin[0], 0, 0 );
        lc2->setHWeight( lv_weight[0], 0, 0 );
        lc2->add( listerwin[1], 0, 1 );
        lc2->setHWeight( lv_weight[1], 0, 1 );
        
        bc = lc->add( new AContainer( mainwin, 1, ( columns + 1 ) * rows + 1 ), 1, 0 );
        bc->setMaxSpace( 0 );
        bc->setMinSpace( 0 );
        bc->setBorderWidth( 0 );
        blCount++;
        
        button_bank_slider = (Slider*)bc->add( new Slider( aguix, 0, 0, slider_size, slider_size, false, 0 ),
                                               0, ( columns + 1 ) * rows, AContainer::CO_INCW );

        lc->setWWeight( 4, 0, 0 );

        l = getMaxButtonsWidth() + 4;
        lc->setMaxWidth( l, 1, 0 );
      }
    }
    it1++;
  }

  if ( button_bank_slider != NULL ) {
      button_bank_slider->setShowArrowButtons( false );
      button_bank_slider->setAcceptFocus( false );
  }
  
  if ( ( curRows != 0 ) && ( curColumns != 0 ) ) {
    for ( i = 0; i < curRows; i++ ) {
      for( unsigned int j = 0; j < curColumns; j++ ) {
        mainwin->remove(buttons[ i * curColumns + j ]);
        delete buttons[ i * curColumns + j ];
        buttons[ i * curColumns + j ] = NULL;
      }
    }
    delete [] buttons;
    for(i=0;i<curRows;i++) {
      mainwin->remove(pathbs[i]);
      delete pathbs[ i ];
      pathbs[ i ] = NULL;
    }
    delete [] pathbs;
  }

  font = aguix->getFont( wconfig->getFont( 1 ).c_str() );
  AGUIXFont *statebarfont = aguix->getFont( wconfig->getFont( 5 ).c_str() );

  th = aguix->getCharHeight() + 4;
  int statebarfont_th = statebarfont->getCharHeight() + 4;

  aboutb->setFont( wconfig->getFont( 5 ).c_str() );
  configureb->setFont( wconfig->getFont( 5 ).c_str() );
  statebar->setFont( wconfig->getFont( 5 ).c_str() );

  aboutb->resize( statebarfont_th, statebarfont_th );
  configureb->resize( statebarfont_th, statebarfont_th );
  statebar->resize( statebar->getWidth(), statebarfont_th );
  if ( sbc != NULL ) sbc->readLimits();
  
  clockbar->resize( clockbar->getWidth(), th );
  if ( clbc != NULL ) clbc->readLimits();
  
  if ( font != NULL ) th = font->getCharHeight() + 4;
  buttons = new Button*[ columns * rows ];
  for ( i2 = 0; i2 < rows; i2++ ) {
    for ( int j = 0; j < columns; j++ ) {
      if ( bv == true ) {
        x = 0;
        y = ( j + 1 ) * rows + i2;
      } else {
        x = j + 1;
        y = i2;
      }
    
      buttons[ i2 * columns + j ] = new Button( aguix, 0, 0, th, th, "", 1, 0, 0 );
      buttons[ i2 * columns + j ]->setAcceptFocus( false );
      if ( font != NULL ) buttons[ i2 * columns + j ]->setFont( wconfig->getFont( 1 ).c_str() );

      if ( bc != NULL ) bc->add( buttons[ i2 * columns + j ], x, y, ( bv == false ) ? conlyh : 0 );

      buttons[ i2 * columns + j ]->setBubbleHelpCallback(
                                                         [this, i2, j, columns]( int data ) {
                                                             return getButtonHelpText( i2 * columns + j );
                                                         },
                                                         i2 * columns + j );
    }
  }
  pathbs = new Button*[ rows ];
  for ( i2 = 0; i2 < rows; i2++ ) {
    pathbs[i2] = new Button( aguix, 0, 0, th, "", "", 1, 1, 0, 0, 0 );
    pathbs[i2]->setAcceptFocus( false );
    pathbs[i2]->resize( th, th );
    if ( font != NULL ) pathbs[i2]->setFont( wconfig->getFont( 1 ).c_str() );
    pathbs[i2]->setShowDual(false);

    if ( bc != NULL ) bc->add( pathbs[i2], 0, i2, ( bv == false ) ? conlyh : 0 );
  }
  
  if ( clockbarCount > 0 ) {
    clockbar->show();
  } else {
    clockbar->hide();
  }
  if ( ( listviewsCount < 1 ) && ( bllCount < 1 ) && ( blCount < 1 ) ) {
    listerwin[0]->hide();
    listerwin[1]->hide();
  } else if ( ( listviewsCount == 1 ) && ( lvv == true ) ) {
    listerwin[0]->show();
    listerwin[1]->hide();
  } else {
    listerwin[0]->show();
    listerwin[1]->show();
  }

  curRows=rows;
  curColumns=columns;
  showPathBank();
  showButtonBank();
  if ( isRoot == false ) {
    aboutb->setFG( 0, aguix->getFaces().getColor( "statusbar-fg" ) );
    aboutb->setBG( 0, aguix->getFaces().getColor( "statusbar-bg" ) );
    configureb->setFG( 0, aguix->getFaces().getColor( "statusbar-fg" ) );
    configureb->setBG( 0, aguix->getFaces().getColor( "statusbar-bg" ) );
    statebar->setFG( 0, aguix->getFaces().getColor( "statusbar-fg" ) );
    statebar->setBG( 0, aguix->getFaces().getColor( "statusbar-bg" ) );
    statebar->setFG( 1, aguix->getFaces().getColor( "statusbar-fg" ) );
    statebar->setBG( 1, aguix->getFaces().getColor( "statusbar-bg" ) );
  } else {
    aboutb->setFG( 0, 2 );
    aboutb->setBG( 0, 4 );
    configureb->setFG( 0, 2 );
    configureb->setBG( 0, 4 );
    statebar->setFG( 0, 2 );
    statebar->setBG( 0, 4 );
    statebar->setFG( 1, 2 );
    statebar->setBG( 1, 4 );
  }
  clockbar->setFG( 0, aguix->getFaces().getColor( "clockbar-fg" ) );
  clockbar->setBG( 0, aguix->getFaces().getColor( "clockbar-bg" ) );
  clockbar->setFG( 1, aguix->getFaces().getColor( "clockbar-fg" ) );
  clockbar->setBG( 1, aguix->getFaces().getColor( "clockbar-bg" ) );
  maincont->resize( m_main_window_width, m_main_window_height );
  maincont->rearrange();

  AGMessage *msg=new AGMessage;
  msg->type = AG_SIZECHANGED;
  msg->size.window = mainwin->getWindow();
  msg->size.neww = m_main_window_width;
  msg->size.newh = m_main_window_height;
  if ( lister[0] != NULL ) lister[0]->messageHandler( msg );
  if ( lister[1] != NULL ) lister[1]->messageHandler( msg );
  delete msg;

  FieldListView::changeButtonFunction( FieldListView::SELECT_BUTTON, wconfig->getMouseSelectButton() );
  FieldListView::changeButtonFunction( FieldListView::ACTIVATE_BUTTON,
                                       wconfig->getMouseActivateButton(),
                                       wconfig->getMouseActivateMod() );
  FieldListView::changeButtonFunction( FieldListView::SCROLL_BUTTON,
                                       wconfig->getMouseScrollButton(),
                                       wconfig->getMouseScrollMod() );
  FieldListView::changeButtonFunction( FieldListView::ENTRY_PRESSED_MSG_BUTTON,
                                       wconfig->getMouseContextButton(),
                                       wconfig->getMouseContextMod() );
  FieldListView::changeSelectMode( ( wconfig->getMouseSelectMethod() == WConfig::MOUSECONF_ALT_MODE ) ? FieldListView::MOUSE_SELECT_ALT : FieldListView::MOUSE_SELECT_NORMAL );

  FieldListView::changeDelayedEntryPressedEvent( wconfig->getMouseContextMenuWithDelayedActivate() );

  mainwin->show();
}

void Worker::setStatebarText(const char *str)
{
    if ( statebar != NULL ) {
        m_statebar_text = str;
        updateStatebar();
    }
}

void Worker::shufflePath()
{
  pbanknr++;
  showPathBank();
}

void Worker::shuffleButton( int dir )
{
  shuffleButton( dir, false );
}

void Worker::shuffleButton( int dir, bool nocycle )
{
  int rows, columns, maxbank, oldbank;

  rows = wconfig->getRows();
  columns = wconfig->getColumns();
  List *buttonlist = wconfig->getButtons();
  maxbank = buttonlist->size() / ( rows * columns * 2 );

  oldbank = bbanknr;
  bbanknr += dir;
  if ( bbanknr < 0 ) {
    bbanknr = ( ( nocycle == false ) ? ( maxbank - 1 ) : 0 );
  } else if ( bbanknr >= maxbank ) {
    bbanknr = ( ( nocycle == false ) ? 0 : ( maxbank - 1 ) );
  }
  if ( oldbank != bbanknr ) showButtonBank();
}

void Worker::switchToButtonBank( int nr )
{
    int rows, columns, maxbank;

    rows = wconfig->getRows();
    columns = wconfig->getColumns();
    List *buttonlist = wconfig->getButtons();
    maxbank = buttonlist->size() / ( rows * columns * 2 );

    if ( nr < 0 || nr >= maxbank ) return;

    if ( nr != bbanknr ) {
        bbanknr = nr;
        showButtonBank();
    }
}

void Worker::showPathBank()
{
  int rows,id,maxbank;
  List *paths=wconfig->getPaths();
  rows=wconfig->getRows();
  maxbank=paths->size()/rows;
  if(pbanknr>=maxbank) pbanknr=0;
  id=paths->initEnum();
  WCPath *p1=(WCPath*)paths->getElementAt(id,pbanknr*rows);
  for(int i=0;i<rows;i++) {
    if( p1->getCheck() == true ) {
      pathbs[i]->setText(0,p1->getName());
      pathbs[i]->setFG(0,p1->getFG());
      pathbs[i]->setBG(0,p1->getBG());
      pathbs[i]->setText(1,p1->getName());
      pathbs[i]->setFG(1,p1->getFG());
      pathbs[i]->setBG(1,p1->getBG());
      pathbs[i]->activate(0);
    } else {
      pathbs[i]->setText(0,"");
      pathbs[i]->setFG(0,0);
      pathbs[i]->setBG(0,0);
      pathbs[i]->setText(1,"");
      pathbs[i]->setFG(1,0);
      pathbs[i]->setBG(1,0);
      pathbs[i]->deactivate(0);
    }
    p1=(WCPath*)paths->getNextElement(id);
  }
  paths->closeEnum(id);

  m_clockbar_dirty = true;
}

void Worker::showButtonBank()
{
  int rows,columns,id,maxbank;
  rows=wconfig->getRows();
  columns=wconfig->getColumns();
  List *buttonlist=wconfig->getButtons();
  maxbank=buttonlist->size()/(rows*columns*2);
  if(bbanknr>=maxbank) bbanknr=0;
  id=buttonlist->initEnum();
  WCButton *b1=(WCButton*)buttonlist->getElementAt(id,bbanknr*rows*columns*2);
  for(int i=0;i<rows;i++) {
    for(int j=0;j<columns;j++) {
      if( b1->getCheck() == true ) {
        buttons[i*columns+j]->setText(0,b1->getText());
        buttons[i*columns+j]->setFG(0,b1->getFG());
        buttons[i*columns+j]->setBG(0,b1->getBG());
        // only activate if this button has some commands
        if ( ! b1->getComs().empty() )
          buttons[i*columns+j]->activate(0);
        else
          buttons[i*columns+j]->deactivate(0);
      } else {
        buttons[i*columns+j]->setText(0,"");
        buttons[i*columns+j]->setFG(0,0);
        buttons[i*columns+j]->setBG(0,0);
        buttons[i*columns+j]->deactivate(0);
      }
      b1=(WCButton*)buttonlist->getNextElement(id);
      if(b1->getCheck()==true) {
        buttons[i*columns+j]->setDualState(true);
        buttons[i*columns+j]->setShowDual(true);
        buttons[i*columns+j]->setText(1,b1->getText());
        buttons[i*columns+j]->setFG(1,b1->getFG());
        buttons[i*columns+j]->setBG(1,b1->getBG());
        buttons[i*columns+j]->activate(1);
      } else {
        buttons[i*columns+j]->setDualState(false);
        buttons[i*columns+j]->setShowDual(false);
        buttons[i*columns+j]->deactivate(1);
      }
      b1=(WCButton*)buttonlist->getNextElement(id);
    }
  }
  buttonlist->closeEnum(id);

  if ( button_bank_slider != NULL ) {
      button_bank_slider->setMaxDisplay( 1 );
      button_bank_slider->setMaxLen( maxbank );
      button_bank_slider->setOffset( bbanknr );
  }

  m_clockbar_dirty = true;
}

void Worker::about()
{
    About ab( aguix, *this );

    ab.about();
}

bool Worker::configure( const std::string &panel )
{
  std::string oldsbt;
  bool erg;

  if ( wconfig->checkForNewerConfig() == true ) {
      std::string str1 = catalog.getLocale( 511 );
      str1 += "|";
      str1 += catalog.getLocale( 512 );
      erg = req_static->request( catalog.getLocale( 123 ),
                                 catalog.getLocale( 510 ),
                                 str1.c_str() );
      if ( erg == 0 ) {
          wconfig->load();
          wconfig->applyColorList(wconfig->getColors());
          wconfig->applyLanguage();
      }
  }

  oldsbt = statebar->getText( 0 );
  setStatebarText( catalog.getLocale( 595 ) );
  setWaitCursor();

  if ( ! panel.empty() ) {
      wconfig->setInitialCommand( m_pending_config_command );
  }

  erg = wconfig->configure( panel );

  setStatebarText( oldsbt.c_str() );
  unsetWaitCursor();

  // set restart to RECONFIG even of configure was canceled
  // some caches might be invalid
  restart = RS_RECONFIG;

  return erg;
}

Lister *Worker::getOtherLister(Lister *l1)
{
  if(l1==lister[0]) return lister[1];
  if(l1==lister[1]) return lister[0];
  return NULL;
}

int Worker::getSide(Lister *l1)
{
  if(l1==lister[0]) return 0;
  if(l1==lister[1]) return 1;
  return -1;
}

void Worker::setPath(int nr)
{
  List *paths=wconfig->getPaths();
  WCPath *p1=(WCPath*)paths->getElementAt(nr);
  if(p1!=NULL) {
    setPath( p1 );
  } else {
    printf("Unexpected error in Worker::setPath 1\n");
  }
}

void Worker::setPath( WCPath *p )
{
    if ( m_help_active ) {
        showHelp( lastkey, lastmod, {}, {}, { p } );
        return;
    }

    if ( p != NULL ) {
    if(p->getCheck()==true) {
      Lister *l1=lister[0];
      if(lister[1]->isActive()==true) l1=lister[1];

      if ( ! dynamic_cast< VirtualDirMode *>( l1->getActiveMode() ) ) {
          l1->switch2Mode( 0 );
      }

      char *tstr = Worker_replaceEnv( p->getPath() );

      if ( auto vdm = dynamic_cast< VirtualDirMode *>( l1->getActiveMode() ) ) {
          vdm->showDir( tstr );
      }

      _freesafe( tstr );
    }
  }
}

int Worker::interpret( const command_list_t &coms,ActionMessage *msg)
{
    std::shared_ptr< WPUContext > wpu;

  if ( ( msg == NULL ) ) return 1;

  if ( m_interpret_recursion_depth > m_interpret_max_recursion ) {
      if ( getRequester() != NULL ) {
          getRequester()->request( catalog.getLocale( 125 ), catalog.getLocale( 821 ), catalog.getLocale( 11 ) );
      } else {
          fprintf( stderr, "%s", catalog.getLocale( 821 ) );
      }
      return 1;
  }
  m_interpret_recursion_depth++;
  
  mainwin->forbidUserInput();
  usr1signal = 0;

  if ( ( coms.empty() ) && ( msg->filetype != NULL ) ) {
    // no action and filetype, so use list with parentaction
      command_list_t parentActionList = {};
      parentActionList.push_back( std::shared_ptr< FunctionProto >( new ParentActionOp() ) );
      wpu = std::make_shared< WPUContext >( this, parentActionList, msg );
  } else {
      wpu = std::make_shared< WPUContext >( this, coms, msg );
  }

  setCurrentWPU( wpu );

  while( wpu->next( wpu, msg ) == 0 ) {
    if ( usr1signal != 0 ) {
      //Command execution stopped by USR1 signal
      if ( getRequester() != NULL ) {
        getRequester()->request( catalog.getLocale( 124 ), catalog.getLocale( 503 ), catalog.getLocale( 11 ) );
      }
      usr1signal = 0;
      break;
    }
  }

  mainwin->permitUserInput();
  m_interpret_recursion_depth--;

  if ( m_interpret_recursion_depth == 0 ) {
      auto active_lister = getActiveLister();
      ListerMode *activemode = active_lister ? active_lister->getActiveMode() : nullptr;
      auto inactive_lister = getOtherLister( getActiveLister() );
      ListerMode *inactivemode = inactive_lister ? inactive_lister->getActiveMode() : nullptr;
      
      if ( activemode ) activemode->interpretFinalize();
      if ( inactivemode ) inactivemode->interpretFinalize();
  }

  return 0;
}

static bool isDoubleKeyTime( Time t2, Time t1 )
{
    if ( abs( (int)( t2 - t1 ) ) < ( 60 * 1000 ) ) return true;
    return false;
}

int Worker::activateShortkey(AGMessage *msg)
{
  int keystate;
  WCHotkey *hk1;
  WCButton *b1;
  WCPath *p1;
  ActionMessage amsg( this );
  bool found, keychain_cand = false;
  if((msg->type==AG_KEYPRESSED)||(msg->type==AG_KEYRELEASED)) {

      amsg.setKeyAction( true );

    if ( msg->type == AG_KEYPRESSED ) {
      // I will ignore here any modifier key because
      // it makes no sense (for me) to allow a single modifier press
      // release is still supported
      // reason for this is the double shortkey: e.g. ctrl-x
      // will send first "ctrl press" and then "ctrl-x press"
      // and this would lead to wrong results
      // other way could be to just dont save this modifier presses in lastkey
      if ( AGUIX::isModifier( msg->key.key ) == true )
        return 0;
    }

    keystate=KEYSTATEMASK(msg->key.keystate);
    found=false;

    removeStatebarText( m_key_message );

    /* procedure:
     * a previous key present?
     *   no: Check for normal key
     *     found: execute
     *     not found: store key as previous
     *   yes: Check for double key
     *     found: execute and clear previous
     *     not found: Check for normal key
     *       found: execute and clear previous
     *       not found: store key as previous
     */

    if ( lastkey != 0 && isDoubleKeyTime( msg->key.time, lastkey_time ) == true ) {
        if ( findkey( lastkey, lastmod, msg->key.key, keystate, &b1, &hk1, &p1 ) > 0 ) {
            if ( b1 != NULL ) {
                amsg.mode = amsg.AM_MODE_NORMAL;
                if ( m_help_active && ! checkCommandListForCommand( b1->getComs(), HelpOp::name ) ) {
                    showHelp( 0, 0, { b1 }, {}, {} );
                } else {
                    interpret( b1->getComs(), &amsg );
                }
                found = true;
            } else if ( hk1 != NULL ) {
                amsg.mode = amsg.AM_MODE_NORMAL;
                if ( m_help_active && ! checkCommandListForCommand( hk1->getComs(), HelpOp::name ) ) {
                    showHelp( 0, 0, {}, { hk1 }, {} );
                } else {
                    interpret( hk1->getComs(), &amsg );
                }
                found = true;
            } else if ( p1 != NULL ) {
                setPath( p1 );
                found = true;
            }
            lastkey = 0;
            lastmod = 0;
        }
    }
    if ( found == false ) {
        if ( findkey( msg->key.key, keystate, &b1, &hk1, &p1 ) > 0 ) {
            if ( b1 != NULL ) {
                amsg.mode = amsg.AM_MODE_NORMAL;
                if ( m_help_active && ! checkCommandListForCommand( b1->getComs(), HelpOp::name ) ) {
                    showHelp( msg->key.key, keystate, { b1 }, {}, {} );
                } else {
                    interpret( b1->getComs(), &amsg );
                }
                found = true;
            } else if ( hk1 != NULL ) {
                amsg.mode = amsg.AM_MODE_NORMAL;
                if ( m_help_active && ! checkCommandListForCommand( hk1->getComs(), HelpOp::name ) ) {
                    showHelp( msg->key.key, keystate, {}, { hk1 }, {} );
                } else {
                    interpret( hk1->getComs(), &amsg );
                }
                found = true;
            } else if ( p1 != NULL ) {
                setPath( p1 );
                found = true;
            }
            lastkey = 0;
            lastmod = 0;
        } else {
            lastkey = msg->key.key;
            lastmod = keystate;
            lastkey_time = msg->key.time;

            if ( isKeyChainPrefix( msg->key.key, keystate ) == true ) {
                keychain_cand = true;
            }
        }
    }

    setKeyChainCandidateActive( false );

    if ( found == false && keychain_cand == true ) {
        if ( m_help_active ) {
            std::list< WCButton * > buttons;
            std::list< WCHotkey * > hotkeys;
            std::list< WCPath * > paths;
            getNamesOfKeyChainCompletions( msg->key.key, keystate, &buttons, &hotkeys, &paths  );
            showHelp( lastkey, lastmod, buttons, hotkeys, paths );
        } else {
            char *tstr = aguix->getNameOfKey( msg->key.key, keystate );
            std::string key_info;
            if ( tstr != NULL ) {
                key_info = tstr;
                _freesafe( tstr );
            } else {
                key_info = "???";
            }
            key_info += " [ ";
            key_info += getNamesOfKeyChainCompletions( msg->key.key, keystate, NULL, NULL, NULL );
            key_info += " ]";
            m_key_message = TimedText( key_info,
                                       time( NULL ) + 1,
                                       time( NULL ) + 61 );
            updateStatebarText( m_key_message );

            setKeyChainCandidateActive( true );
        }
    }

    return ( found == true ) ? 1 : 0;
  }
  return 0;
}

Lister *Worker::getLister(int side)
{
  if((side<0)||(side>1)) return NULL;
  return lister[side];
}

Lister *Worker::getActiveLister()
{
  if(lister[0]->isActive()==true) return lister[0];
  if(lister[1]->isActive()==true) return lister[1];
  return NULL;
}

void Worker::activateButton( int pos )
{
    List *l1;
    WCButton *b1;
    int id;
    ActionMessage amsg( this );

    l1 = wconfig->getButtons();

    id = l1->initEnum();
    b1 = (WCButton*)l1->getElementAt( id, pos );
    if ( b1 != NULL ) {
        if ( b1->getCheck() == true ) {
            amsg.mode = amsg.AM_MODE_NORMAL;
            if ( m_help_active && ! checkCommandListForCommand( b1->getComs(), HelpOp::name ) ) {
                showHelp( 0, 0, { b1 }, {}, {} );
            } else {
                interpret( b1->getComs(),
                           &amsg );
            }
        }
    }
    l1->closeEnum( id );
}

void Worker::activateButton(int i,int j,int m)
{
  List *l1;
  WCButton *b1;
  int id,pos;
  int rows,columns;
  ActionMessage amsg( this );
  rows=wconfig->getRows();
  columns=wconfig->getColumns();
  l1=wconfig->getButtons();
  id=l1->initEnum();
  pos=2*(bbanknr*rows*columns+i*columns+j)+m-1;
  b1=(WCButton*)l1->getElementAt(id,pos);
  if(b1!=NULL) {
    if(b1->getCheck()==true) {
      amsg.mode=amsg.AM_MODE_NORMAL;
      if ( m_help_active && ! checkCommandListForCommand( b1->getComs(), HelpOp::name ) ) {
          showHelp( 0, 0, { b1 }, {}, {} );
      } else {
          interpret(b1->getComs(),&amsg);
      }
    }
  }
  l1->closeEnum(id);
}

void Worker::activateHotkey( int pos )
{
    List *l1;
    WCHotkey *hk1;
    int id;
    ActionMessage amsg( this );

    l1 = wconfig->getHotkeys();

    id = l1->initEnum();
    hk1 = (WCHotkey*)l1->getElementAt( id, pos );
    if ( hk1 != NULL ) {
        amsg.mode = amsg.AM_MODE_NORMAL;
      if ( m_help_active && ! checkCommandListForCommand( hk1->getComs(), HelpOp::name ) ) {
          showHelp( 0, 0, {}, { hk1 }, {} );
      } else {
          interpret( hk1->getComs(),
                     &amsg );
      }
    }
    l1->closeEnum( id );
}

int Worker::saveListerState()
{
  // Datei oeffnen ($HOME/.worker/listerstates)
  // int-Wert mit Seite (0/1)
  // Vielleicht: Die ListerModes nicht durchnumeriert laden sondern
  //             die ID speichern und dann dem entsprechenden Mode
  //             das laden ueberlassen, nach Seitennummer die Anzahl der
  //             der abgespeicherten Modis
  // Also der Lister-lade-funktion den FH geben und die macht dann den Rest

  std::string home = WorkerInitialSettings::getInstance().getConfigBaseDir();
  std::string str1;
  Datei *fh;

  if ( home.length() < 1 ) return false;
  
  str1 = home;
#ifdef USEOWNCONFIGFILES
  str1 = NWC::Path::join( str1, "lvconf-dev" );
#else
  str1 = NWC::Path::join( str1, "lvconf" );
#endif

  fh = new Datei();
  if ( fh->open( str1.c_str(), "w" ) == 0 ) {
    if ( lister[1]->getFocus() == true ) {
      fh->configPutPair( "activeside", "right" );
    } else {
      fh->configPutPair( "activeside", "left" );
    }
    fh->configOpenSection( "left" );
    lister[0]->saveState( fh );
    fh->configCloseSection(); //left
    fh->configOpenSection( "right" );
    lister[1]->saveState( fh );
    fh->configCloseSection(); //right

    if ( fh->errors() != 0 ) {
      //TODO: Requester zeigen?
      fprintf( stderr, "Worker:error while writing listview configuration (lvconf)\n" );
    }
    fh->close();
  }
  delete fh;
  return 0;
}

int Worker::initListerState()
{
  int pos,s;
  char *startd[2], *tstr;
  std::string home = WorkerInitialSettings::getInstance().getConfigBaseDir();
  std::string str1;
  FILE *fp;
  std::string cwd;
  int activeside;
  int found_error = 0;
  
  if ( home.length() < 1 ) return 1;

  str1 = home;
#ifdef USEOWNCONFIGFILES
  str1 = NWC::Path::join( str1, "lvconf-dev" );
#else
  str1 = NWC::Path::join( str1, "lvconf" );
#endif

  activeside = -1;
  fp = worker_fopen( str1.c_str(), "r" );
  if ( fp != NULL ) {
    yyrestart( fp );
    readtoken();
    for (;;) {
      if ( worker_token == ACTIVESIDE_WCP ) {
        readtoken();

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

        if ( worker_token == LEFT_WCP ) {
          activeside = 0;
        } else if ( worker_token == RIGHT_WCP ) {
          activeside = 1;
        } else {
          found_error = 1;
          break;
        }
        readtoken();

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

        if ( worker_token != LEFTBRACE_WCP ) {
          found_error = 1;
          break;
        }
        readtoken();
        
        if ( lister[0]->loadState() != 0 ) {
          found_error = 1;
          break;
        }
        
        if ( worker_token != RIGHTBRACE_WCP ) {
          found_error = 1;
          break;
        }
        readtoken();
      } else if ( worker_token == RIGHT_WCP ) {
        readtoken();

        if ( worker_token != LEFTBRACE_WCP ) {
          found_error = 1;
          break;
        }
        readtoken();
        
        if ( lister[1]->loadState() != 0 ) {
          found_error = 1;
          break;
        }
        
        if ( worker_token != RIGHTBRACE_WCP ) {
          found_error = 1;
          break;
        }
        readtoken();
      } else if ( worker_token != 0 ) {
        // parse error
        found_error = 1;
        break;
      } else break;  // end
    }
    if ( found_error != 0 ) {
      //TODO: Requester zeigen?
      //      Eigentlich kann der User eh nicht viel machen
      //      denn beim Beenden wird neue Datei geschrieben
      fprintf( stderr, "Worker:error in listview configuration (lvconf)\n" );
    }
    worker_fclose( fp );
  }

  if ( ( activeside == 0 ) || ( activeside == 1 ) ) {
    lister[activeside]->makeActive();
  }
  // TODO: Vielleicht nur diesen check, wenn Datei nicht gefunden oder beide auf -1
  int lmode=-1,rmode=-1;
  if(lister[0]->getActiveMode()!=NULL) {
    lmode=getID4Mode(lister[0]->getActiveMode());
  }
  if(lister[1]->getActiveMode()!=NULL) {
    rmode=getID4Mode(lister[1]->getActiveMode());
  }
  if((lmode!=0)&&(rmode!=0)) {
    lister[0]->switch2Mode( 0 );
    lister[1]->switch2Mode( 0 );
  }
  //now check for startdirs
  startd[0]=NULL;
  startd[1]=NULL;
  if ( m_optind >= 1 ) {
      pos = m_optind;
  } else {
      pos = 1;
  }
  for(s=0;s<2;s++) {
    for(;pos<argc;pos++) {
      if(strncmp(argv[pos],"-",1)!=0) {
        if(strncmp(argv[pos],"--",2)!=0) {
          startd[s]=dupstring(argv[pos]);
          break;
        }
      }
    }
    pos++;
  }

  cwd = NWC::OS::getCWD();

  for(s=0;s<2;s++) {

    getKVPStore().setStringValue( "initial-path-source", "arg" );

    if(startd[s]==NULL) {
      tstr=wconfig->getStartDir(s);
      if(tstr!=NULL) {
          startd[s]=dupstring(tstr);

          getKVPStore().setStringValue( "initial-path-source", "config" );
      }
    }
    if(startd[s]!=NULL) {
      if(strlen(startd[s])>0) {
        lister[s]->switch2Mode( 0 );
        ListerMode *lm1 = lister[s]->getActiveMode();

        if ( lm1 != NULL ) {
            tstr = NWC::Path::handlePathExt( startd[s] );

            if ( tstr[0] != '/' ) {
                str1 = cwd;
                str1 += "/";
                str1 += tstr;
            } else {
                str1 = tstr;
            }

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

            args.push_back( RefCount< ArgClass >( new StringArg( str1 ) ) );
            lm1->runCommand( "enter_dir", args );

            _freesafe(tstr);
        }
      }
      _freesafe(startd[s]);
    }
  }

  getKVPStore().eraseStringValue( "initial-path-source" );
  
  /* if no side is active make 0 active */
  if(getActiveLister()==NULL) lister[0]->makeActive();
  return 0;
}

int Worker::quit(int mode)
{
  int returnvalue=0;
  if(mode==1) {
    runningmode=WORKER_QUIT;
    returnvalue=1;
  } else {
    char *str;
    str=(char*)_allocsafe(strlen(catalog.getLocale(11))+1+strlen(catalog.getLocale(8))+1);
    sprintf(str,"%s|%s",catalog.getLocale(11),catalog.getLocale(8));
    int res=req_static->request(catalog.getLocale(114),catalog.getLocale(115),str);
    _freesafe(str);
    if(res==0) {
      runningmode=WORKER_QUIT;
      returnvalue=1;
    }
  }
  return returnvalue;
}

static std::string get_version_string()
{
    return AGUIXUtils::formatStringToString( "Worker %d.%d.%d%s",
                                             WORKER_MAJOR,
                                             WORKER_MINOR,
                                             WORKER_PATCH,
                                             WORKER_COMMENT );
}

static std::string get_free_memory_string()
{
#if defined HAVE_SYS_SYSINFO_H && defined HAVE_SYSINFO
    struct sysinfo info;
    unsigned long freeram, freeswap;
    std::string s1;

    sysinfo( &info );

    freeram = info.freeram / 1024;
    freeram *= info.mem_unit;
    freeswap = info.freeswap / 1024;
    freeswap *= info.mem_unit;

    s1 = AGUIXUtils::formatStringToString( "%s: %lu KB  -  %s: %lu KB",
                                           catalog.getLocale( 105 ), freeram,
                                           catalog.getLocale( 106 ), freeswap );

    return s1;
#else
    return "";
#endif
}

void Worker::updateTime(void)
{
#if defined HAVE_SYS_SYSINFO_H && defined HAVE_SYSINFO
  struct sysinfo info;
  unsigned long freeram,freeswap;
#endif
  time_t timep;
  int dt=wconfig->getClockbarUpdatetime();
  WConfig::clockbar_mode_t cbm=wconfig->getClockbarMode();
  std::string final_line;

  bool hint_valid = false;

  time(&timep);

  if ( wconfig->getClockbarHints() == true &&
       timep - m_hint_time <= HINT_VALID_TIME &&
       ! m_current_hint.empty() ) {
      hint_valid = true;
  }

  if ( cbm == WConfig::CLOCKBAR_MODE_VERSION ) {
      setClockUpdate( 0 );
  } else {
      setClockUpdate( dt * 1000 );
  }

  bool do_update = false;

  if ( cbm == WConfig::CLOCKBAR_MODE_CUSTOM &&
       m_clockbar_dirty ) {
      do_update = true;
  } else if ( lasttimep == 0 ) {
      do_update = true;
  } else if ( lasttimep + dt <= timep &&
              cbm != WConfig::CLOCKBAR_MODE_VERSION ) {
      do_update = true;
  }
  
  if ( do_update ) {
    if(cbm==WConfig::CLOCKBAR_MODE_VERSION) {
      final_line = get_version_string();
    } else if(cbm==WConfig::CLOCKBAR_MODE_EXTERN) {
      char *buffer = NULL;

      auto clbc = wconfig->getClockbarCommand();
      if ( ! clbc.empty() ) {
        // start the program
        ExeClass *myexe = new ExeClass();
        myexe->addCommand( "%s", clbc.c_str() );
        buffer = myexe->getOutput();
        if ( buffer != NULL ) {
          int tlen;
	  tlen = aguix->getStrlen4Width( buffer, clockbar->getWidth() - 10, NULL );
          if ( (int)strlen( buffer ) > tlen ) {
            buffer[tlen] = '\0';
          }
        }
        delete myexe;
      } else buffer=dupstring("");
      
      if ( hint_valid ) {
          final_line = m_current_hint;
      } else {
          final_line = buffer;
      }

      _freesafe( buffer );
    } else if ( cbm == WConfig::CLOCKBAR_MODE_CUSTOM ) {
        std::string s;

        if ( hint_valid ) {
            s = wconfig->getClockbarCustom( 1 );
            if ( s.empty() ) {
                s = wconfig->getClockbarCustom( 0 );
            }
        } else {
            s = wconfig->getClockbarCustom( 0 );
        }

        if ( ! m_clockbar_flag_replacer ) {
            initClockbarFlagReplacer();
        }

        final_line = m_clockbar_flag_replacer->replaceFlags( s, false );
    } else { // _TIME or _TIMERAM
      char *str;
      struct tm *timeptr;

      timeptr=localtime(&timep);
      str=asctime(timeptr);
      str[strlen(str)-1]=0;

      final_line = str;

      if ( hint_valid ) {
          final_line += " -- ";

          final_line += m_current_hint;
       } else {
#if defined HAVE_SYS_SYSINFO_H && defined HAVE_SYSINFO
      if(cbm==WConfig::CLOCKBAR_MODE_TIMERAM) {
        std::string s1;

        sysinfo(&info);
        freeram = info.freeram / 1024;
        freeram *= info.mem_unit;
        freeswap = info.freeswap / 1024;
        freeswap *= info.mem_unit;
        
        s1 = AGUIXUtils::formatStringToString( "  --  %s: %lu KB  -  %s: %lu KB",
          catalog.getLocale( 105 ), freeram,
          catalog.getLocale( 106 ), freeswap );

        final_line += s1;
      }
#endif
      }
    }

    clockbar->setText( 0, final_line.c_str() );
    clockbar->setText( 1, final_line.c_str() );
    lasttimep=timep;
  }

  m_clockbar_dirty = false;
}

void Worker::checkfirststart( char **setlang )
{
  bool copyconfig=false;
  const char *textstr,*buttonstr;
  int erg;
  bool firststart = false;
  std::string file;

  std::string home = WorkerInitialSettings::getInstance().getConfigBaseDir();
  if ( home.length() < 1 ) {
    textstr="There is no HOME variable. Please set it to your home and retry.";
    buttonstr="Ok";
    req_static->request( "Worker warning", textstr, buttonstr );
  } else {
#ifdef USEOWNCONFIGFILES
    file = NWC::Path::join( home, "wconfig2" );
#else
    file = NWC::Path::join( home, "config" );
#endif
    if ( Datei::fileExists( file.c_str() ) == false ) {
      firststart = true;
    
      textstr="It seems that Worker is started the first time!|I will try to install an example configuration in %s|but I will not overwrite any existing file!||If you choose \"Skip\", Worker will use a simple builtin configuration\
|but then not all functions are accessible (without reconfiguration)!";
      std::string text = AGUIXUtils::formatStringToString( textstr, home.c_str() );

      buttonstr="Ok|Skip";
      erg = req_static->request( "Worker message", text.c_str(), buttonstr );
      if(erg==0) copyconfig=true;
    }
    if(copyconfig==true) {
      std::string catdir, str2, str3;
      std::string chosen_lang;

      chosen_lang = queryLanguage();

      catdir = home;
      if ( Datei::fileExists( catdir.c_str() ) == false ) {
        worker_mkdir( catdir.c_str(), 0700 );
      }

      catdir = NWC::Path::join( home, "catalogs" );
      if ( Datei::fileExists( catdir.c_str() ) == false ) {
        worker_mkdir( catdir.c_str(), 0700 );
      }

      if ( Worker::getDataDir().length() > 0 ) {
        str2 = Worker::getDataDir();
        str2 += "/config-";
        str2 += chosen_lang;
        if ( UTF8::getUTF8Mode() != UTF8::UTF8_DISABLED && UTF8::getCurrentEncoding() == UTF8::ENCODING_UTF8 ) {
          str2 += ".utf8";
        }
      } else str2 = "";
      
      if ( Datei::fileExists( str2.c_str() ) == false ) {
        // choosed language has no default config so take the english
        // and set the language later
        if ( Worker::getDataDir().length() > 0 ) {
          str2 = Worker::getDataDir();
          str2 += "/config-english";
        } else str2 = "";
        
        if ( setlang != NULL ) {
          // if not english remember to set the language later
          if ( chosen_lang != "english" )
            *setlang = dupstring( chosen_lang.c_str() );
        }
      }
      
      if(Datei::fileExists( str2.c_str() )==true) {
        char *tstr1, *tstr2, *tstr3;
        tstr1 = AGUIX_catTrustedAndUnTrusted( "cp ", str2.c_str() );
        tstr2 = catstring( tstr1, " " );
        tstr3 = AGUIX_catTrustedAndUnTrusted( tstr2, file.c_str() );
        int tres __attribute__((unused)) = system( tstr3 );
        _freesafe( tstr1 );
        _freesafe( tstr2 );
        _freesafe( tstr3 );
        if ( Datei::fileExists( file.c_str() ) == false ) {
          textstr = "The configuration file couldn't be copyied!";
          buttonstr="Ok";
          req_static->request( "Worker error", textstr, buttonstr );
        } else {
          textstr="Installation finished.";
          buttonstr="Ok";
          req_static->request( "Worker message", textstr, buttonstr );
        }
      } else {
        str3 = "There is no example configuration at " + str2 + "!|Perhaps worker is not completely installed!";
        buttonstr="Ok";
        req_static->request( "Worker error", str3.c_str(), buttonstr );
      }
    }
  }
  if ( firststart == true ) about();
}

void Worker::setTitle(const char *add_infos)
{
  if(add_infos==NULL) {
    if ( isRoot == false )
      mainwin->setTitle("Worker");
    else
      mainwin->setTitle( "rootWorker" );
  } else {
    char *tstr;
    
    if ( isRoot == false )
      tstr=catstring("Worker - ",add_infos);
    else
      tstr=catstring("rootWorker - ",add_infos);
    mainwin->setTitle(tstr);
    _freesafe(tstr);
  }
}

Requester *Worker::getRequester()
{
  if ( req_static == NULL ) {
    req_static = new Requester( aguix );
  }
  return req_static;
}

static std::string getNameOfXthKey( AGUIX *aguix, List *dkeys, int key_nr )
{
    int id_k;
    WCDoubleShortkey *dk;
    std::string res = "";

    if ( ! dkeys ) return "";

    id_k = dkeys->initEnum();
    dk = (WCDoubleShortkey*)dkeys->getFirstElement( id_k );

    while ( dk != NULL && key_nr > 0 ) {
        dk = (WCDoubleShortkey*)dkeys->getNextElement( id_k );
        key_nr--;
    }

    if ( dk ) {
        res = dk->getName( aguix );
    }

    dkeys->closeEnum( id_k );

    return res;
}

int Worker::buildShortkeyLV( FieldListView *lv,
                             List *sklist )
{
    int id;
    struct shortkeylisttype *sk1;
    const char *name1, *name2;
    int row;
    List *dkeys;
    int id_k;
    WCDoubleShortkey *dk;
    int used_cols, cur_col;
    std::string str1;

    used_cols = lv->getNrOfFields();

    lv->setSize( sklist->size() );

    // add the shortkeys
    id = sklist->initEnum();
    sk1 = (struct shortkeylisttype*)sklist->getFirstElement( id );
    row = 0;
    while ( sk1 != NULL ) {
        dkeys = NULL;
        name2 = NULL;
        name1 = NULL;
        if ( sk1->b != NULL ) {
            dkeys = sk1->b->getDoubleKeys();
            name1 = catalog.getLocale( 389 );
            name2 = sk1->b->getText();
        } else if ( sk1->h != NULL ) {
            dkeys = sk1->h->getDoubleKeys();
            name1 = catalog.getLocale( 391 );
            name2 = sk1->h->getName();
        } else if ( sk1->p != NULL ) {
            dkeys = sk1->p->getDoubleKeys();
            name1 = catalog.getLocale( 390 );
            name2 = sk1->p->getName();
        }
        if ( dkeys != NULL && name2 != NULL ) {
            lv->setText( row, 0, name1 );
            lv->setText( row, 1, name2 );
            id_k = dkeys->initEnum();
            dk = (WCDoubleShortkey*)dkeys->getFirstElement( id_k );
            cur_col = 2;
            int key_number = 0;
            int last_col_set = cur_col - 1;

            while ( dk != NULL ) {
                key_number++;
                if ( cur_col >= used_cols ) {
                    lv->setNrOfFields( cur_col + 1 );

                    std::string column_name = AGUIXUtils::formatStringToString( catalog.getLocale( 935 ), key_number );

                    lv->setFieldText( cur_col, column_name );
                    used_cols = cur_col + 1;
                }
                if ( cur_col > 2 ) {
                    // not the first key
                    lv->setText( row, cur_col - 1, "," );
                }

                str1 = dk->getName( aguix );

                lv->setText( row, cur_col, str1 );
                last_col_set = cur_col;

                dk = (WCDoubleShortkey*)dkeys->getNextElement( id_k );
                cur_col += 2;
            }
            dkeys->closeEnum( id_k );

            while ( last_col_set < used_cols - 1 ) {
                lv->setText( row, ++last_col_set, "" );
            }

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

        sk1 = (struct shortkeylisttype*)sklist->getNextElement( id );
        row++;
    }
    sklist->closeEnum( id );
    
    return 0;
}

void Worker::activateShortkeyFromList()
{
  // 1.alle moeglichen Shortkeys finden und in eine Liste packen
  // 2.Diese Liste sortieren
  //   Hotkey<Button<Pfad   (Hotkey zuerst, weil man die am schnellsten vergisst)
  //   Unter gleichem Typ einfach nach Name case-ins sortieren
  // 3.Aus dieser Liste wird nun das LV erstellt:
  //   <Typ>:<Name>   <Shortkey>
  //   Dabei <Typ>:<Name> maximieren, damit Shortkey ganz rechts steht
  //   Achtung: Vielleicht fuer das LV den Font des linken LVs nehmen, falls der User doch mal
  //            einen nichtpro. Font einstellt, dann klappt das auch hier
  // 4.lv Horizontal maximieren
  // 5.lv->takeFocus(), damit man das ganze auch einfach mit der Tastatur benutzen kann
  // 6.der User waehlt nun aus
  // 7.Bei Abbruch nichts machen
  // 8.Bei Okay/Return/Doppelklick dann diesen Shortkey aktivieren

  List *sks;
  struct shortkeylisttype *sk1;
  int id;
  List *l1;
  WCHotkey *hk1;
  WCButton *b1;
  WCPath *p1;
  ActionMessage amsg( this );
  AWindow *win;
  int tw,l;
  AGMessage *msg;
  int ende=0;
  std::string str1;
  int cur_sortmode;

  sks=new List();
  l1=wconfig->getHotkeys();
  id=l1->initEnum();
  hk1=(WCHotkey*)l1->getFirstElement(id);
  while(hk1!=NULL) {
    if ( hk1->getDoubleKeys()->size() > 0 ) {
      sk1=(struct shortkeylisttype*)_allocsafe(sizeof(struct shortkeylisttype));
      sk1->h=hk1;
      sk1->b=NULL;
      sk1->p=NULL;
      sks->addElement(sk1);
    }
    hk1=(WCHotkey*)l1->getNextElement(id);
  }
  l1->closeEnum(id);

  l1=wconfig->getButtons();
  id=l1->initEnum();
  b1=(WCButton*)l1->getFirstElement(id);
  while(b1!=NULL) {
    if ( b1->getDoubleKeys()->size() > 0 ) {
      sk1=(struct shortkeylisttype*)_allocsafe(sizeof(struct shortkeylisttype));
      sk1->h=NULL;
      sk1->b=b1;
      sk1->p=NULL;
      sks->addElement(sk1);
    }
    b1=(WCButton*)l1->getNextElement(id);
  }
  l1->closeEnum(id);

  l1=wconfig->getPaths();
  id=l1->initEnum();
  p1=(WCPath*)l1->getFirstElement(id);
  while(p1!=NULL) {
    if ( p1->getDoubleKeys()->size() > 0 ) {
      sk1=(struct shortkeylisttype*)_allocsafe(sizeof(struct shortkeylisttype));
      sk1->h=NULL;
      sk1->b=NULL;
      sk1->p=p1;
      sks->addElement(sk1);
    }
    p1=(WCPath*)l1->getNextElement(id);
  }
  l1->closeEnum(id);
  
  // sorting
  cur_sortmode = 0;
  sks->sort( Worker::shortkeysort, cur_sortmode );
  
  // create window
  win = new AWindow( aguix, 10, 10, 20, 20, catalog.getLocale( 387 ), AWindow::AWINDOW_DIALOG );
  win->create();

  AContainer *ac1 = win->setContainer( new AContainer( win, 1, 3 ), true );
  ac1->setMinSpace( 5 );
  ac1->setMaxSpace( 5 );
    
  ac1->add( new Text( aguix, 0, 0, catalog.getLocale( 388 ) ),
            0, 0, AContainer::CO_INCWNR );
  
  FieldListView *lv = (FieldListView*)ac1->add( new FieldListView( aguix,
                                                                   0, 0,
                                                                   50, 400, 0 ),
                                                0, 1, AContainer::CO_MIN );
  lv->setAcceptFocus( true );
  lv->setDisplayFocus( true );
  lv->setNrOfFields( 2 );
  lv->setGlobalFieldSpace( 5 );
  lv->setShowHeader( true );
  lv->setFieldText( 0, catalog.getLocale( 936 ) );
  lv->setFieldText( 1, catalog.getLocale( 937 ) );

  buildShortkeyLV( lv, sks );
  
  lv->setSortHint( 0, FieldListView::HEADER_SORT_HINT_DOWN );

  lv->setHBarState(2);
  lv->setVBarState(2);
  lv->maximizeX();
  tw=lv->getWidth();

  int rx, ry, rw, rh;

  aguix->getLargestDimensionOfCurrentScreen( &rx, &ry,
                                             &rw, &rh );

  if ( tw > ( rw * 90 / 100 ) ) {
    lv->resize( rw * 90 / 100, lv->getHeight() );
  }
  ac1->readLimits();

  AContainer *ac1_2 = ac1->add( new AContainer( win, 2, 1 ), 0, 2 );
  ac1_2->setMinSpace( 5 );
  ac1_2->setMaxSpace( -1 );
  ac1_2->setBorderWidth( 0 );
  Button *okb =(Button*)ac1_2->add( new Button( aguix,
                                                0,
                                                0,
                                                catalog.getLocale( 392 ),
                                                0 ), 0, 0, AContainer::CO_FIX );
  Button *cb = (Button*)ac1_2->add( new Button( aguix,
						0,
						0,
						catalog.getLocale( 8 ),
						0 ), 1, 0, AContainer::CO_FIX );
  
  okb->takeFocus();
  win->setDoTabCycling( true );
  win->contMaximize( true );
  win->show();

  lv->takeFocus();

  while((msg=aguix->GetMessage(NULL))!=NULL) aguix->ReplyMessage(msg);
  while(ende==0) {
    msg=aguix->WaitMessage(win);
    if(msg!=NULL) {
      if(msg->type==AG_CLOSEWINDOW) {
        if(msg->closewindow.window==win->getWindow()) ende=-1;
      } else if(msg->type==AG_BUTTONCLICKED) {
        if(msg->button.button==okb) ende=1;
        else if(msg->button.button==cb) ende=-1;
      } else if(msg->type==AG_KEYPRESSED) {
        if ( win->isParent( msg->key.window, false ) == true ) {
          if((msg->key.key==XK_Return)||
             (msg->key.key==XK_KP_Enter)) {
            if ( cb->getHasFocus() == false ) {
              ende=1;
            }
          } else if(msg->key.key==XK_Escape) {
            ende=-1;
          }
        }
      } else if ( msg->type == AG_FIELDLV_DOUBLECLICK ) {
          if ( msg->fieldlv.lv == lv ) {
              ende = 1;
          }
      } else if ( msg->type == AG_FIELDLV_HEADERCLICKED ) {
          if ( msg->fieldlv.row <= 2 || ( msg->fieldlv.row % 2 ) == 0 ) {
              if ( cur_sortmode == msg->fieldlv.row ) {
                  cur_sortmode |= 0x80000000;
              } else {
                  cur_sortmode = msg->fieldlv.row;
              }
              sks->sort( Worker::shortkeysort, cur_sortmode );
              buildShortkeyLV( lv, sks );

              lv->setSortHint( msg->fieldlv.row, cur_sortmode & 0x80000000 ? FieldListView::HEADER_SORT_HINT_UP : FieldListView::HEADER_SORT_HINT_DOWN );

              lv->redraw();
          }
      }
      aguix->ReplyMessage(msg);
    }
  }
  l=-1;
  if(ende==1) {
    l = lv->getActiveRow();
  }
  delete win;

  if(l>=0) {
    sk1=(struct shortkeylisttype*)sks->getElementAt(l);
    if(sk1!=NULL) {
      amsg.mode=amsg.AM_MODE_NORMAL;
      if(sk1->h!=NULL) {
        interpret(sk1->h->getComs(),&amsg);
      } else if(sk1->b!=NULL) {
        interpret(sk1->b->getComs(),&amsg);
      } else if(sk1->p!=NULL) {
        setPath(wconfig->getPaths()->getIndex(sk1->p));
      }
    }
  }
  id=sks->initEnum();
  sk1=(struct shortkeylisttype*)sks->getFirstElement(id);
  while(sk1!=NULL) {
    _freesafe(sk1);
    sks->removeFirstElement();
    sk1=(struct shortkeylisttype*)sks->getFirstElement(id);    
  }
  sks->closeEnum(id);
  delete sks;
}

int Worker::shortkeysort( void *p1, void *p2, int sortmode )
{
    struct shortkeylisttype *sk1 = (struct shortkeylisttype*)p1;
    struct shortkeylisttype *sk2 = (struct shortkeylisttype*)p2;
    const char *str1, *str2;
    List *dkeys1, *dkeys2;
    int name_cmp;
    int type_cmp;
    int inverse = 1;

    if ( sk1->h == NULL && sk1->b == NULL && sk1->p == NULL ) return 0;
    if ( sk2->h == NULL && sk2->b == NULL && sk2->p == NULL ) return 0;

    if ( sortmode & 0x80000000 ) {
        inverse = -1;
        sortmode &= 0x7fffffff;
    }

    str1 = str2 = NULL;
    dkeys1 = dkeys2 = NULL;
    if ( sk1->h != NULL ) {
        str1 = sk1->h->getName();
        dkeys1 = sk1->h->getDoubleKeys();
    } else if ( sk1->b != NULL ) {
        str1 = sk1->b->getText();
        dkeys1 = sk1->b->getDoubleKeys();
    } else if ( sk1->p != NULL ) {
        str1 = sk1->p->getName();
        dkeys1 = sk1->p->getDoubleKeys();
    }
    if ( sk2->h != NULL ) {
        str2 = sk2->h->getName();
        dkeys2 = sk2->h->getDoubleKeys();
    } else if ( sk2->b != NULL ) {
        str2 = sk2->b->getText();
        dkeys2 = sk2->b->getDoubleKeys();
    } else if ( sk2->p != NULL ) {
        str2 = sk2->p->getName();  
        dkeys2 = sk2->p->getDoubleKeys();
    }

    if ( str1 != NULL && str2 != NULL ) {
        name_cmp = strcasecmp( str1, str2 );
    } else {
        name_cmp = 0;
    }

    type_cmp = 0;

    // hotkey is smaller than button and path
    if ( sk1->h != NULL && sk2->h == NULL ) type_cmp = -1;

    // button is smaller than path but larger than hotkey
    if ( sk1->b != NULL ) {
        if ( sk2->h != NULL ) type_cmp = 1;
        if ( sk2->p != NULL ) type_cmp = -1;
    }

    // path is larger than hotkey and button
    if ( sk1->p != NULL && sk2->p == NULL ) type_cmp = 1;
  
    switch ( sortmode ) {
        case 0:
            if ( type_cmp == 0 ) return inverse * name_cmp;
            else return inverse * type_cmp;
            break;
        case 1:
            return inverse * name_cmp;
            break;
        default:
            {
                int key_nr = ( sortmode - 2 ) / 2;

                const std::string keystr1 = getNameOfXthKey( aguix, dkeys1, key_nr );
                const std::string keystr2 = getNameOfXthKey( aguix, dkeys2, key_nr );

                if ( keystr1 == keystr2 ) return 0;

                if ( keystr1.empty() ) return inverse * 1;

                if ( keystr2.empty() ) return inverse * -1;

                if ( keystr1 < keystr2 ) return inverse * -1;

                return inverse * 1;
            }
            break;
    }

    return 0;
}

int Worker::PS_readSpace( const char*name )
{
  return freesp->readSpace( name );
}

loff_t Worker::PS_getBlocksize()
{
  return freesp->getBlocksize();
}

loff_t Worker::PS_getFreeSpace()
{
  return freesp->getFreeSpace();
}

loff_t Worker::PS_getSpace()
{
  return freesp->getSpace();
}

std::string Worker::PS_getFreeSpaceH() const
{
  return freesp->getFreeSpaceH();
}

std::string Worker::PS_getSpaceH() const
{
  return freesp->getSpaceH();
}

void Worker::buildtable()
{
  int i, id, id_dk;
  List *l1;
  List *dkeys;
  WCHotkey *hk1;
  WCButton *b1;
  WCPath *p1;
  WCDoubleShortkey *dk;

  if ( keyhashtable != NULL )
    deletetable();
  keyhashtable = new keyhash*[ keyhashtable_size ];
  
  for ( i = 0; i < keyhashtable_size; i++ ) {
    keyhashtable[i] = NULL;
  }

  l1 = wconfig->getHotkeys();
  id = l1->initEnum();
  hk1 = (WCHotkey*)l1->getFirstElement( id );
  while ( hk1 != NULL ) {
    dkeys = hk1->getDoubleKeys();
    id_dk = dkeys->initEnum();
    dk = (WCDoubleShortkey*)dkeys->getFirstElement( id_dk );
    while ( dk != NULL ) {
      insertkey( dk->getKeySym( 0 ), dk->getMod( 0 ), NULL, hk1, NULL );
      dk = (WCDoubleShortkey*)dkeys->getNextElement( id_dk );
    }
    dkeys->closeEnum( id_dk );
    hk1 = (WCHotkey*)l1->getNextElement( id );
  }
  l1->closeEnum( id );

  l1 = wconfig->getButtons();
  id = l1->initEnum();
  b1 = (WCButton*)l1->getFirstElement( id );
  while ( b1 != NULL ) {
    dkeys = b1->getDoubleKeys();
    id_dk = dkeys->initEnum();
    dk = (WCDoubleShortkey*)dkeys->getFirstElement( id_dk );
    while ( dk != NULL ) {
      insertkey( dk->getKeySym( 0 ), dk->getMod( 0 ), b1, NULL, NULL );
      dk = (WCDoubleShortkey*)dkeys->getNextElement( id_dk );
    }
    dkeys->closeEnum( id_dk );
    b1 = (WCButton*)l1->getNextElement( id );
  }
  l1->closeEnum( id );

  l1 = wconfig->getPaths();
  id = l1->initEnum();
  p1 = (WCPath*)l1->getFirstElement( id );
  while ( p1 != NULL ) {
    dkeys = p1->getDoubleKeys();
    id_dk = dkeys->initEnum();
    dk = (WCDoubleShortkey*)dkeys->getFirstElement( id_dk );
    while ( dk != NULL ) {
      insertkey( dk->getKeySym( 0 ), dk->getMod( 0 ), NULL, NULL, p1 );
      dk = (WCDoubleShortkey*)dkeys->getNextElement( id_dk );
    }
    dkeys->closeEnum( id_dk );
    p1 = (WCPath*)l1->getNextElement( id );
  }
  l1->closeEnum( id );
}

void Worker::deletetable()
{
  int i, j;
  keyhash *kh, *kh2;
  
  if ( keyhashtable != NULL ) {
    for ( i = 0; i < keyhashtable_size; i++ ) {
      kh = keyhashtable[i];
      j = 0;
      while ( kh != NULL ) {
        kh2 = kh->next;
        delete kh;
        kh = kh2;
        j++;
      }
#ifdef WANT_KEYHASHSTAT
      if ( j > 0 ) printf( "keyhashtable[%d] used %d entries\n", i, j );
#endif
    }
  
    delete [] keyhashtable;
    keyhashtable = NULL;
  }
}

unsigned int Worker::keyhashfunc( KeySym k, unsigned int m )
{
  unsigned long hashv;
  
  hashv = k;
  hashv += m << 16;
  return ( hashv % keyhashtable_size );
}

void Worker::insertkey( KeySym k, unsigned int m, WCButton *b, WCHotkey *hk, WCPath *p )
{
  unsigned int hashvalue;
  keyhash *kh, *kh2;

  if ( ( b == NULL ) && ( hk == NULL ) && ( p == NULL ) ) return;
  if ( k == 0 ) return;
  if ( keyhashtable == NULL ) return;
  
  kh2 = new keyhash;
  kh2->next = NULL;
  kh2->b = b;
  kh2->h = hk;
  kh2->p = p;

  hashvalue = keyhashfunc( k, m );
  kh = keyhashtable[ hashvalue ];
  if ( kh == NULL ) {
    keyhashtable[ hashvalue ] = kh2;
  } else {
    while ( kh->next != NULL ) kh = kh->next;
    kh->next = kh2;
  }
}

int Worker::findkey( KeySym k, unsigned int m, WCButton **b, WCHotkey **hk, WCPath **p )
{
  unsigned int hashvalue;
  keyhash *kh;
  int returnvalue = 0;

  if ( k == 0 ) return returnvalue;
  if ( keyhashtable == NULL ) return returnvalue;
  
  hashvalue = keyhashfunc( k, m );
  kh = keyhashtable[ hashvalue ];

  if ( b != NULL ) *b = NULL;
  if ( hk != NULL ) *hk = NULL;
  if ( p != NULL ) *p = NULL;

  if ( kh != NULL ) {
    while ( kh != NULL ) {
      if ( kh->b != NULL ) {
        if ( kh->b->hasKey( k, m ) == true ) {
          if ( b != NULL ) *b = kh->b;
          returnvalue = 1;
          break;
        }
      } else if ( kh->h != NULL ) {
        if ( kh->h->hasKey( k, m ) == true ) {
          if ( hk != NULL ) *hk = kh->h;
          returnvalue = 1;
          break;
        }
      } else if ( kh->p != NULL ) {
        if ( kh->p->hasKey( k, m ) == true ) {
          if ( p != NULL ) *p = kh->p;
          returnvalue = 1;
          break;
        }
      }
      kh = kh->next;
    }
  }

  return returnvalue;
}

int Worker::findkey( KeySym k1, unsigned int m1, KeySym k2, unsigned int m2, WCButton **b, WCHotkey **hk, WCPath **p )
{
  unsigned int hashvalue;
  keyhash *kh;
  int returnvalue = 0;

  if ( ( k1 == 0 ) || ( k2 == 0 ) ) return returnvalue;
  if ( keyhashtable == NULL ) return returnvalue;
  
  hashvalue = keyhashfunc( k1, m1 );
  kh = keyhashtable[ hashvalue ];

  if ( b != NULL ) *b = NULL;
  if ( hk != NULL ) *hk = NULL;
  if ( p != NULL ) *p = NULL;

  if ( kh != NULL ) {
    while ( kh != NULL ) {
      if ( kh->b != NULL ) {
        if ( kh->b->hasKey( k1, m1, k2, m2 ) == true ) {
          if ( b != NULL ) *b = kh->b;
          returnvalue = 1;
          break;
        }
      } else if ( kh->h != NULL ) {
        if ( kh->h->hasKey( k1, m1, k2, m2 ) == true ) {
          if ( hk != NULL ) *hk = kh->h;
          returnvalue = 1;
          break;
        }
      } else if ( kh->p != NULL ) {
        if ( kh->p->hasKey( k1, m1, k2, m2 ) == true ) {
          if ( p != NULL ) *p = kh->p;
          returnvalue = 1;
          break;
        }
      }
      kh = kh->next;
    }
  }

  return returnvalue;
}

bool Worker::isKeyChainPrefix( KeySym k, unsigned int m )
{
    unsigned int hashvalue;
    keyhash *kh;
    bool hit = false;

    if ( k == 0 ) return false;
    if ( keyhashtable == NULL ) return false;
    
    hashvalue = keyhashfunc( k, m );
    kh = keyhashtable[ hashvalue ];
    
    while ( kh != NULL ) {
        if ( kh->b != NULL ) {
            if ( kh->b->isKeyChainPrefix( k, m ) == true ) {
                hit = true;
                break;
            }
        } else if ( kh->h != NULL ) {
            if ( kh->h->isKeyChainPrefix( k, m ) == true ) {
                hit = true;
                break;
            }
        } else if ( kh->p != NULL ) {
            if ( kh->p->isKeyChainPrefix( k, m ) == true ) {
                hit = true;
                break;
            }
        }
        kh = kh->next;
    }
    return hit;
}

void Worker::closeMainWin()
{
  unsigned int i, j;

  if ( m_worker_menu != NULL ) {
      delete m_worker_menu;
      m_worker_menu = NULL;
  }

  delete req_static;
  req_static = new Requester( aguix );

  delete listerwin[0];
  listerwin[0] = NULL;
  delete listerwin[1];
  listerwin[1] = NULL;

  if ( buttons != NULL ) {
    if ( ( curRows != 0 ) && ( curColumns != 0 ) ) {
      for ( i = 0; i < curRows; i++ ) {
        for( j = 0; j < curColumns; j++ ) {
          mainwin->remove(buttons[ i * curColumns + j ]);
          delete buttons[ i * curColumns + j ];
          buttons[ i * curColumns + j ] = NULL;
        }
      }
    }
    delete [] buttons;
    buttons = NULL;
  }
  if ( pathbs != NULL ) {
    if ( ( curRows != 0 ) && ( curColumns != 0 ) ) {
      for ( i = 0; i < curRows; i++ ) {
        mainwin->remove( pathbs[i] );
        delete pathbs[ i ];
        pathbs[ i ] = NULL;
      }
    }
    delete [] pathbs;
    pathbs = NULL;
  }
  delete mainwin;
  mainwin = NULL;
}

void Worker::setWaitCursor()
{
  if ( waitcursorcount == 0 ) {
    getMainWin()->setCursor( AGUIX::WAIT_CURSOR );
    aguix->Flush();
  }
  waitcursorcount++;
}

void Worker::unsetWaitCursor()
{
  if ( waitcursorcount > 0 ) {
    waitcursorcount--;
    if ( waitcursorcount == 0 ) getMainWin()->unsetCursor();
  }
}

void Worker::PS_setLifetime( double t )
{
  freesp->setLifetime( t );
}

/*
 * static getDatatDir()
 *
 * will return the basedir of the worker share files
 * or empty string if not found
 *
 * search order is:
 * 1.PREFIX/share/worker
 * 2.`dirname $0`/../share/worker
 *   (if worker is in a bin-directory
 * 3./usr/share/worker
 * 4./usr/local/share/worker
 * 5.$HOME/share/worker
 *
 */
const std::string Worker::getDataDir()
{
  static std::string datadir="";
  std::string str1;
  const char *s1;
  char *s2, *s3;
  std::list<std::string> searchlist;
  std::list<std::string>::iterator it1;
  static bool warned = false;

#ifdef PREFIX
  if ( datadir.length() < 1 ) {
    // first try PREFIX
    if ( strlen( PREFIX ) > 0 ) {
      str1 = PREFIX;
      str1 += "/share/worker";
      if ( Datei::fileExistsExt( str1.c_str() ) == Datei::D_FE_DIR ) {
        // dir found
        datadir = str1;
      }
      searchlist.push_back( str1 );
    }
  }
#endif
  if ( datadir.length() < 1 ) {
    // now try to get the datadir from argv[0]
    if ( ( argc > 0 ) && ( argv != NULL ) ) {
      if ( argv[0][0] != '/' ) {
        str1 = startcwd;
        str1 += "/";
        str1 += argv[0];
      } else {
        str1 = argv[0];
      }
      s2 = NWC::Path::handlePath( str1.c_str() );  // remove . and .. parts
      if ( s2 != NULL ) {
        s3 = NWC::Path::parentDir( s2, NULL );  // get dirname from exename
        _freesafe( s2 );
        if ( s3 != NULL ) {
          s2 = NWC::Path::parentDir( s3, NULL );  // now the parent of the dirname
          _freesafe( s3 );
          if ( s2 != NULL ) {
            str1 = s2;
            str1 += "/share/worker";
            if ( Datei::fileExistsExt( str1.c_str() ) == Datei::D_FE_DIR ) {
              // dir found
              datadir = str1;
            }
            searchlist.push_back( str1 );
            _freesafe( s2 );
          }
        }
      }
    }
  }
  if ( datadir.length() < 1 ) {
    // try /usr
    str1 = "/usr/share/worker";
    if ( Datei::fileExistsExt( str1.c_str() ) == Datei::D_FE_DIR ) {
      // dir found
      datadir = str1;
    }
    searchlist.push_back( str1 );
  }
  if ( datadir.length() < 1 ) {
    // try /usr/local
    str1 = "/usr/local/share/worker";
    if ( Datei::fileExistsExt( str1.c_str() ) == Datei::D_FE_DIR ) {
      // dir found
      datadir = str1;
    }
    searchlist.push_back( str1 );
  }
  if ( datadir.length() < 1 ) {
    // finally try $HOME
    s1 = getenv( "HOME" );
    if ( s1 != NULL ) {
      str1 = s1; 
      str1 += "/share/worker";
      if ( Datei::fileExistsExt( str1.c_str() ) == Datei::D_FE_DIR ) {
        // dir found
        datadir = str1;
      }
      searchlist.push_back( str1 );
    }
  }
  if ( datadir.length() < 1 ) {
    if ( Worker::getRequester() != NULL ) {
      if ( warned == false ) {
        str1 = "Worker couldn't find the data dir! I was looking in:|";
        for ( it1 = searchlist.begin(); it1 != searchlist.end(); it1++ ) {
          str1 += *it1;
          str1 += "|";
        }
        str1 += "Check for correct worker installation!";
        Worker::getRequester()->request( "Worker Warning", str1.c_str(), "Okay" );
        warned = true;
      }
    }
  }
  return datadir;
}

void Worker::createMainWin()
{
  int tw, th;
  
  if ( mainwin != NULL ) return;
  
  mainwin = new AWindow( aguix, 10, 10, m_main_window_width, m_main_window_height, "Worker" );

  if ( m_main_window_last_maximized_x || m_main_window_last_maximized_y ) {
      if ( aguix->isValidScreenPosition( m_main_window_last_absolute_x,
                                         m_main_window_last_absolute_y,
                                         m_main_window_width,
                                         m_main_window_height ) ) {
          mainwin->forceOpenPosition( m_main_window_last_absolute_x,
                                      m_main_window_last_absolute_y,
                                      m_main_window_last_maximized_x,
                                      m_main_window_last_maximized_y );
      }
  }

  mainwin->create();
  mainwin->setMinSize(300,200);

  aboutb=new Button(aguix,0,0,"A",1,0,1);
  aboutb->setAcceptFocus( false );
  aboutb->getSize(&tw,&th);
  aboutb->resize(th,th);
  aboutb->setBubbleHelpText( catalog.getLocale( 1135 ) );

  configureb=new Button(aguix,0,0,"",1,0,2);
  configureb->setAcceptFocus( false );
  configureb->setBubbleHelpText( catalog.getLocale( 1121 ) );
  configureb->setIcon( aguix_icon_pref, aguix_icon_pref_len,
                       Button::ICON_LEFT );

  statebar = new Button( aguix, 0, 0, th, "", "", 1, 1, 0, 0, 4 );
  statebar->deactivate( 0 );
  statebar->setShowDual( false );
  statebar->setAcceptFocus( false );
  statebar->setText( 1, catalog.getLocale( 786 ) );
  statebar->setBubbleHelpText( catalog.getLocale( 1122 ) );

  clockbar=new Button(aguix,0,0,th,"","",1,1,0,0,3);
  clockbar->setAcceptFocus( false );
  clockbar->setShowDual(false);
  clockbar->setAllowWheel( true );
  clockbar->setBubbleHelpText( catalog.getLocale( 1123 ) );
  
  listerwin[0] = new AWindow( aguix, 0, 0, 200, 200, "" );
  mainwin->add( listerwin[0] );
  listerwin[0]->create();
  listerwin[0]->show();
  listerwin[1] = new AWindow( aguix, 0, 0, 200, 200, "" );
  mainwin->add( listerwin[1] );
  listerwin[1]->create();
  listerwin[1]->show();

  mainwin->enableXDND();
  
  mainwin->show();

  delete req_static;
  req_static = new Requester( aguix, mainwin );
  aguix->setTransientWindow( mainwin );

  createWorkerMenu();
}

AWindow *Worker::getListerWin( Lister *l )
{
  if ( lister[0] == NULL ) return listerwin[0];
  else if ( lister[0] == l ) return listerwin[0];
  else if ( lister[1] == NULL ) return listerwin[1];
  else if ( lister[1] == l ) return listerwin[1];
  return NULL;
}

int Worker::getMaxButtonsWidth()
{
  int bw;
  int id;
  List *buttonlist;
  WCButton *b1;
  int t;
  const char *tstr;

  AGUIXFont *font = aguix->getFont( wconfig->getFont( 1 ).c_str() );
  
  if ( font == NULL ) return 0;

  buttonlist = wconfig->getButtons();
  id = buttonlist->initEnum();
  b1 = (WCButton*)buttonlist->getFirstElement( id );
  bw = 0;
  while ( b1 != NULL ) {
    if( b1->getCheck() == true ) {
      tstr = b1->getText();
      if ( tstr != NULL ) {
        t = aguix->getTextWidth( tstr, font );
        if ( t > bw ) bw = t;
      }
    }
    b1 = (WCButton*)buttonlist->getNextElement( id );
  }
  buttonlist->closeEnum( id );
  
  return bw;
}

/*
 * this method will execute a given string
 */
int Worker::runCommand( const char *exestr,
			const char *tmpname,
			const char *tmpoutput,
			bool inbackground,
                        RefCount< GenericCallbackArg< void, int > > pea )
{
    pid_t child, ret;
    int status, retval;
  
    setWaitCursor();
    child = fork();
    if ( child != 0 ) {
        // parent
        bool put_in_background = false;
        
        TimedText temp_sbtext( catalog.getLocale( 547 ),
                               time( NULL ) );
        
        if ( child == -1 ) {
            unsetWaitCursor();
            return -2;
        }

        retval = 0;
        // calling these functions will clear old values
        aguix->getLastKeyRelease();
        aguix->getLastMouseRelease();

        if ( inbackground == false ) {
            pushStatebarText( temp_sbtext );
        } else {
            put_in_background = true;
            
            m_process_handler.addChildProcess( child, pea );
        }

        for ( ; put_in_background == false; ) {
            // first answer all messages to be able to redraw
            aguix->doXMsgs( NULL, false );

            // check for events to disconnect from child
            if ( ( aguix->getLastKeyRelease() == XK_Escape ) ||
                 ( aguix->getLastMouseRelease() == Button2 ) ) {
                put_in_background = true;
                
                m_process_handler.addChildProcess( child, pea );
                
                break;
            }

            // now check for existence of the child
            ret = waitpid( child, &status, WNOHANG );
            if ( ret == child ) {
                if ( WIFEXITED( status ) ) {
                    // normal exit
#ifdef DEBUG
                    printf( "child exited normally!\n" );
#endif
                    retval = WEXITSTATUS( status );
                    break;
                } else if ( WIFSIGNALED( status ) ) {
#ifdef DEBUG
                    printf("child exited by a signal!\n");
#endif
                    retval = -1;
                    break;
                } else {
#ifdef DEBUG
                    printf( "child exited by an unhandled way!\n" );
#endif
                    retval = -1;
                    break;
                }
            } else if ( ret == -1 ) {
                if ( errno == ECHILD ) {
                    // child does not exists?
                    retval = -1;
                    break;
                }
            }

            // wait for events
            waitForEvent();
        }

        bool callback_called = false;

        if ( put_in_background == false ) {
            if ( pea.get() != NULL ) {
                pea->callback( retval );
                callback_called = true;
            }
        }

        unsetWaitCursor();

        if ( inbackground == false ) {
            removeStatebarText( temp_sbtext );
        }

        if ( put_in_background == false && callback_called == false && retval != 0 ) {
            showCommandFailed( retval );
        }

        return retval;
    }
    
    // child
    // from now on I will use _exit instead of exit to avoid calling
    // exit handlers
    // avfs currently has the problem that a exit handler removes
    // the temp. directory rendering the vfs for the main process
    // useless
    //TODO:If this changes in avfs, I will use exit again
    // wait for second child exit until there is something in the pipe
    
    // disconnect from parent
    setsid();
    if ( worker_chdir( "/" ) != 0 ) {
        // that should always succeed
        abort();
    }

    // remove sigchld handler to avoid filling the pipe
    struct sigaction sig_ac;
    memset( &sig_ac, 0, sizeof( sig_ac ) );
    sig_ac.sa_handler = SIG_DFL;
    sigaction( SIGCHLD, &sig_ac, NULL );

    // several threads can have open file descriptors but those
    // threads are not running in thge child process so no one will
    // close them. These can be from async existence tests, or file
    // type tests. Some (but not all) are flagged with O_CLOEXEC but
    // we are not doing exec here to be able to remove the temp files
    // after the program has finished. I could use a wrapper scripts
    // (which already exists for "early return" terminals), but this
    // would make it even more complicated. Therefore, I just loop
    // over all possible FD numbers and close them. /proc/self/fd
    // contains the list of actually opened files, but this is not
    // portable. close_range() would be more efficent, but it is not
    // portable either.
    long max_fds = sysconf( _SC_OPEN_MAX );
    if ( max_fds > 0 ) {
        for ( long fd = 3; fd < max_fds; fd++ ) {
            close( fd );
        }
    }

    // execute command
    // we could use exec* to call this
    //   but then we have to change exestr to give a char**argv
    //   (which is not so hard because it contains only the commands to execute
    //    tmpname)
    //   we also have to make another fork because exec* doesn't return
    //     and so we need an other process to remove the tmp-files after command finished
    status = system( exestr );
    
    // remove temp files
    if ( tmpname ) {
        ::unlink( tmpname );
    }
    if ( tmpoutput ) {
        ::unlink( tmpoutput );
    }
    
    // and exit
    if ( status == -1 ) {
        _exit( EXIT_FAILURE );
    } else {
        _exit( WEXITSTATUS( status ) );
    }

    // just for some compiler to compile without warning
    return 0;
}

std::string Worker::queryLanguage()
{
    std::vector<std::string> langs;
    DIR *dir;
    std::string catdir, buttonstr2, str2, str3;
    worker_struct_dirent *namelist;
    std::string selected_lang;

    selected_lang = "english";
    
    langs.push_back( selected_lang );
    if ( Worker::getDataDir().length() > 0 ) {
        catdir = Worker::getDataDir();
        catdir += "/catalogs";
        dir = worker_opendir( catdir.c_str() );
        if ( dir != NULL ) {
            std::string ending = ".catalog";
            if ( UTF8::getUTF8Mode() != UTF8::UTF8_DISABLED &&
                 UTF8::getCurrentEncoding() == UTF8::ENCODING_UTF8 ) {
                ending += ".utf8";
            }
            
            while ( ( namelist = worker_readdir( dir ) ) != NULL ) {
                if ( strlen( namelist->d_name ) > ending.length() ) {
                    if ( strcmp( ending.c_str(),
                                 ( namelist->d_name ) + strlen( namelist->d_name ) - ending.length() ) == 0 ) {
                        // looks a catalog file
                        std::string str1( namelist->d_name,
                                          strlen( namelist->d_name ) - ending.length() );
                        langs.push_back( str1 );
                    }
                }
            }
            worker_closedir( dir );
        }
    }
    // now all available languages are in the list
    // build a requester

    AWindow *win;
    AGMessage *msg;
    int endmode=-1;
    
    win = new AWindow( aguix, 10, 10, 10, 10, "Language selection", AWindow::AWINDOW_DIALOG );
    win->create();
    
    AContainer *ac1 = win->setContainer( new AContainer( win, 1, 3 ), true );
    ac1->setMinSpace( 5 );
    ac1->setMaxSpace( 5 );
    
    ac1->add( new Text( aguix, 0, 0, "Choose your language (this can be changed later):" ),
              0, 0, AContainer::CO_INCWNR );

    FieldListView *lv1 = (FieldListView*)ac1->add( new FieldListView( aguix,
                                                                      0, 0,
                                                                      200, 200, 0 ),
                                                   0, 1, AContainer::CO_MIN );
    lv1->setHBarState( 2 );
    lv1->setVBarState( 2 );
    lv1->setAcceptFocus( true );
    lv1->setDisplayFocus( true );

    for ( std::vector<std::string>::iterator it1 = langs.begin();
          it1 != langs.end();
          it1++ ) {
        int row = lv1->addRow();
        lv1->setText( row, 0, *it1 );
        lv1->setPreColors( row, FieldListView::PRECOLOR_ONLYACTIVE );
    }

    AContainer *ac1_1 = ac1->add( new AContainer( win, 3, 1 ), 0, 2 );
    ac1_1->setMinSpace( 5 );
    ac1_1->setMaxSpace( -1 );
    ac1_1->setBorderWidth( 0 );
    Button *contb =(Button*)ac1_1->add( new Button( aguix,
                                                    0,
                                                    0,
                                                    "Continue",
                                                    0 ), 1, 0, AContainer::CO_FIX );
    win->setDoTabCycling( true );
    win->contMaximize( true );
    win->show();
    
    for ( ;endmode == -1; ) {
        msg = aguix->WaitMessage( win );
        if ( msg != NULL ) {
            switch ( msg->type ) {
              case AG_CLOSEWINDOW:
                  if ( msg->closewindow.window == win->getWindow() ) endmode = 1;
                  break;
              case AG_BUTTONCLICKED:
                  if ( msg->button.button == contb ) endmode = 0;
                  break;
            }
            aguix->ReplyMessage( msg );
        }
    }
    
    if ( endmode == 0 ) {
        int row = lv1->getActiveRow();
        if ( lv1->isValidRow( row ) == true ) {
            selected_lang = langs[row];
        }
    }
    
    delete win;
    return selected_lang;
}

std::string Worker::getWorkerConfigDir()
{
    std::string d;

    d = WorkerInitialSettings::getInstance().getConfigBaseDir();

    //TODO the result could be cached
    if ( Datei::fileExists( d.c_str() ) == false ) {
        if ( NWC::OS::make_dirs( d ) != 0 ) {
            std::cerr << "Worker: Couldn't create worker configuration directory " << d << std::endl;
            exit( EXIT_FAILURE );
        }
    }
    return d;
}

void Worker::createWorkerMenu()
{
    if ( m_worker_menu != NULL ) {
        delete m_worker_menu;
        m_worker_menu = NULL;
    }

    std::list<PopUpMenu::PopUpEntry> m1;
    PopUpMenu::PopUpEntry e1;
    e1.name = catalog.getLocale( 787 );
    e1.type = PopUpMenu::HEADER;
    m1.push_back( e1 );
    e1.type = PopUpMenu::HLINE;
    m1.push_back( e1 );

    // about
    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 208 );
    e1.id = 1;
    m1.push_back( e1 );
    
    // configure
    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 0 );
    e1.id = 2;
    m1.push_back( e1 );

    // log
    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 1410 );
    e1.id = 5;
    m1.push_back( e1 );
    
    e1.type = PopUpMenu::HLINE;
    m1.push_back( e1 );

    // save worker state
    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 788 );
    e1.id = 3;
    m1.push_back( e1 );
    
    e1.type = PopUpMenu::HLINE;
    m1.push_back( e1 );

    // quit
    e1.type = PopUpMenu::NORMAL;
    e1.name = catalog.getLocale( 789 );
    e1.id = 4;
    m1.push_back( e1 );
    
    m_worker_menu = new PopUpMenu( aguix, m1 );
    m_worker_menu->create();
}

void Worker::openWorkerMenu()
{
    if ( m_worker_menu != NULL ) {
        m_worker_menu->show();
    }
}

void Worker::startMenuAction( AGMessage *msg )
{
    if ( msg->popupmenu.menu != m_worker_menu ) return;

    switch ( msg->popupmenu.clicked_entry_id ) {
        case 1: // about
            about();
            break;
        case 2: // configure
            configure();
            break;
        case 3: // save worker state
            saveWorkerState();
            break;
        case 4: // quit worker
            quit( 1 );
            break;
        case 5: // show command log
            {
                ActionMessage amsg( this );
                amsg.mode = amsg.AM_MODE_NORMAL;

                command_list_t list = { std::shared_ptr< FunctionProto >( new ViewCommandLogOp() ) };

                interpret( list, &amsg );
            }
            break;
        default:
            break;
    }
}

void Worker::saveWorkerState()
{
    saveListerState();

    std::string filename = Worker::getWorkerConfigDir();
#ifdef DEBUG
    filename = NWC::Path::join( filename, "lastsize-v2-dbg." );
#else
    filename = NWC::Path::join( filename, "lastsize-v2." );
#endif

    filename = AGUIXUtils::formatStringToString( "%s%d", filename.c_str(), aguix->getScreen() );

    std::ofstream ofile( filename );

    ofile << m_main_window_width << "x" << m_main_window_height << std::endl;

    int maximized = mainwin->isMaximized();
    ofile << ( ( maximized & 1 ) ? "maxh:1" : "maxh:0" ) << " " << ( ( maximized & 2 ) ? "maxv:1" : "maxv:0" ) << std::endl;

    int x, y;

    if ( mainwin->getRootPosition( &x, &y ) != 0 ) {
        x = y =0;
    }

    ofile << x << "," << y << std::endl;
}

void Worker::loadWindowGeometry()
{
    std::string filename = Worker::getWorkerConfigDir();
#ifdef DEBUG
    filename = NWC::Path::join( filename, "lastsize-v2-dbg." );
#else
    filename = NWC::Path::join( filename, "lastsize-v2." );
#endif

    filename = AGUIXUtils::formatStringToString( "%s%d", filename.c_str(), aguix->getScreen() );

    if ( NWC::FSEntry( filename ).entryExists() ) {
        std::ifstream ifile( filename.c_str() );
        std::string line;
    
        if ( ifile.is_open() ) {
            int lineno = 0;
            while ( std::getline( ifile, line ) ) {
                if ( lineno == 0 ) {
                    if ( sscanf( line.c_str(), "%dx%d", &m_main_window_width,
                                 &m_main_window_height ) != 2 ) {
                        m_main_window_width = m_main_window_height = 0;
                        break;
                    }
                } else if ( lineno == 1 ) {
                    int v1, v2;
                    if ( sscanf( line.c_str(), "maxh:%d maxv:%d", &v1,
                                 &v2 ) != 2 ) {
                        break;
                    }

                    if ( v1 ) m_main_window_last_maximized_x = true;
                    if ( v2 ) m_main_window_last_maximized_y = true;
                } else if ( lineno == 2 ) {
                    if ( sscanf( line.c_str(), "%d,%d", &m_main_window_last_absolute_x,
                                 &m_main_window_last_absolute_y ) != 2 ) {
                        m_main_window_last_absolute_x = m_main_window_last_absolute_y = 0;
                        break;
                    }
                } else {
                    break;
                }

                lineno++;
            }
        }
    } else {
        Datei *fp;
  
        filename = Worker::getWorkerConfigDir();
        filename = NWC::Path::join( filename, "lastsize." );

        filename = AGUIXUtils::formatStringToString( "%s%d", filename.c_str(), aguix->getScreen() );

        fp =new Datei();
        if ( fp != NULL ) {
            if ( fp->open( filename.c_str(), "r" ) == 0 ) {
                m_main_window_width = fp->getUShort();
                m_main_window_height = fp->getUShort();
                fp->close();
            }
            delete fp;
        }
    }

    if ( m_main_window_width < 300 ) m_main_window_width = 300;
    if ( m_main_window_height < 200 ) m_main_window_height = 200;
}

int Worker::getStatebarWidth()
{
    return statebar->getWidth() - 2 * 2;
}

BookmarkDBProxy &Worker::getBookmarkDBInstance()
{
    if ( m_bookmarkproxy.get() != NULL ) return *m_bookmarkproxy;

    std::string cfgfile = Worker::getWorkerConfigDir();
#ifdef DEBUG
    cfgfile = NWC::Path::join( cfgfile, "bookmarks2" );
#else
    cfgfile = NWC::Path::join( cfgfile, "bookmarks" );
#endif
    m_bookmarkproxy.reset( new BookmarkDBProxy( std::unique_ptr<BookmarkDB>( new BookmarkDB( cfgfile ) ) ) );
    m_bookmarkproxy->read();
    return *m_bookmarkproxy;
}

static bool cb_cmp( const RefCount<GenericCallbackArg<void, AGMessage*> > &cmp_entry,
                    const RefCount<GenericCallbackArg<void, AGMessage*> > &elem )
{
    if ( elem.get() == NULL || cmp_entry.get() == NULL ) return false;
    if ( elem.get() == cmp_entry.get() ) return true;
    return false;
}

void Worker::registerPopUpCallback( RefCount<GenericCallbackArg<void, AGMessage*> > cb,
                                    const PopUpMenu *menu )
{
    std::map<const PopUpMenu *, std::list<RefCount<GenericCallbackArg<void, AGMessage*> > > >::iterator it1;

    it1 = m_popup_callbacks.find( menu );

    if ( it1 != m_popup_callbacks.end() ) {
        if ( std::find_if( m_popup_callbacks[menu].begin(),
                           m_popup_callbacks[menu].end(),
                           [&cb]( auto &elem ) {
                               return cb_cmp( cb, elem );
                           }) == m_popup_callbacks[menu].end() ) {
            m_popup_callbacks[menu].push_back( cb );
        }
    } else {
        m_popup_callbacks[menu].push_back( cb );
    }
}

void Worker::unregisterPopUpCallback( RefCount<GenericCallbackArg<void, AGMessage*> > cb )
{
    std::map<const PopUpMenu *, std::list<RefCount<GenericCallbackArg<void, AGMessage*> > > >::iterator it1;
    std::list<RefCount<GenericCallbackArg<void, AGMessage*> > >::iterator it2;

    for ( it1 = m_popup_callbacks.begin();
          it1 != m_popup_callbacks.end();
          ++it1 ) {
        it2 = std::find_if( it1->second.begin(),
                            it1->second.end(),
                            [&cb]( auto &elem ) {
                                return cb_cmp( cb, elem );
                            });
        if ( it2 != it1->second.end() ) {
            it1->second.erase( it2 );
        }
    }
}

void Worker::handlePopUps( AGMessage *msg )
{
    if ( msg == NULL ) return;

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

    std::map<const PopUpMenu *, std::list<RefCount<GenericCallbackArg<void, AGMessage*> > > >::iterator it1;
    std::list<RefCount<GenericCallbackArg<void, AGMessage*> > > temp_list;
    std::list<RefCount<GenericCallbackArg<void, AGMessage*> > >::iterator it2;

    it1 = m_popup_callbacks.find( msg->popupmenu.menu );

    if ( it1 != m_popup_callbacks.end() ) {
        // copy list because a callback might remove it from the list
        // thus invalidating the iterator
        temp_list = it1->second;
        for ( it2 = temp_list.begin();
              it2 != temp_list.end();
              ++it2 ) {
            (*it2)->callback( msg );
        }
    }

    it1 = m_popup_callbacks.find( NULL );

    if ( it1 != m_popup_callbacks.end() ) {
        temp_list = it1->second;
        for ( it2 = temp_list.begin();
              it2 != temp_list.end();
              ++it2 ) {
            (*it2)->callback( msg );
        }
    }
}

LayoutSettings Worker::getCustomLayout() const
{
    return m_custom_layout;
}

bool Worker::getUseCustomLayout() const
{
    return m_use_custom_layout;
}

void Worker::setCustomLayout( const LayoutSettings &nv )
{
    m_use_custom_layout = true;
    m_custom_layout = nv;
    restart = RS_RELAYOUT;
}

void Worker::unsetCustomLayout()
{
    m_use_custom_layout = false;
    restart = RS_RELAYOUT;
}

/*
 * insert TimedText sorted by age, youngest last
 */
void Worker::pushStatebarText( const TimedText &text )
{
    std::list< TimedText >::iterator it1 = m_statebar_texts.begin();

    for ( ;
          it1 != m_statebar_texts.end();
          it1++ ) {
        if ( text.isOlder( *it1 ) )
            break;
    }
    m_statebar_texts.insert( it1, text );

    updateStatebar();
}

bool Worker::getValidStatebarText( TimedText &return_text )
{
    std::list< TimedText >::iterator it1 = m_statebar_texts.begin();
    time_t now = time( NULL );

    // first remove all expired texts
    while ( it1 != m_statebar_texts.end() ) {
        if ( it1->isExpired( now ) == true ) {
            std::list< TimedText >::iterator it2 = it1;
            it1++;

            m_statebar_texts.erase( it2 );
        } else {
            it1++;
        }
    }

    // now find the youngest valid entry
    std::list< TimedText >::iterator cand = m_statebar_texts.end();

    it1 = m_statebar_texts.begin();
    while ( it1 != m_statebar_texts.end() ) {
        if ( it1->isValid( now ) == true ) {
            cand = it1;
        } else {
            break;
        }
        it1++;
    }

    if ( cand != m_statebar_texts.end() ) {
        return_text = *cand;
        return true;
    } else {
        return false;
    }
}

/*
 * find first TimedText from now which will be valid next
 */
bool Worker::getNextValidStatebarText( TimedText &return_text )
{
    std::list< TimedText >::iterator it1 = m_statebar_texts.begin();
    time_t now = time( NULL );

    while ( it1 != m_statebar_texts.end() ) {
        if ( it1->getValidAt() > now ) {
            break;
        }
        it1++;
    }

    if ( it1 != m_statebar_texts.end() ) {
        return_text = *it1;
        return true;
    } else {
        return false;
    }
}

void Worker::updateStatebarText( const TimedText &text,
                                 state_bar_text_update_type update_type )
{
    std::list< TimedText >::iterator it1 = m_statebar_texts.begin();
    bool found = false;

    for ( ;
          it1 != m_statebar_texts.end();
          it1++ ) {
        if ( update_type == UPDATE_ON_TEXT_CHANGE ) {
            if ( it1->getValidAt() == text.getValidAt() &&
                 it1->getExpireAt() == text.getExpireAt() ) {
                *it1 = text;

                updateStatebar();
                found = true;
                break;
            }
        } else if ( update_type == UPDATE_ON_TIME_CHANGE ) {
            if ( it1->getText() == text.getText() ) {
                found = true;

                // if times differ, we have to remove it and re-insert
                // to keep correct ordering
                if ( it1->getValidAt() != text.getValidAt() ||
                     it1->getExpireAt() != text.getExpireAt() ) {
                    m_statebar_texts.erase( it1 );
                    found = false;
                }

                break;
            }
        }
    }

    if ( found == false ) {
        pushStatebarText( text );
    }
}

void Worker::removeStatebarText( const TimedText &text )
{
    std::list< TimedText >::iterator it1 = m_statebar_texts.begin();

    for ( ;
          it1 != m_statebar_texts.end();
          it1++ ) {
        if ( it1->getValidAt() == text.getValidAt() &&
             it1->getExpireAt() == text.getExpireAt() ) {
            m_statebar_texts.erase( it1 );

            updateStatebar();
            break;
        }
    }
}

void Worker::updateStatebar()
{
    TimedText t;
    std::string text;

    if ( statebar == NULL ) return;

    if ( getValidStatebarText( t ) == true ) {
        text = t.getText();

        // add timeout when valid timedtext expires or next timedtext becomes valid
        time_t timeout = t.getExpireAt();
        if ( getNextValidStatebarText( t ) == true ) {
            if ( t.getValidAt() < timeout ) timeout = t.getValidAt();
        }

        time_t now = time( NULL );

        loff_t to = ( timeout - now ) * 1000;
        if ( to < 100 ) to = 100;

        setStatebarUpdate( to );
    } else {
        text = m_statebar_text;

        if ( getNextValidStatebarText( t ) == true ) {
            time_t now = time( NULL );

            loff_t to = ( t.getValidAt() - now ) * 1000;
            if ( to < 100 ) to = 100;

            setStatebarUpdate( to );
        } else {
            setStatebarUpdate( 0 );
        }
    }
    if ( text != statebar->getText( 0 ) ) {
        statebar->setText( 0, text.c_str() );
    }
}

std::string Worker::getNamesOfKeyChainCompletions( KeySym k, unsigned int m,
                                                   std::list< WCButton * > *buttons,
                                                   std::list< WCHotkey * > *hotkeys,
                                                   std::list< WCPath * > *paths )
{
    unsigned int hashvalue;
    keyhash *kh;
    std::string res;
    int id_dk;
    List *dkeys;
    WCDoubleShortkey *dk;
    
    if ( k == 0 ) return "";
    if ( keyhashtable == NULL ) return "";
  
    hashvalue = keyhashfunc( k, m );
    kh = keyhashtable[ hashvalue ];

    if ( kh != NULL ) {
        while ( kh != NULL ) {
            dkeys = NULL;
            if ( kh->b != NULL ) {
                dkeys = kh->b->getDoubleKeys();
            } else if ( kh->h != NULL ) {
                dkeys = kh->h->getDoubleKeys();
            } else if ( kh->p != NULL ) {
                dkeys = kh->p->getDoubleKeys();
            }

            if ( dkeys != NULL ) {
                id_dk = dkeys->initEnum();
                dk = (WCDoubleShortkey*)dkeys->getFirstElement( id_dk );
                while ( dk != NULL ) {
                    if ( dk->getType() == WCDoubleShortkey::WCDS_DOUBLE &&
                         dk->getKeySym( 0 ) == k &&
                         dk->getMod( 0 ) == m ) {
                        char *tstr = aguix->getNameOfKey( dk->getKeySym( 1 ),
                                                          dk->getMod( 1 ) );
                        if ( tstr != NULL ) {
                            if ( ! res.empty() ) {
                                res += " , ";
                            }
                            res += tstr;
                            _freesafe( tstr );
                        }

                        if ( kh->b && buttons ) {
                            buttons->push_back( kh->b );
                        } else if ( kh->h && hotkeys ) {
                            hotkeys->push_back( kh->h );
                        } else if ( kh->p && paths ) {
                            paths->push_back( kh->p );
                        }
                    }
                    
                    dk = (WCDoubleShortkey*)dkeys->getNextElement( id_dk );
                }
                dkeys->closeEnum( id_dk );
            }

            kh = kh->next;
        }
    }
    
    return res;
}

HWVolumeManager &Worker::getVolumeManager()
{
    return m_volume_manager;
}

void Worker::checkNewVolumes()
{
    std::list< HWVolume > current_devs, new_devs;
    std::list< HWVolume >::const_iterator it1, it2;

    current_devs = m_volume_manager.filterUniqueDevices( m_volume_manager.getNewVolumes( false ) );
    current_devs.sort();

    it1 = current_devs.begin();
    it2 = m_known_volumes.begin();
    while ( true ) {
        if ( it1 == current_devs.end() ) break;

        if ( it2 == m_known_volumes.end() ) {
            // no devices left in current list
            // so add all entries to the new list
            for ( ; it1 != current_devs.end(); it1++ ) {
                new_devs.push_back( *it1 );
            }
            break;
        }

        if ( it1->sameDevice( *it2 ) ) {
            it1++;
            it2++;
            continue;
        }

        if ( *it1 < *it2 ) {
            // new device in current_devs
            new_devs.push_back( *it1 );
            it1++;
        } else {
            // devices removed, do nothing currently
            it2++;
        }
    }

    m_known_volumes = current_devs;
    
    if ( new_devs.empty() ) {
        removeStatebarText( m_new_volumes_infotext );
    } else {
        removeStatebarText( m_new_volumes_infotext );

        std::list< std::string > hidden_devs = VolumeManagerUI::getListOfHiddenDevices();
        std::list< HWVolume >::iterator it3, it4;
        for ( it3 = new_devs.begin();
              it3 != new_devs.end();
              ) {
            if ( std::find( hidden_devs.begin(),
                            hidden_devs.end(),
                            it3->getDevice() ) != hidden_devs.end() ) {
                it4 = it3;
                it4++;
                new_devs.erase( it3 );
                it3 = it4;
            } else {
                it3++;
            }
        }

        if ( ! new_devs.empty() ) {
            if ( wconfig->getVMRequestAction() == true &&
                 aguix->getFocusedAWindow( false ) != NULL ) {
                // we have the X focus so show a dialog to ask
                // what to do
                queryNewVolumeAction( new_devs );
            } else {
                std::string infotext = catalog.getLocale( 864 );
                int count = 0;
                for ( it1 = new_devs.begin();
                      it1 != new_devs.end();
                      it1++ ) {
                    if ( count > 0 ) infotext += ",";
                    infotext += " ";
                    infotext += it1->getDevice();
                    count++;
                }

                m_new_volumes_infotext = TimedText( infotext,
                                                    time( NULL ),
                                                    time( NULL ) + 20 );
                updateStatebarText( m_new_volumes_infotext );
            }
        }
    }
}

void Worker::clearNewVolumesInfo()
{
    m_volume_manager.getNewVolumes( true );
    m_known_volumes = m_volume_manager.filterUniqueDevices( m_volume_manager.getNewVolumes( false ) );
    m_known_volumes.sort();
    removeStatebarText( m_new_volumes_infotext );
}

void Worker::updateVolumeManagerConfig()
{
    m_volume_manager.setMountCommand( wconfig->getVMMountCommand() );
    m_volume_manager.setUnmountCommand( wconfig->getVMUnmountCommand() );
    m_volume_manager.setFStabFile( wconfig->getVMFStabFile() );
    m_volume_manager.setMtabFile( wconfig->getVMMtabFile() );
    m_volume_manager.setPartitionFile( wconfig->getVMPartitionFile() );
    m_volume_manager.setPreferredUDisksVersion( wconfig->getVMPreferredUdisksVersion() );
}


void Worker::queryNewVolumeAction( const std::list< HWVolume > &l )
{
    std::string msg;
    std::list< HWVolume >::const_iterator it1;

    if ( l.empty() ) return;

    AWindow *win;
    const AWindow *twin;
    
    if ( aguix->getTransientWindow() != NULL ) {
        twin = aguix->getTransientWindow();
    } else {
        twin = NULL;
    }

    win = new AWindow( aguix, 10, 10, 10, 10, catalog.getLocale( 123 ), AWindow::AWINDOW_DIALOG );
    win->create();
    
    AContainer *ac1 = win->setContainer( new AContainer( win, 1, 3 ), true );
    ac1->setBorderWidth( 5 );
    ac1->setMinSpace( 5 );
    ac1->setMaxSpace( 5 );

    AContainer *ac1_1 = ac1->add( new AContainer( win, 1, 1 ), 0, 0 );
    ac1_1->setBorderWidth( 0 );
    ac1_1->setMinSpace( 0 );
    ac1_1->setMaxSpace( 0 );

    ac1_1->add( new Text( aguix, 0, 0, catalog.getLocale( 900 ) ),
                0, 0, AContainer::CO_INCW );

    AContainer *ac1_2 = ac1->add( new AContainer( win, 1, 1 ), 0, 1 );
    ac1_2->setBorderWidth( 0 );
    ac1_2->setMinSpace( 0 );
    ac1_2->setMaxSpace( 0 );

    FieldListView *lv = (FieldListView*)ac1_2->add( new FieldListView( aguix, 0, 0, 100, 50, 0 ),
                                                    0, 0, AContainer::CO_INCW );
    
    AContainer *ac1_3 = ac1->add( new AContainer( win, 4, 1 ), 0, 2 );
    ac1_3->setBorderWidth( 0 );
    ac1_3->setMinSpace( 5 );
    ac1_3->setMaxSpace( -1 );

    Button *mount_and_cd_b = (Button*)ac1_3->add( new Button( aguix, 0, 0, catalog.getLocale( 901 ), 0 ),
                                                  0, 0, AContainer::CO_INCW );

    Button *mount_b = (Button*)ac1_3->add( new Button( aguix, 0, 0, catalog.getLocale( 902 ), 0 ),
                                           1, 0, AContainer::CO_INCW );

    Button *open_volman_b = (Button*)ac1_3->add( new Button( aguix, 0, 0,catalog.getLocale( 903 ), 0 ),
                                                 2, 0, AContainer::CO_INCW );

    Button *nothing_b = (Button*)ac1_3->add( new Button( aguix, 0, 0, catalog.getLocale( 904 ), 0 ),
                                             3, 0, AContainer::CO_INCW );

    lv->setHBarState( 0 );
    lv->setVBarState( 0 );
    for ( it1 = l.begin();
          it1 != l.end();
          it1++ ) {
        int row = lv->addRow();
        lv->setText( row, 0, it1->getDevice() );
        lv->setPreColors( row, FieldListView::PRECOLOR_ONLYACTIVE );
    }
    lv->setActiveRow( 0 );
    lv->maximizeX();
    lv->maximizeY();
    ac1_2->readLimits();

    if ( twin != NULL ) {
        win->setTransientForAWindow( twin );
    }

    win->setDoTabCycling( true );
    win->contMaximize( true );
    win->useStippleBackground();
    win->centerScreen();
    win->show();
    
    AGMessage *agmsg;
    int endmode = -1;
    for ( ;endmode == -1; ) {
        agmsg = aguix->WaitMessage( win );
        if ( agmsg != NULL ) {
            switch ( agmsg->type ) {
              case AG_CLOSEWINDOW:
                  if ( agmsg->closewindow.window == win->getWindow() ) endmode = 3;
                  break;
              case AG_BUTTONCLICKED:
                  if ( agmsg->button.button == mount_and_cd_b ) endmode = 0;
                  else if ( agmsg->button.button == mount_b ) endmode = 1;
                  else if ( agmsg->button.button == open_volman_b ) endmode = 2;
                  else if ( agmsg->button.button == nothing_b ) endmode = 3;
                  break;
              case AG_KEYPRESSED:
                  if ( win->isParent( agmsg->key.window, false ) == true ) {
                      if ( agmsg->key.key == XK_Return ||
                           agmsg->key.key == XK_KP_Enter ) {
                          if ( mount_and_cd_b->getHasFocus() == true ) {
                              endmode = 0;
                          }
                      } else if ( agmsg->key.key == XK_Escape ) {
                          endmode = 3;
                      } else if ( agmsg->key.key == XK_F1 ) {
                          endmode = 0;
                      } else if ( agmsg->key.key == XK_F2 ) {
                          endmode = 1;
                      } else if ( agmsg->key.key == XK_F3 ) {
                          endmode = 2;
                      } else if ( agmsg->key.key == XK_F4 ) {
                          endmode = 3;
                      }
                  }
                  break;
            }
            aguix->ReplyMessage( agmsg );
        }
    }

    int row = lv->getActiveRow();

    if ( ! lv->isValidRow( row ) ) {
        row = -1;
    }

    delete win;

    int choose = endmode;

    if ( row >= 0 ) {

        HWVolume vol;
        for ( it1 = l.begin();
              it1 != l.end();
              it1++ ) {
            if ( row == 0 ) {
                vol = *it1;
                break;
            }
            row--;
        }
        switch ( choose ) {
            case 0:{
                std::string err;
                if ( getVolumeManager().mount( vol, err ) != 0 ) {
                    msg = AGUIXUtils::formatStringToString( catalog.getLocale( 905 ),
                                                            err.c_str() );
                    Worker::getRequester()->request( catalog.getLocale( 347 ),
                                                     msg.c_str(), catalog.getLocale( 11 ) );
                } else {
                    Lister *l1;
                    ListerMode *lm1;
                
                    //find a better way to update fstab/mtab tables
                    getVolumeManager().getVolumes();
                    getVolumeManager().update( vol );

                    l1 = getActiveLister();
                    if ( l1 != NULL ) {
                        NWC::FSEntry fse( vol.getMountPoint() );
                        if ( fse.isDir( true ) ) {
                            if ( l1->getActiveMode() == NULL ||
                                 ! ( dynamic_cast< VirtualDirMode *>( l1->getActiveMode() ) ) ) {
                                l1->switch2Mode( 0 );
                            }

                            lm1 = l1->getActiveMode();
                            if ( lm1 != NULL ) {
                                std::list< RefCount< ArgClass > > args;

                                args.push_back( RefCount< ArgClass >( new StringArg( vol.getMountPoint() ) ) );
                                lm1->runCommand( "enter_dir", args );
                            }
                        }
                    }
                }
                break;
            }
            case 1: {
                std::string err;
                if ( getVolumeManager().mount( vol, err ) != 0 ) {
                    msg = AGUIXUtils::formatStringToString( catalog.getLocale( 905 ),
                                                            err.c_str() );
                    Worker::getRequester()->request( catalog.getLocale( 347 ),
                                                     msg.c_str(), catalog.getLocale( 11 ) );
                }
                break;
            }
            case 2: {
                ActionMessage amsg( this );
                amsg.mode = amsg.AM_MODE_NORMAL;

                command_list_t list = { std::shared_ptr< FunctionProto >( new VolumeManagerOp() ) };

                interpret( list, &amsg );
                break;
            }
            default:
                break;
        }
    }
}

void Worker::initVolumeManagerStuff()
{
    updateVolumeManagerConfig();
    //m_volume_manager.getNewVolumes();
    m_known_volumes = m_volume_manager.filterUniqueDevices( m_volume_manager.getNewVolumes() );
    m_known_volumes.sort();
}

void Worker::initWaitThread()
{
    m_eventqueue.reset( new EventQueue() );
    m_waitthread.reset( new WaitThread( m_eventqueue, m_timeouts ) );

#ifdef HAVE_XCONNECTIONNUMBER
    int x_fd = XConnectionNumber( aguix->getDisplay() );
    m_waitthread->watchFD( x_fd );
#else
    m_timeouts->addTimeout( 20 );
#endif

#ifdef HAVE_SIGACTION
    m_waitthread->watchFD2( sigchld_pipe[0] );
#endif

    if ( m_pathname_watcher->getWatcherFD() != -1 ) {
        m_waitthread->watchFD3( m_pathname_watcher->getWatcherFD() );
    }

    m_waitthread->start();
}

void Worker::shutdownWaitThread()
{
    m_waitthread->state_set( WaitThread::WAITTHREAD_STOP );
    m_waitthread->join();
}

void Worker::registerTimeout( loff_t timeout_ms )
{
    m_timeouts->addTimeout( timeout_ms );
}

void Worker::unregisterTimeout( loff_t timeout_ms )
{
    m_timeouts->clearTimeout( timeout_ms );
}

void Worker::wakeupMainLoop()
{
    m_eventqueue->push( EventQueue::EQ_TIMEOUT );
}

void Worker::setClockUpdate( loff_t time_ms )
{
    if ( m_current_clock_update_ms == time_ms ) return;

    if ( m_current_clock_update_ms != 0 ) {
        unregisterTimeout( m_current_clock_update_ms );
        m_current_clock_update_ms = 0;
    }
    
    if ( time_ms > 0 ) {
        registerTimeout( time_ms );
        m_current_clock_update_ms = time_ms;
    }
}

void Worker::setDbusUpdate( loff_t time_ms )
{
    if ( m_current_dbus_update_ms == time_ms ) return;

    if ( m_current_dbus_update_ms != 0 ) {
        unregisterTimeout( m_current_dbus_update_ms );
        m_current_dbus_update_ms = 0;
    }
    
    if ( time_ms > 0 ) {
        registerTimeout( time_ms );
        m_current_dbus_update_ms = time_ms;
    }
}

void Worker::setStatebarUpdate( loff_t time_ms )
{
    if ( m_current_statebar_update_ms == time_ms ) return;

    if ( m_current_statebar_update_ms != 0 ) {
        unregisterTimeout( m_current_statebar_update_ms );
        m_current_statebar_update_ms = 0;
    }
    
    if ( time_ms > 0 ) {
        registerTimeout( time_ms );
        m_current_statebar_update_ms = time_ms;
    }
}

void Worker::waitForEvent()
{
    bool sigchld_pipe_available = false;
    bool inotify_events = false;

    m_eventqueue->lock();
    while ( m_eventqueue->empty() ) {
        m_waitthread->state_set( WaitThread::WAITTHREAD_SELECT );
        m_eventqueue->wait();
    }
    while ( ! m_eventqueue->empty() ) {
        EventQueue::eq_event_t event = m_eventqueue->pop();
        switch ( event ) {
            case EventQueue::EQ_FD2:
                sigchld_pipe_available = true;
                break;
            case EventQueue::EQ_FD3:
                inotify_events = true;
                break;
            case EventQueue::EQ_TIMEOUT:
                m_events_seen[EventCallbacks::TIMEOUT] = true;
                break;
            default:
                break;
        }
    }
    m_eventqueue->unlock();
    
    if ( sigchld_pipe_available == true ) {
	    char buf[1] = { '\0' };
	    fd_set rdfs;
	    struct timeval timeout;
	    int sel_ret;
	
	    FD_ZERO( &rdfs );
	    FD_SET( sigchld_pipe[0], &rdfs );

	    timeout.tv_sec = 0;
	    timeout.tv_usec = 0;

	    // since events may come from other sources too
	    // it is possible that the message came from a previous
	    // select and that the waitthread did the new select already before
	    // I was able to clear the pipe. So next time, I will get another
	    // EQ_FD2 message and will read the pipe which will be empty and
	    // therefore block.
	    // scenario: main thread issues select but gets a timeout event
	    // from another thread (normalmode). waitthread does select but
	    // before pushing the message, the main thread finished handling
	    // the timeout and issues another select. The first fd2 message
	    // arrives, the waitthread does another select and after that
	    // the main thread clears the pipe. Another fd2 message will arrive.
	    sel_ret = select( sigchld_pipe[0] + 1, &rdfs, NULL, NULL, &timeout );
	    if ( sel_ret == 1 && FD_ISSET( sigchld_pipe[0], &rdfs ) ) {
		    int tres __attribute__((unused)) = read( sigchld_pipe[0], &buf[0], 1 );
	    }
	    sigchld_pipe_available = false;
	    if ( buf[0] == '\0' ) m_event_store.check_children = true;
    }

    if ( inotify_events ) {
        std::list< std::string > l = m_pathname_watcher->getChangedPaths();

        for ( std::list< std::string >::const_iterator it1 = l.begin();
              it1 != l.end();
              ++it1 ) {
            m_changed_paths.insert( *it1 );
        }
    }
}

void Worker::showCommandFailed( int status )
{
    std::string s = AGUIXUtils::formatStringToString( catalog.getLocale( 941 ),
                                                      status );
    TimedText temp_sbtext2( s, time( NULL ), time( NULL ) + 5 );
    pushStatebarText( temp_sbtext2 );
}

void Worker::setIgnoreKey( KeySym key, bool state )
{
    if ( state == true ) {
        if ( m_ignored_keys.count( key ) == 0 ) {
            m_ignored_keys[key] = 1;
        } else {
            m_ignored_keys[key]++;
        }
    } else {
        if ( m_ignored_keys.count( key ) > 1 ) {
            m_ignored_keys[key]--;
        } else if ( m_ignored_keys.count( key ) == 1 ) {
            m_ignored_keys.erase( key );
        }
    }
}

bool Worker::isIgnoredKey( KeySym key ) const
{
    if ( m_ignored_keys.count( key ) != 0 ) return true;
    return false;
}

void Worker::updateSettingsFromWConfig()
{
    StringComparator::setStringCompareMode( wconfig->getUseStringCompareMode() );
    aguix->setApplyWindowDialogType( wconfig->getApplyWindowDialogType() );
    aguix->applyFaces( wconfig->getFaceDB() );

    WCFiletype::setUseExtendedRegEx( wconfig->getUseExtendedRegEx() );
    CompRegEx::setUseExtendedRegEx( wconfig->getUseExtendedRegEx() );
    getKVPStore().setIntValue( "use-extended-regex", wconfig->getUseExtendedRegEx() ? 1 : 0 );
}

EventCallbacks &Worker::getEventHandler()
{
    return m_event_handler;
}

void Worker::fireSeenEvents()
{
    for ( std::map< EventCallbacks::event_callback_type_t, bool >::const_iterator it1 = m_events_seen.begin();
          it1 != m_events_seen.end();
          it1++ ) {
        if ( it1->second == true ) {
            m_event_handler.fireCallbacks( it1->first );
        }

        if ( it1->first == EventCallbacks::TIMEOUT ) {
            aguix->external_timeout_callback();
        }
    }
    m_events_seen.clear();
}

TemporaryFileCache::TemporaryFileCacheEntry Worker::getTempCopy( const std::string &filename,
                                                                 CopyfileProgressCallback *progress_callback )
{
    return m_temp_file_cache.getCopy( filename, progress_callback );
}

DeepPathStore &Worker::getPathStore()
{
    return m_path_store;
}

bool Worker::storePathPers( const std::string &path, time_t ts )
{
    if ( m_pers_path_store.get() == NULL ) return false;

    if ( ! wconfig->getPathJumpAllowDirs().empty() ) {
        const std::list< std::string > &l = wconfig->getPathJumpAllowDirs();

        for ( std::list< std::string >::const_iterator it1 = l.begin();
              it1 != l.end();
              it1++ ) {
            if ( AGUIXUtils::starts_with( path, *it1 ) ) {
                m_pers_path_store->storePath( path, ts );
                removePathPersArchive( path );
                return true;
            }
        }
    }

    return false;
}

bool Worker::storePathPers( const std::string &path,
                            const std::string &command_str,
                            time_t ts )
{
    char **args;
    auto n = Worker_buildArgvList( command_str.c_str(), &args );
    
    if ( n <= 0 ) {
        Worker_freeArgvList( args );
        return false;
    }
    
    std::string command = args[0];

    Worker_freeArgvList( args );

    if ( command.empty() ||
         AGUIXUtils::starts_with( command, "." ) ) {
        return false;
    }

    if ( path.empty() ) {
        return false;
    }

    if ( m_pers_path_store.get() == NULL ) return false;

    if ( ! wconfig->getPathJumpAllowDirs().empty() ) {
        const std::list< std::string > &l = wconfig->getPathJumpAllowDirs();

        for ( std::list< std::string >::const_iterator it1 = l.begin();
              it1 != l.end();
              it1++ ) {
            if ( AGUIXUtils::starts_with( path, *it1 ) ) {
                m_pers_path_store->storePathByKeyword( command, path, ts );
                return true;
            }
        }
    }

    return false;
}

DeepPathStore Worker::getPathsPers()
{
    if ( m_pers_path_store.get() == NULL ) return DeepPathStore();

    return m_pers_path_store->getCopy();
}

bool Worker::storePathPersArchive( const std::string &path, time_t ts )
{
    if ( m_pers_path_store_archive.get() == NULL ) return false;

    if ( ! wconfig->getPathJumpAllowDirs().empty() ) {
        const std::list< std::string > &l = wconfig->getPathJumpAllowDirs();

        for ( std::list< std::string >::const_iterator it1 = l.begin();
              it1 != l.end();
              it1++ ) {
            if ( AGUIXUtils::starts_with( path, *it1 ) ) {
                m_pers_path_store_archive->storePath( path, ts );
                return true;
            }
        }
    }

    return false;
}

DeepPathStore Worker::getPathsPersArchive()
{
    if ( m_pers_path_store_archive.get() == NULL ) return DeepPathStore();

    return m_pers_path_store_archive->getCopy();
}

std::map< std::string, DeepPathStore > Worker::getPathProgramsPers()
{
    if ( m_pers_path_store.get() == NULL ) return std::map< std::string, DeepPathStore >();

    return m_pers_path_store->getKeywordCopy();
}

loff_t Worker::getSizeOfPersPaths() const
{
    if ( m_pers_path_store.get() == NULL ) return 0;

    return m_pers_path_store->getLastSize();
}

loff_t Worker::getSizeOfPersPathsArchive() const
{
    if ( m_pers_path_store_archive.get() == NULL ) return 0;

    return m_pers_path_store_archive->getLastSize();
}

loff_t Worker::getSizeOfPersPathPrograms() const
{
    if ( m_pers_path_store.get() == NULL ) return 0;

    return m_pers_path_store->getLastProgSize();
}

void Worker::removePathPers( const std::string &path )
{
    if ( m_pers_path_store.get() == NULL ) return;

    m_pers_path_store->removeEntry( path );
}

void Worker::removePathPersArchive( const std::string &path )
{
    if ( m_pers_path_store_archive.get() == NULL ) return;

    m_pers_path_store_archive->removeEntry( path );
}

void Worker::removePathProgramPers( const std::string &program )
{
    if ( m_pers_path_store.get() == NULL ) return;

    m_pers_path_store->removeKeyword( program );
}

void Worker::relocateEntriesPers( const std::string &dest,
                                  const std::string &source,
                                  time_t ts,
                                  bool move )
{
    if ( m_pers_path_store.get() == NULL ) return;

    if ( ! wconfig->getPathJumpAllowDirs().empty() ) {
        const std::list< std::string > &l = wconfig->getPathJumpAllowDirs();

        for ( std::list< std::string >::const_iterator it1 = l.begin();
              it1 != l.end();
              it1++ ) {
            if ( AGUIXUtils::starts_with( dest, *it1 ) ) {
                m_pers_path_store->relocateEntries( dest, source, ts, move );
                break;
            }
        }
    }
}

void Worker::showNextHint()
{
    if ( wconfig->getClockbarHints() == false ) return;

    time_t now = time( NULL );

    if ( m_hint_time == 0 || now - m_hint_time > HINT_UPDATE_TIME ) {
        if ( m_hint_db.get() != NULL ) {
            m_current_hint = m_hint_db->getNextHint();

            m_hint_time = now;
        }
    }
}

void Worker::updateHintDB()
{
#ifdef USEOWNCONFIGFILES
    if ( 1 ) {
        std::string f1 = "hints";
#else
    if ( Worker::getDataDir().length() > 0 ) {
        std::string f1 = Worker::getDataDir();
        f1 += "/hints-";

        if ( std::string( wconfig->getLang() ) != "builtin" ) {
            f1 += wconfig->getLang();
        } else {
            f1 += "/hints-english";
        }
        if ( UTF8::getUTF8Mode() != UTF8::UTF8_DISABLED && UTF8::getCurrentEncoding() == UTF8::ENCODING_UTF8 ) {
            f1 += ".utf8";
        }
#endif

        if ( Datei::fileExists( f1.c_str() ) == true ) {
            m_hint_db.reset( new HintDB( f1, *wconfig ) );
        } else {
            std::string f2 = Worker::getDataDir();
            f2 += "/hints-english";
            if ( UTF8::getUTF8Mode() != UTF8::UTF8_DISABLED && UTF8::getCurrentEncoding() == UTF8::ENCODING_UTF8 ) {
                f2 += ".utf8";
            }

            if ( Datei::fileExists( f2.c_str() ) == true ) {
                m_hint_db.reset( new HintDB( f2, *wconfig ) );
            }
        }
    }
}

RefCount< PathnameWatcher > Worker::getPathnameWatcher()
{
    return m_pathname_watcher;
}

bool Worker::checkEnterDir( const std::string &dir )
{
    std::string str1;
    bool allow_entering = true;
    
#ifdef HAVE_AVFS
    if ( AGUIXUtils::starts_with( dir, "/#ssh:" ) && m_ssh_allow != SSH_ALWAYS ) {
        if ( m_ssh_allow == SSH_NEVER ) {
            allow_entering = false;
        } else {
            bool var1;
            int erg;
            
            // show requester
            str1 = catalog.getLocale( 629 );
            str1 += "|";
            str1 += catalog.getLocale( 8 );
            
            var1 = false;
            
            erg = getRequester()->request_choose( catalog.getLocale( 123 ),
                                                  catalog.getLocale( 630 ),
                                                  catalog.getLocale( 631 ), var1,
                                                  str1.c_str() );
            if ( var1 == true ) {
                if ( erg != 0 ) {
                    m_ssh_allow = SSH_NEVER;
                } else {
                    m_ssh_allow = SSH_ALWAYS;
                }
            }
            
            if ( erg != 0 ) {
                allow_entering = false;
            }
        }
    }
#endif

    if ( allow_entering == true ) {
        if ( worker_access( dir.c_str(), R_OK | X_OK ) != 0 ) {
            char *tstr2;
            const char *myerror;
            
            // show requester
            myerror = strerror( errno );
            tstr2 = (char*)_allocsafe( strlen( catalog.getLocale( 532 ) ) +
                                       strlen( dir.c_str() ) +
                                       strlen( myerror ) + 1 );
            sprintf( tstr2, catalog.getLocale( 532 ), dir.c_str(), myerror );
            getRequester()->request( catalog.getLocale( 347 ), tstr2, catalog.getLocale( 11 ) );
            _freesafe( tstr2 );
            
            allow_entering = false;
        }
    }

    return allow_entering;
}

mode_t Worker::getUMask()
{
    return m_process_umask;
}

template< class X > static std::string getKeyDescription( X *x )
{
    List *doublekeys = x->getDoubleKeys();
    std::string res;

    if ( doublekeys ) {
        int id_k = doublekeys->initEnum();

        WCDoubleShortkey *dk = (WCDoubleShortkey*)doublekeys->getFirstElement( id_k );

        while ( dk != NULL ) {
            std::string str1 = dk->getName( Worker::getAGUIX() );

            if ( res.empty() ) {
                res = str1;
            } else {
                res += ", ";
                res += str1;
            }
            
            dk = (WCDoubleShortkey*)doublekeys->getNextElement( id_k );
        }
        doublekeys->closeEnum( id_k );
    }                

    return res;
}

void Worker::initWorkerTextMenu( MenuTreeNode *menu )
{
    MenuTreeNode *node;

    node = menu->insertChild( catalog.getLocale( 208 ), "", "" );
    node->setActivateFunction( [this]() { about(); } );

    node = menu->insertChild( catalog.getLocale( 248 ), "", "" );
    node->setActivateFunction( [this]() { configure(); } );

    node = menu->insertChild( catalog.getLocale( 788 ), "", "" );
    node->setActivateFunction( [this]() { saveWorkerState(); } );

    node = menu->insertChild( catalog.getLocale( 789 ), "", "" );
    node->setActivateFunction( [this]() { quit( 0 ); } );
}

void Worker::prepareMenuTree( MenuTreeUI &menuui )
{
    MenuTree *menutree = new MenuTree();
    m_menutree_element.clear();

    menuui.setMenuTree( menutree );

    MenuTreeNode *root = menutree->getRootNode();

    m_menutree_element[ WorkerTypes::TOP_LEVEL ] = root;

    MenuTreeNode *menu = root->insertChild( catalog.getLocale( 1004 ), "", "" );

    initWorkerTextMenu( menu );

    m_menutree_element[ WorkerTypes::MENUS ] = menu;

    MenuTreeNode *buttonmenu = root->insertChild( catalog.getLocale( 558 ), "", "" );
    m_menutree_element[ WorkerTypes::BUTTONS ] = buttonmenu;

    int rows, columns, id, pos;
    rows = wconfig->getRows();
    columns = wconfig->getColumns();
    List *buttonlist = wconfig->getButtons();
    id = buttonlist->initEnum();
    WCButton *b1=(WCButton*)buttonlist->getFirstElement( id );
    pos = 0;

    std::string last_bank_str;
    MenuTreeNode *bank = NULL;

    buttonmenu->setHighlightFunction( [this]()
                                      {
                                          m_commandmenu_original_button_banknr = bbanknr;
                                      } );
    buttonmenu->setDeHighlightFunction( [this]()
                                        {
                                            switchToButtonBank( m_commandmenu_original_button_banknr );
                                        } );

    while ( b1 != NULL ) {
        int banknr = pos / ( rows * columns * 2);
        std::string bank_str = AGUIXUtils::formatStringToString( catalog.getLocale( 1005 ), banknr + 1 );

        if ( bank_str != last_bank_str || bank == NULL ) {
            bank = buttonmenu->insertChild( bank_str, "", "" );

            bank->setHighlightFunction( [this,banknr]()
                                        {
                                            switchToButtonBank( banknr );
                                        } );

            last_bank_str = bank_str;
        }

        if( b1->getCheck() == true ) {
            if ( ! b1->getComs().empty() ) {
                if ( ! checkCommandListForCommand( b1->getComs(),
                                                   CommandMenuOp::name ) ) {
                    MenuTreeNode *e = bank->insertChild( b1->getText(),
                                                         getStringReprForList( b1->getComs() ),
                                                         getKeyDescription( b1 ) );
                    e->setActivateFunction( [this, pos]() { activateButton( pos ); } );

                    int x, y, state;

                    x = ( pos / 2 ) % columns;
                    y = ( ( pos / 2 ) / columns ) % rows;
                    state = pos % 2 + 1;

                    e->setHighlightFunction( [x,y,this,columns,state]()
                                             {
                                                 buttons[ y * columns + x ]->setState( state );
                                             } );
                    e->setDeHighlightFunction( [x,y,this,columns]()
                                               {
                                                   buttons[ y * columns + x ]->setState( 0 );
                                               } );
                }
            }
        }
        b1=(WCButton*)buttonlist->getNextElement(id);
        pos++;
    }
    buttonlist->closeEnum( id );

    MenuTreeNode *hotkeymenu = root->insertChild( catalog.getLocale( 91 ), "", "" );
    m_menutree_element[ WorkerTypes::HOTKEYS ] = hotkeymenu;

    List *hotkeylist = wconfig->getHotkeys();
    id = hotkeylist->initEnum();
    WCHotkey *hk1 = (WCHotkey*)hotkeylist->getFirstElement( id );
    pos = 0;

    while ( hk1 != NULL ) {
        if ( ! hk1->getComs().empty() ) {
            if ( ! checkCommandListForCommand( hk1->getComs(),
                                               CommandMenuOp::name ) ) {
                MenuTreeNode *e = hotkeymenu->insertChild( hk1->getName(),
                                                           getStringReprForList( hk1->getComs() ),
                                                           getKeyDescription( hk1 ) );
                e->setActivateFunction( [this, pos]() { activateHotkey( pos ); } );
            }
        }
        hk1 = (WCHotkey*)hotkeylist->getNextElement( id );
        pos++;
    }
    hotkeylist->closeEnum( id );

    MenuTreeNode *pathmenu = root->insertChild( catalog.getLocale( 1006 ), "", "" );
    m_menutree_element[ WorkerTypes::PATHS ] = pathmenu;

    List *pathlist = wconfig->getPaths();
    id = pathlist->initEnum();
    WCPath *p1 = (WCPath*)pathlist->getFirstElement( id );

    bank = NULL;
    last_bank_str.clear();
    pos = 0;

    pathmenu->setHighlightFunction( [this]()
                                    {
                                        m_commandmenu_original_path_banknr = pbanknr;
                                    } );
    pathmenu->setDeHighlightFunction( [this]()
                                      {
                                          pbanknr = m_commandmenu_original_path_banknr;
                                          showPathBank();
                                      } );

    while ( p1 != NULL ) {
        int banknr = pos / rows;
        std::string bank_str = AGUIXUtils::formatStringToString( catalog.getLocale( 1005 ), banknr + 1 );

        if ( bank_str != last_bank_str || bank == NULL ) {
            bank = pathmenu->insertChild( bank_str, "", "" );

            bank->setHighlightFunction( [this,banknr]()
                                        {
                                            pbanknr = banknr;
                                            showPathBank();
                                        } );

            last_bank_str = bank_str;
        }

        if ( p1->getCheck() == true ) {
            MenuTreeNode *e = bank->insertChild( p1->getName(), "",
                                                 getKeyDescription( p1 ) );
            e->setActivateFunction( [this, pos]() { setPath( pos ); } );

            int y;

            y = pos % rows;

            e->setHighlightFunction( [y,this]()
                                     {
                                         pathbs[ y ]->setState( 1 );
                                     } );
            e->setDeHighlightFunction( [y,this]()
                                       {
                                           pathbs[ y ]->setState( 0 );
                                       } );
        }
        p1 = (WCPath*)pathlist->getNextElement( id );
        pos++;
    }
    pathlist->closeEnum( id );

    MenuTreeNode *lvmmenu = root->insertChild( catalog.getLocale( 1016 ), "", "" );
    m_menutree_element[ WorkerTypes::LV_MODES ] = lvmmenu;

    {
        MenuTreeNode *e = lvmmenu->insertChild( "vdm-mode", catalog.getLocale( 1018 ),
                                                "" );
        e->setActivateFunction( [this]() { switchCurrentMode( 0 ); } );
    }
    {
        MenuTreeNode *e = lvmmenu->insertChild( "image-mode", catalog.getLocale( 1019 ),
                                                "" );
        e->setActivateFunction( [this]() { switchCurrentMode( 1 ); } );
    }
    {
        MenuTreeNode *e = lvmmenu->insertChild( "info-mode", catalog.getLocale( 1020 ),
                                                "" );
        e->setActivateFunction( [this]() { switchCurrentMode( 2 ); } );
    }
    {
        MenuTreeNode *e = lvmmenu->insertChild( "text-mode", catalog.getLocale( 1021 ),
                                                "" );
        e->setActivateFunction( [this]() { switchCurrentMode( 3 ); } );
    }
    {
        MenuTreeNode *e = lvmmenu->insertChild( "mode-conf", catalog.getLocale( 1022 ),
                                                "" );
        e->setActivateFunction( [this]() { configureCurrentMode(); } );
    }
    {
        MenuTreeNode *registeredmenu = lvmmenu->insertChild( catalog.getLocale( 1023 ), "",
                                                "" );
        m_menutree_element[ WorkerTypes::LV_COMMANDS ] = registeredmenu;

        Lister *l1 = lister[0];
                
        if ( l1 != NULL ) {
            ListerMode *lm1 = l1->getActiveMode();

            if ( lm1 != NULL ) {
                auto l = lm1->getListOfCommandsWithoutArgs();
                for ( auto &e : lm1->getListOfCommandsWithArgs() ) {
                    l.push_back( e );
                }

                l.sort( []( const auto &lhs,
                            const auto &rhs ) {
                    return lhs.name < rhs.name;
                } );

                for ( auto &cmd : l ) {
                    std::string description;

                    description = cmd.description;
                    if ( cmd.has_args ) {
                        description = AGUIXUtils::formatStringToString( catalog.getLocale( 1393 ),
                                                                        description.c_str() );
                    }
                    
                    MenuTreeNode *e = registeredmenu->insertChild( cmd.name,
                                                                   description,
                                                                   "" );
                    e->setActivateFunction( [this, name = cmd.name, need_args = cmd.has_args]() {
                        if ( auto tl1 = getActiveLister() ) {
                            if ( auto tlm1 = tl1->getActiveMode() ) {
                                if ( need_args ) {
                                    if ( auto wpu = getCurrentWPU() ) {
                                        std::string args;
                                        if ( requestCommandArgs( name, args ) ) {
                                            std::string res_str;
                                            if ( wpu->parse( args.c_str(), res_str, a_max( EXE_STRING_LEN - 1024, 256 ),
                                                             true,
                                                             WPUContext::PERSIST_NOTHING ) == WPUContext::PARSE_SUCCESS ) {
                                                std::string command = name;
                                                command += " ";
                                                command += res_str;
                                                tlm1->runCommandWithStrArgs( command );
                                            }
                                        }
                                    }
                                } else {
                                    tlm1->runCommand( name );
                                }
                            }
                        }
                    } );
                }
            }
        }
    }

    MenuTreeNode *commandsmenu = root->insertChild( catalog.getLocale( 245 ), "", "" );
    m_menutree_element[ WorkerTypes::COMMANDS ] = commandsmenu;

    std::vector< std::tuple< std::string, std::string, int, bool > > command_names;

    for ( int i = 0; i < getNrOfCommands(); i++ ) {
        auto tfpr = getCommand4ID( i );

        command_names.push_back( std::make_tuple( tfpr->getName(),
                                                  tfpr->getDescription(),
                                                  i,
                                                  tfpr->isInteractiveRun() ) );
    }

    std::sort( command_names.begin(),
               command_names.end(), []( const std::tuple< std::string, std::string, int, bool > &lhs,
                                        const std::tuple< std::string, std::string, int, bool > &rhs ) {
                   return std::get<1>( lhs ) < std::get<1>( rhs );
               } );

    for ( auto &op : command_names ) {
        MenuTreeNode *e = commandsmenu->insertChild( std::get<1>( op ),
                                                     "",
                                                     "" );
        int id = std::get<2>( op );
        
        e->setActivateFunction( [ this, id ]() {
                activateCommandByID( id );
            } );

        if ( std::get<3>( op ) ) {
            MenuTreeNode *e = commandsmenu->insertChild( std::get<1>( op ) + " (interactive)",
                                                         "",
                                                         "" );
            e->setActivateFunction( [ this, id ]() {
                    activateCommandByID( id, true );
                } );
        }
    }
}

void Worker::openCommandMenu( bool show_recently_used, WorkerTypes::command_menu_node start_node )
{
    if ( ! m_menu_active ) {
        m_menu_active = true;

        aguix->setTransientWindow( mainwin );

        MenuTreeNode *initial_node = NULL;

        if ( m_menutree_element.count( start_node ) > 0 ) {
            initial_node = m_menutree_element[ start_node ];
        }

        m_menu_ui->show( show_recently_used, initial_node );

        m_menu_active = false;
    }
}

bool Worker::checkCommandListForCommand( const command_list_t &list, const std::string &name )
{
    int res = false;

    for ( auto &fp : list ) {
        if ( fp->isName( name.c_str() ) == true ) {
            res = true;
        }
    }

    return res;
}

worker_version Worker::getLastRunningVersion()
{
    std::string home = WorkerInitialSettings::getInstance().getConfigBaseDir();
    std::string str1;
    Datei fh;

    // if no file could be found, use the last version without this feature
    worker_version res( 3, 1, 0 );

    if ( home.length() < 1 ) return res;
  
    str1 = home;
    str1 = NWC::Path::join( str1, "last_running_version" );
#ifdef USEOWNCONFIGFILES
    str1 += "-dev";
#endif

    if ( fh.open( str1.c_str(), "r" ) == 0 ) {
        char *cs = fh.getLine();

        if ( cs != NULL ) {
            std::string s = cs;
            _freesafe( cs );

            std::vector< std::string > v;

            AGUIXUtils::split_string( v,
                                      s,
                                      '.' );

            if ( v.size() == 3 ) {
                int major, minor, patch;

                if ( AGUIXUtils::convertFromString( v[0],
                                                    major ) &&
                     AGUIXUtils::convertFromString( v[1],
                                                    minor ) &&
                     AGUIXUtils::convertFromString( v[2],
                                                    patch ) ) {
                    res = worker_version( major,
                                          minor,
                                          patch );
                }
            }
        }
    }

    return res;
}

void Worker::updateRunningVersion()
{
    std::string home = WorkerInitialSettings::getInstance().getConfigBaseDir();
    std::string str1;
    Datei fh;

    if ( home.length() < 1 ) return;
  
    str1 = home;
    str1 = NWC::Path::join( str1, "last_running_version" );
#ifdef USEOWNCONFIGFILES
    str1 += "-dev";
#endif

    if ( fh.open( str1.c_str(), "w" ) == 0 ) {
        std::string s = AGUIXUtils::formatStringToString( "%d.%d.%d",
                                                          WORKER_MAJOR,
                                                          WORKER_MINOR,
                                                          WORKER_PATCH );

        fh.putLine( s.c_str() );

        if ( fh.errors() != 0 ) {
            //TODO: Requester zeigen?
            fprintf( stderr, "Worker:error while writing current version to last_running_version\n" );
        }
        fh.close();
    }

    return;
}

void Worker::checkForUpdates( const worker_version &last_running_version,
                              WConfig *conf )
{
    worker_version my_version( WORKER_MAJOR,
                               WORKER_MINOR,
                               WORKER_PATCH );
    int config_major = 0, config_minor = 0, config_patch = 0;

    conf->getLoadedVersion( config_major, config_minor, config_patch  );

    worker_version base_version( config_major,
                                 config_minor,
                                 config_patch );

    if ( last_running_version > base_version ) {
        base_version = last_running_version;
    }
    if ( base_version < my_version ) {
        std::string update_dir;
#ifdef USEOWNCONFIGFILES
        update_dir = "./config-updates";
#else
        update_dir = Worker::getDataDir();
        update_dir += "/config-updates";
#endif

        char *s = NWC::Path::handlePathExt( update_dir.c_str() );
        update_dir = s;
        _freesafe( s );

        if ( ! update_dir.empty() &&
             NWC::FSEntry( update_dir ).entryExists() ) {

            NWC::Dir ud( update_dir );

            ud.readDir( false );

            std::list< std::string> entries = ud.getFullnamesOfSubentries();
            std::list< std::pair< worker_version, std::string > > updates;

            for ( auto e : entries ) {
                std::string::size_type p1 = e.rfind( "update-" );

                if ( p1 != std::string::npos ) {
                    std::string s1( e, p1 + 7 );

                    std::string::size_type p2;

                    if ( UTF8::getUTF8Mode() != UTF8::UTF8_DISABLED &&
                         UTF8::getCurrentEncoding() == UTF8::ENCODING_UTF8 ) {
                        p2 = s1.rfind( "-utf8.conf" );
                    } else {
                        if ( s1.rfind( "-utf8.conf" ) != std::string::npos ) {
                            p2 = std::string::npos;
                        } else {
                            p2 = s1.rfind( ".conf" );
                        }
                    }

                    if ( p2 != std::string::npos ) {
                        std::string s2( s1, 0, p2 );

                        std::vector< std::string > v;

                        AGUIXUtils::split_string( v,
                                                  s2,
                                                  '.' );

                        if ( v.size() >= 3 ) {
                            int major, minor, patch;

                            if ( AGUIXUtils::convertFromString( v[0],
                                                                major ) &&
                                 AGUIXUtils::convertFromString( v[1],
                                                                minor ) &&
                                 AGUIXUtils::convertFromString( v[2],
                                                                patch ) ) {

                                worker_version wv = worker_version( major,
                                                                    minor,
                                                                    patch );

                                updates.push_back( std::make_pair( wv,
                                                                   e ) );
                            }
                        }
                    }
                }

            }

            updates.sort( []( const std::pair< worker_version, std::string > &left,
                              const std::pair< worker_version, std::string > &right ) {
                              return left.first < right.first;
                          } );

            int successful_updates = 0;

            for ( auto e : updates ) {
                if ( e.first > base_version &&
                     e.first <= my_version ) {
                    if ( importUpdate( conf, e ) ) {
                        successful_updates++;
                    }
                }
            }

            if ( successful_updates > 0 ) {
                conf->save();
            }
        }
    }

    if ( last_running_version < my_version ) {
        updateRunningVersion();
    }
}

bool Worker::importUpdate( WConfig *baseconfig,
                           const std::pair< worker_version, std::string > &config_entry )
{
    WConfig *conf = NULL;
    bool import_buttons = false, import_hotkeys = false, import_filetypes = false;
    int erg = 0;
    bool config_changed = false;

    WConfig::setAlwaysKeepOldKey( false );

    std::string info_text = AGUIXUtils::formatStringToString( catalog.getLocale( 1012),
                                                              config_entry.first.m_major,
                                                              config_entry.first.m_minor,
                                                              config_entry.first.m_patch );

    if ( baseconfig->loadImportConfig( config_entry.second.c_str(),
                                       import_buttons,
                                       import_hotkeys,
                                       import_filetypes,
                                       info_text,
                                       &conf ) == 0 ) {
        if ( import_buttons == true ) {
            erg = baseconfig->fixDoubleKeys( baseconfig->getButtons(),
                                             baseconfig->getPaths(),
                                             baseconfig->getHotkeys(),
                                             conf->getButtons(), NULL );
            if ( erg == 0 ) {
                erg = baseconfig->addButtons( conf->getButtons() );
            }
            config_changed = true;
        }
        if ( import_hotkeys == true ) {
            if ( erg == 0 ) {
                erg = baseconfig->fixDoubleKeys( baseconfig->getButtons(),
                                                 baseconfig->getPaths(),
                                                 baseconfig->getHotkeys(),
                                                 NULL,
                                                 conf->getHotkeys() );
            }
            if ( erg == 0 ) {
                erg = baseconfig->addHotkeys( conf->getHotkeys() );
            }
            config_changed = true;
        }
        if ( import_filetypes == true ) {
            if ( erg == 0 ) {
                erg = baseconfig->addFiletypes( conf->getFiletypes(), true );
                config_changed = true;
            }
        }

        if ( import_buttons == false && import_hotkeys == false && import_filetypes == false ) {
            getRequester()->request( catalog.getLocale( 124 ), catalog.getLocale( 694 ), catalog.getLocale( 11 ) );
        } else if ( erg == 0 ) {
            getRequester()->request( catalog.getLocale( 124 ), catalog.getLocale( 430 ), catalog.getLocale( 11 ) );
        }
    }
        
    if ( conf != NULL ) {
        delete conf;
    }

    return config_changed;
}

void Worker::switchCurrentMode( int mode )
{
    Lister *l1 = lister[0];
    if ( lister[1]->isActive() == true ) l1 = lister[1];

    l1->switch2Mode( mode );
}

void Worker::configureCurrentMode()
{
    Lister *l1;
                
    l1 = getActiveLister();
    if ( l1 != NULL ) {
        ListerMode *lm1 = l1->getActiveMode();

        if ( lm1 != NULL ) {
            lm1->configure();
        }
    }
}

std::string Worker::getOverrideXIMPath() const
{
    std::string str1 = WorkerInitialSettings::getInstance().getConfigBaseDir();
    str1 = NWC::Path::join( str1, "override-xim" );
    return str1;
}

void Worker::setOverrideXIM( bool nv )
{
    if ( nv == true ) {
        aguix->setOverrideXIM( true );

        if ( ! NWC::FSEntry( getOverrideXIMPath() ).entryExists() ) {
            Datei fp;

            fp.open( getOverrideXIMPath().c_str(), "w" );
            fp.close();
        }
    } else {
        if ( NWC::FSEntry( getOverrideXIMPath() ).entryExists() ) {
            worker_unlink( getOverrideXIMPath().c_str() );
        }
    }
}

PersistentKVP &Worker::getPersKVPStore()
{
    if ( m_pers_kvp_store.get() != NULL ) return *m_pers_kvp_store;

    std::string cfgfile = Worker::getWorkerConfigDir();
#ifdef DEBUG
    cfgfile = NWC::Path::join( cfgfile, "kvp2" );
#else
    cfgfile = NWC::Path::join( cfgfile, "kvp" );
#endif
    m_pers_kvp_store.reset( new PersistentKVP( cfgfile ) );
    return *m_pers_kvp_store;
}

KVPStore &Worker::getKVPStore()
{
    return m_kvp_store;
}

bool Worker::storePathPersIfInProgress( const std::string &path )
{
    bool store_entry = m_path_jump_in_progress;
    bool res = false;

    if ( wconfig->getPathJumpStoreFilesAlways() ) {
        store_entry = true;
    }

    if ( store_entry && ! path.empty() ) {
        bool allowed = storePathPers( path, time( NULL ) );

        if ( ! m_path_jump_last_filter.empty() ) {
            if ( allowed ) {
                getDirHistPrefixDB().pushAccess( m_path_jump_last_filter, path, time( NULL ) );
            }
            m_path_jump_last_filter = "";
        }

        res = allowed;

        m_path_jump_in_progress = false;
    }

    return res;
}

void Worker::setPathJumpInProgress( bool nv )
{
    m_path_jump_in_progress = nv;
}

void Worker::setPathJumpInProgress( const std::string last_filter )
{
    m_path_jump_in_progress = true;
    m_path_jump_last_filter = last_filter;
}

PrefixDB &Worker::getDirHistPrefixDB()
{
    if ( ! m_pdb ) throw 1;

    return *m_pdb;
}

void Worker::setKeyChainCandidateActive( bool nv )
{
    m_key_chain_cand_active = nv;
}

bool Worker::getKeyChainCandidateActive() const
{
    return m_key_chain_cand_active;
}

void Worker::showButtonCommandHelp( int i, int j, int state )
{
    removeStatebarText( m_button_help_text );

    List *l1;
    WCButton *b1;
    int id, pos;
    int rows, columns;

    rows = wconfig->getRows();
    columns = wconfig->getColumns();
    l1 = wconfig->getButtons();
    id = l1->initEnum();
    pos = 2 * ( bbanknr * rows * columns + i * columns + j ) + ( ( state == 1 ) ? 0 : 1 );
    b1 = (WCButton*)l1->getElementAt( id, pos );
    l1->closeEnum( id );

    if ( ! b1 ) return;

    if ( ! b1->getCheck() ) return;

    std::string help = getStringReprForList( b1->getComs() );

    if ( ! help.empty() ) {
        std::string s = AGUIXUtils::formatStringToString( catalog.getLocale( 1134 ),
                                                          help.c_str() );

        m_button_help_text = TimedText( s,
                                        time( NULL ),
                                        time( NULL ) + 10 );

        pushStatebarText( m_button_help_text );
    }
}

void Worker::activateCommandByID( int id, bool interactive )
{
    ActionMessage amsg( this );
    amsg.mode = amsg.AM_MODE_NORMAL;

    command_list_t commandlist = {};
    auto op = getCommand4ID( id );
    if ( op ) {
        if ( interactive && op->isInteractiveRun() ) {
            op->setInteractiveRun();
        }

        commandlist.push_back( op );
    }

    interpret( commandlist, &amsg );
}

void Worker::enqueueFuture( std::future< ExistenceTest::existence_state_t > f )
{
    m_existence_futures.push_back( std::move( f ) );
}

void Worker::cleanFutures()
{
    m_existence_futures.clear();
}

void Worker::checkFutures()
{
    for ( auto it = m_existence_futures.begin();
          it != m_existence_futures.end();
          ) {
        std::future_status status = it->wait_for( std::chrono::duration<int>::zero() );

        auto next_it = it;
        next_it++;

        if ( status == std::future_status::ready ) {
            m_existence_futures.erase( it );
        } else {
            // only check the first one
            break;
        }

        it = next_it;
    }
}

std::list< WorkerTypes::reg_command_info_t > Worker::getListOfCommandsWithoutArgs() const
{
    Lister *l1 = lister[0];
                
    if ( l1 != NULL ) {
        ListerMode *lm1 = l1->getActiveMode();

        if ( lm1 != NULL ) {
            return lm1->getListOfCommandsWithoutArgs();
        }
    }

    return {};
}

WorkerTypes::reg_command_info_t Worker::getCommandInfo( const std::string &command, bool args ) const
{
    Lister *l1 = lister[0];
                
    if ( l1 != NULL ) {
        ListerMode *lm1 = l1->getActiveMode();

        if ( lm1 != NULL ) {
            return lm1->getCommandInfo( command, args );
        }
    }

    return {};
}

std::string Worker::getTerminalCommand( const std::string &cmdfile,
                                        const std::string &outfile,
                                        bool &remove_after_exec )
{
    std::string exestr;

    if ( wconfig->getTerminalReturnsEarly() ) {
        std::string wrapper = Worker::getDataDir();
        wrapper += "/scripts/";
        wrapper += "terminal_wrapper.sh";

        if ( Datei::fileExistsExt( wrapper.c_str() ) == Datei::D_FE_FILE ) {
            std::string suffix;
            exestr = AGUIXUtils::replace_percent_s_quoted( wconfig->getTerminalStr(), wrapper, &suffix );
            exestr += " ";
            exestr = AGUIX_catTrustedAndUnTrusted2( exestr,
                                                    cmdfile );
            exestr += " ";
            exestr = AGUIX_catTrustedAndUnTrusted2( exestr,
                                                    outfile );
            exestr += " ";
            exestr = AGUIX_catTrustedAndUnTrusted2( exestr,
                                                    cmdfile );
            exestr += suffix;
            
            remove_after_exec = false;

            return exestr;
        }
    }

    exestr = AGUIXUtils::replace_percent_s_quoted( wconfig->getTerminalStr(), cmdfile, NULL );
    remove_after_exec = true;

    return exestr;
}

void Worker::issueConfigOpen( const std::string &panel )
{
    m_pending_config_open = panel;
}

void Worker::issueConfigOpen( const std::string &panel, const WConfigInitialCommand &cmd )
{
    m_pending_config_open = panel;
    m_pending_config_command = cmd;
}

void Worker::handleHelp()
{
    if ( m_help_active ) {
        disableHelp();
        return;
    }

    m_help_message = TimedText( catalog.getLocale( 1346 ),
                                time( NULL ) );
        
    pushStatebarText( m_help_message );
    m_help_active = true;
}

void Worker::disableHelp()
{
    if ( ! m_help_active ) return;

    m_help_active = false;

    lastkey = 0;
    lastmod = 0;

    removeStatebarText( m_help_message );
}

void Worker::showHelp( KeySym lastkey,
                       unsigned int lastmod,
                       const std::list< WCButton * > &button_list,
                       const std::list< WCHotkey * > &hotkey_list,
                       const std::list< WCPath * > &path_list )
{
    AWindow *mainwin = new AWindow( aguix, 10, 10, 50, 10, catalog.getLocale( 1347 ) );
    mainwin->create();

    const AWindow *twin = aguix->getTransientWindow();
    if ( twin != NULL ) {
        mainwin->setTransientForAWindow( twin );
    }

    AContainer *maincont = mainwin->setContainer( new AContainer( mainwin, 1, 3 ), true );

    maincont->setMinSpace( 5 );
    maincont->setMaxSpace( 5 );

    std::string info = catalog.getLocale( 1348 );
    if ( lastkey != 0 ) {
        char *tstr1 = aguix->getNameOfKey( lastkey, lastmod );

        if ( tstr1 != NULL ) {
            info = AGUIXUtils::formatStringToString( catalog.getLocale( 1349 ), tstr1 );
        }
        _freesafe( tstr1 );
    }

    maincont->add( new Text( aguix, 0, 0, info.c_str() ), 0, 0 , AContainer::CO_INCWNR );

    FieldListView *lv = (FieldListView*)maincont->add( new FieldListView( aguix, 0, 0, 500, 200,
                                                                          0 ),
                                                       0, 1, AContainer::CO_MIN );

    lv->setHBarState( 2 );
    lv->setVBarState( 2 );
    lv->setAcceptFocus( true );
    lv->setDisplayFocus( true );
    lv->setNrOfFields( 3 );
    lv->setGlobalFieldSpace( 5 );
    lv->setShowHeader( true );
    lv->setFieldText( 0, catalog.getLocale( 1350 ) );
    lv->setFieldText( 1, catalog.getLocale( 1351 ) );
    lv->setFieldText( 2, catalog.getLocale( 1352 ) );
    lv->setDefaultColorMode( FieldListView::PRECOLOR_NOTSELORACT );

    for ( auto &b : button_list ) {
        if ( ! b ) continue;

        int row = lv->addRow();

        lv->setText( row, 0, AGUIXUtils::formatStringToString( catalog.getLocale( 1353 ), b->getText() ) );
        lv->setText( row, 1, getKeyDescription( b ) );
        lv->setText( row, 2, getStringReprForList( b->getComs() ) );
    }
    for ( auto &h : hotkey_list ) {
        if ( ! h ) continue;

        int row = lv->addRow();

        lv->setText( row, 0, AGUIXUtils::formatStringToString( catalog.getLocale( 1354 ), h->getName() ) );
        lv->setText( row, 1, getKeyDescription( h ) );
        lv->setText( row, 2, getStringReprForList( h->getComs() ) );
    }
    for ( auto &p : path_list ) {
        if ( ! p ) continue;

        int row = lv->addRow();

        lv->setText( row, 0, AGUIXUtils::formatStringToString( catalog.getLocale( 1355 ), p->getName() ) );
        lv->setText( row, 1, getKeyDescription( p ) );
        lv->setText( row, 2, p->getPath() );
    }

    AContainer *subcont = maincont->add( new AContainer( mainwin, 3, 1 ), 0, 2 );
    subcont->setMinSpace( 5 );
    subcont->setMaxSpace( -1 );
    subcont->setBorderWidth( 0 );

    Button *closeb = subcont->addWidget( new Button( aguix,
                                                     0,
                                                     0,
                                                     catalog.getLocale( 633 ),
                                                     0 ), 1, 0, AContainer::CO_FIX );

    mainwin->contMaximize( true );
    mainwin->show();

    AGMessage *agmsg;

    int end_mode = 0;

    for ( ; end_mode == 0; ) {
        agmsg = aguix->WaitMessage( NULL );

        if ( agmsg ) {
            switch ( agmsg->type ) {
                case AG_CLOSEWINDOW:
                    end_mode = -1;
                    break;
                case AG_BUTTONCLICKED:
                    if ( agmsg->button.button == closeb ) {
                        end_mode = 1;
                    }
                    break;
                case AG_KEYPRESSED:
                    if ( agmsg->key.key == XK_Return ) {
                        end_mode = 1;
                    } else if ( agmsg->key.key == XK_Escape ) {
                        end_mode = -1;
                    }

                    break;
            }

            aguix->ReplyMessage( agmsg );
        }
    }

    delete mainwin;

    disableHelp();
}

std::string Worker::getButtonHelpText( int relative_pos_in_bank ) const
{
    List *l1;
    int id;
    WCButton *b1, *b2;
    int rows, columns, pos;
    std::string res;

    rows = wconfig->getRows();
    columns = wconfig->getColumns();
    pos = 2 * ( bbanknr * rows * columns + relative_pos_in_bank );

    l1 = wconfig->getButtons();
    id = l1->initEnum();

    b1 = (WCButton*)l1->getElementAt( id, pos );
    b2 = (WCButton*)l1->getElementAt( id, pos + 1 );

    l1->closeEnum( id );

    if ( b1 != NULL ) {
        if ( b1->getCheck() == true ) {
            std::string command_descr = getStringReprForList( b1->getComs() );

            if ( command_descr.size() > 120 ) {
                int new_len = UTF8::movePosBackToValidPos( command_descr.c_str(), 120 );
                if ( new_len >= 0 ) {
                    command_descr.resize( new_len );
                } else {
                    command_descr.clear();
                }
                command_descr += "...";
            }
            
            res = AGUIXUtils::formatStringToString( "%s %s\n  %s",
                                                    catalog.getLocale( 1371 ),
                                                    b1->getText(),
                                                    command_descr.c_str() );
            std::string key = getKeyDescription( b1 );

            if ( ! key.empty() ) {
                res += AGUIXUtils::formatStringToString( "\n  %s %s",
                                                         catalog.getLocale( 1373 ),
                                                         key.c_str() );
            }
        }
    }
    if ( b2 != NULL ) {
        if ( b2->getCheck() == true ) {
            std::string command_descr = getStringReprForList( b2->getComs() );

            if ( command_descr.size() > 120 ) {
                int new_len = UTF8::movePosBackToValidPos( command_descr.c_str(), 120 );
                if ( new_len >= 0 ) {
                    command_descr.resize( new_len );
                } else {
                    command_descr.clear();
                }
                command_descr += "...";
            }
            
            res += "\n\n";
            res += AGUIXUtils::formatStringToString( "%s %s\n  %s",
                                                     catalog.getLocale( 1372 ),
                                                     b2->getText(),
                                                     command_descr.c_str() );
            std::string key = getKeyDescription( b2 );

            if ( ! key.empty() ) {
                res += AGUIXUtils::formatStringToString( "\n  %s %s",
                                                         catalog.getLocale( 1373 ),
                                                         key.c_str() );
            }
        }
    }

    return res;
}

std::shared_ptr< WPUContext > Worker::getCurrentWPU()
{
    return m_current_wpu.lock();
}

void Worker::setCurrentWPU( std::shared_ptr< WPUContext > wpu )
{
    m_current_wpu = wpu;
}

void Worker::pushCommandLog( const std::string &command,
                             const std::string &path,
                             const std::string &src,
                             const std::string &description )
{
    const std::lock_guard< std::mutex > lock( m_command_log_mutex );

    if ( ! m_command_log ) {
        m_command_log = std::make_unique< FileCommandLog >();
    }

    m_command_log->push( command, path, src, description );
}

std::list< FileCommandLog::Entry > Worker::getCommandLog() const
{
    const std::lock_guard< std::mutex > lock( m_command_log_mutex );

    if ( ! m_command_log ) {
        return {};
    }

    return m_command_log->entries();
}

void Worker::clearCommandLog()
{
    const std::lock_guard< std::mutex > lock( m_command_log_mutex );

    if ( m_command_log ) {
        m_command_log->clear();
    }
}

std::shared_ptr< FilterStash > Worker::getFilterStash()
{
    return m_filter_stash;
}

void Worker::checkErrnoCount()
{
    if ( ErrorCount::errno_count( ENOMEM ) != m_last_enomem_count ) {
        // ENOMEM happened in some stat calls, check if this is
        // releated to special AVFS events
        Datei fh;
        if ( fh.open( "/#avfsstat/xz_memlimit_hit", "r" ) == 0 ) {
            char *str = fh.getLine();

            if ( str ) {
                std::string line( str );
                _freesafe( str );

                size_t v;

                if ( AGUIXUtils::convertFromString( line, v ) ) {
                    if ( v != m_last_avfs_xz_memlimit_hit ) {
                        m_last_avfs_xz_memlimit_hit = v;

                        tryIncreaseXZMemlimit();
                    }
                }
            }

        }

        m_last_enomem_count = ErrorCount::errno_count( ENOMEM );
    }
}

void Worker::tryIncreaseXZMemlimit()
{
    if ( worker_islocal( "/#avfsstat/xz_memlimit" ) ) {
        return;
    }

    Datei fh;
    if ( fh.open( "/#avfsstat/xz_memlimit", "r+" ) == 0 ) {
        char *str = fh.getLine();

        if ( str ) {
            std::string line( str );
            _freesafe( str );

            size_t v;

            if ( AGUIXUtils::convertFromString( line, v ) ) {
                auto current_limit = AGUIXUtils::bytes_to_human_readable_f( (loff_t)v, 0, 0 );
                auto text = AGUIXUtils::formatStringToString( catalog.getLocale( 1452 ),
                                                              current_limit.c_str() );
                std::string button = catalog.getLocale( 1451 );
                button += "|";
                button += catalog.getLocale( 225 );

                int res = req_static->request( catalog.getLocale( 123 ),
                                               text.c_str(),
                                               button.c_str() );

                if ( res == 0 ) {
                    char buf[128];

                    snprintf( buf, sizeof( buf ), "%lu\n", v * 2 );

                    fh.seek( 0, SEEK_SET );

                    fh.putString( buf );
                }
            }
        }
    }
}

int Worker::requestCommandArgs( const std::string &name,
                                std::string &result )
{
    std::string buttonstr = catalog.getLocale( 629 );
    buttonstr += "|";
    buttonstr += catalog.getLocale( 8 );
    char *return_str = NULL;

    std::string info = AGUIXUtils::formatStringToString( catalog.getLocale( 1392 ),
                                                         name.c_str() );

    auto tl1 = getActiveLister();
    ListerMode *tlm1 = nullptr;
    if ( tl1 ) {
        tlm1 = tl1->getActiveMode();
    }

    int res;

    if ( tlm1 && tlm1->commandHasInputHelp( name ) ) {
        Requester::request_options options;
        res = getRequester()->string_request( catalog.getLocale( 123 ), info.c_str(), "", buttonstr.c_str(), &return_str, NULL, Requester::REQUEST_NONE,
                                              options,
                                              catalog.getLocale( 1478 ),
                                              [&name, tlm1]( auto &before, auto &after) -> std::string {
                                                  return tlm1->getCommandInputHelp( name, before );
                                              } );
    } else {
        res = getRequester()->string_request( catalog.getLocale( 123 ), info.c_str(), "", buttonstr.c_str(), &return_str );
    }

    if ( res == 0 ) {
        result = return_str;
        _freesafe( return_str );

        return 1;
    }

    return 0;
}

void Worker::initClockbarFlagReplacer()
{
    m_clockbar_flag_producer = std::make_shared< FlagReplacer::FlagProducerCallback >( nullptr );
    m_clockbar_flag_replacer = std::make_shared< FlagReplacer >( m_clockbar_flag_producer );

    m_clockbar_flag_producer->registerFlag( "hint", [this]( void *user_data ) {
        return m_current_hint;
    } );

    m_clockbar_flag_producer->registerFlag( "buttonbank", [this]( void *user_data ) {
        return AGUIXUtils::formatStringToString( "%d", bbanknr + 1 );
    } );

    m_clockbar_flag_producer->registerFlag( "pathbank", [this]( void *user_data ) {
        return AGUIXUtils::formatStringToString( "%d", pbanknr + 1 );
    } );

    m_clockbar_flag_producer->registerFlagWithOption( "date", []( const std::string &option, void *user_data ) {
        time_t timep;
        struct tm timeptr;
        char buf[128];

        time( &timep );
        if ( localtime_r( &timep, &timeptr ) ) {
            if ( strftime( buf, sizeof( buf ),
                           option.c_str(),
                           &timeptr ) != 0 ) {
                return std::string( buf );
            }
        }

        return std::string();
    } );

    m_clockbar_flag_producer->registerFlag( "time", []( void *user_data ) {
        time_t timep;
        char *str;
        struct tm *timeptr;
        char buf[128];

        time( &timep );
        timeptr = localtime( &timep );
        str = asctime_r( timeptr, buf );
        if ( str ) {
            str[ strlen( str ) - 1 ] = 0;
            return std::string( str );
        }

        return std::string();
    } );

    m_clockbar_flag_producer->registerFlag( "free", []( void *user_data ) {
        return get_free_memory_string();
    } );

    m_clockbar_flag_producer->registerFlag( "version", []( void *user_data ) {
        return get_version_string();
    } );
}
