/* startprogop.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2001-2023 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 "startprogop.h"
#include "listermode.h"
#include "worker.h"
#include <string>
#include "aguix/kartei.h"
#include "aguix/acontainerbb.h"
#include "execlass.h"
#include "basic_actions.h"
#include "wcfiletype.hh"
#include "avfssupport.hh"
#include "fileentry.hh"
#include "nmspecialsourceext.hh"
#include "fileviewer.hh"
#include "processexitaction.hh"
#include "datei.h"
#include "worker_locale.h"
#include "aguix/fieldlistview.h"
#include "aguix/cyclebutton.h"
#include "aguix/choosebutton.h"
#include "aguix/stringgadget.h"
#include "aguix/button.h"
#include "aguix/textview.h"
#include "wconfig.h"
#include "flattypelist.hh"
#include "argclass.hh"
#include "nwc_path.hh"
#include "pers_string_list.hh"
#include "wpucontext.h"
#include "ownop.h"
#include "ajson.hh"
#include "textviewmode.hh"
#include "virtualdirmode.hh"

const char *StartProgOp::name="StartProgOp";

StartProgOp::StartProgOp() : FunctionProto()
{
  startprogstart=STARTPROGOP_START_NORMAL;

  view_str = "";
  
  global=false;
  inbackground = false;
  dontcd = false;
  hasConfigure = true;

  gui_msg = "";

  m_cmd_sg = nullptr;
  m_cmd_flag_b = nullptr;
}

StartProgOp::~StartProgOp()
{
}

StartProgOp*
StartProgOp::duplicate() const
{
  StartProgOp *ta=new StartProgOp();
  ta->startprogstart=startprogstart;

  ta->view_str = view_str;
  ta->global=global;
  ta->inbackground = inbackground;
  ta->dontcd = dontcd;
  ta->m_follow_active = m_follow_active;
  ta->m_watch_file = m_watch_file;
  ta->m_separate_each_entry = m_separate_each_entry;
  return ta;
}

bool
StartProgOp::isName(const char *str)
{
  if(strcmp(str,name)==0) return true; else return false;
}

const char *
StartProgOp::getName()
{
  return name;
}

int
StartProgOp::run( std::shared_ptr< WPUContext > wpu, ActionMessage *msg )
{
  if(msg->mode!=msg->AM_MODE_DNDACTION) {
    Lister *l1 = msg->getWorker()->getActiveLister();
    if(l1!=NULL) {
      startlister=l1;
      endlister = msg->getWorker()->getOtherLister(startlister);
      startprog( wpu, msg );
    }
  }
  return 0;
}

bool
StartProgOp::save(Datei *fh)
{
  if ( fh == NULL ) return false;
  switch(startprogstart) {
    case STARTPROGOP_START_IN_TERMINAL:
      fh->configPutPair( "start", "terminal" );
      break;
    case STARTPROGOP_START_IN_TERMINAL_AND_WAIT4KEY:
      fh->configPutPair( "start", "terminalwait" );
      break;
    case STARTPROGOP_SHOW_OUTPUT:
      fh->configPutPair( "start", "showoutput" );
      break;
    case STARTPROGOP_SHOW_OUTPUT_INT:
      fh->configPutPair( "start", "showoutputint" );
      break;
    case STARTPROGOP_SHOW_OUTPUT_OTHER_SIDE:
      fh->configPutPair( "start", "showoutputotherside" );
      break;
    case STARTPROGOP_SHOW_OUTPUT_CUSTOM_ATTRIBUTE:
      fh->configPutPair( "start", "showoutputcustomattribute" );
      break;
    default:
      fh->configPutPair( "start", "normal" );
      break;
  }
  fh->configPutPairString( "viewstr", view_str.c_str() );
  fh->configPutPairBool( "global", global );
  fh->configPutPairBool( "inbackground", inbackground );
  fh->configPutPairBool( "dontcd", dontcd );

  if ( m_follow_active ) {
      fh->configPutPairBool( "followactive", m_follow_active );
  }

  if ( m_watch_file ) {
      fh->configPutPairBool( "watchfile", m_watch_file );
  }

  if ( m_separate_each_entry ) {
      fh->configPutPairBool( "separateeachentry", m_separate_each_entry );
  }

  return true;
}

const char *
StartProgOp::getDescription()
{
  return catalog.getLocale(1273);
}

/* replace backslash with double backslash and backslash { and } */
static std::string protectForParsing( const char *str1 )
{
    std::string res;

    if ( str1 != NULL ) {
        for ( ; *str1 != '\0'; str1++ ) {
            switch ( *str1 ) {
                case '\\':
                    res += "\\\\";
                    break;
                case '{':
                    res += "\\{";
                    break;
                case '}':
                    res += "\\}";
                    break;
                default:
                    res += *str1;
                    break;
            }
        }
    }

    return res;
}

int
StartProgOp::startprog( std::shared_ptr< WPUContext > wpu, ActionMessage *am )
{
  ListerMode *lm1 = NULL;

  char *tmpname, *tmpoutput, *tstr;
  std::string string1;
  bool useint;
  bool removeTMPFiles;
  std::string fullname;

  if ( startlister == NULL ) return 1;
  lm1 = startlister->getActiveMode();
  if ( lm1 == NULL ) return 1;

  std::list< NM_specialsourceExt > filelist;

  lm1->getSelFiles( filelist, ListerMode::LM_GETFILES_ONLYACTIVE );

  const FileEntry *tfe;
  int tfe_id = -1;
  if ( am->mode == am->AM_MODE_SPECIAL ) {
    // Copy of FE is not necessary because it's already duplicated in ActionMessage
    tfe = am->getFE();
  } else if ( ! filelist.empty() &&
              filelist.begin()->entry() != NULL ) {
    tfe = filelist.begin()->entry();
    tfe_id = filelist.begin()->getID();
  } else {
    tfe = NULL;
  }
  if ( tfe != NULL ) {
      std::string cwd = lm1->getCurrentDirectory();

      default_file = tfe->name;
      fullname = tfe->fullname;

      if ( ! cwd.empty() ) {
          std::string name_string = NWC::Path::get_extended_basename( cwd,
                                                                      tfe->fullname );
          if ( ! name_string.empty() ) {
              default_file = name_string;
          }
      }

      if ( tfe->filetype ) {
          m_default_file_filetype = tfe->filetype->getName();
      } else {
          m_default_file_filetype = "";
      }

      if ( tfe->filetype && tfe->filetype != wconfig->getunknowntype() ) {
          m_default_file_unknown = false;
      } else {
          m_default_file_unknown = true;
      }
      m_default_file_mime_type = tfe->getMimeType();
  } else {
    default_file = "";
    m_default_file_unknown = false;
  }

  std::unique_ptr< StartSettings > m1;
  m1 = showGUI();
  if ( m1->_startmode == StartSettings::STARTPROG &&
       m1->_start == STARTPROGOP_SHOW_OUTPUT_OTHER_SIDE ) {
      endlister->switch2Mode( 3 );
      auto tvm = dynamic_cast< TextViewMode * >( endlister->getActiveMode() );
      if ( tvm ) {
          uint32_t options = 0;
          if ( m1->m_follow_active ) {
              options |= TextViewMode::FOLLOW_ACTIVE;
          }
          if ( m1->m_watch_file ) {
              options |= TextViewMode::WATCH_FILE;
          }
          tvm->setCommandString( m1->_command,
                                 lm1->getCurrentDirectory(),
                                 options );
      }
  } else if ( m1->_startmode == StartSettings::STARTPROG &&
              m1->_start == STARTPROGOP_SHOW_OUTPUT_CUSTOM_ATTRIBUTE ) {
      auto vdm = dynamic_cast< VirtualDirMode * >( lm1 );
      if ( vdm ) {
          bool prev_use_virtual_copies = wpu->getUseVirtualTempCopies();
          wpu->setUseVirtualTempCopies( true );
          vdm->processCustomAttribute( am,
                                       { .command_str = m1->_command,
                                         .dont_cd = dontcd,
                                         .global_command = true,
                                         .separate_each_entry = m1->m_separate_each_entry
                                       },
                                       tfe,
                                       tfe_id );
          wpu->setUseVirtualTempCopies( prev_use_virtual_copies );
      }
  } else if ( m1->_startmode == StartSettings::STARTPROG ) {
    tmpname = Datei::createTMPName();
    tmpoutput = Datei::createTMPName();

    if ( tmpname != NULL && tmpoutput != NULL ) {
      removeTMPFiles = true;

      std::string tmpoutput_lnk;

      RefCount< GenericCallbackArg< void, int > > pea( NULL );
      
      if ( m1->_start == STARTPROGOP_SHOW_OUTPUT_INT ) {
          tmpoutput_lnk = Datei::createTMPHardlink( tmpoutput );

          pea.reset( new ProcessExitAction( true,
                                            tmpoutput_lnk,
                                            am->getWorker(), wpu ) );
      } else {
          pea.reset( new ProcessExitAction( false,
                                            "",
                                            am->getWorker(), wpu ) );
      }

      Datei fp;
      if ( fp.open( tmpname, "w" ) == 0 ) {
        fp.putString( "#! /bin/sh\n" );
        if ( ! lm1->getCurrentDirectory().empty() ) {
            if ( ( worker_islocal( lm1->getCurrentDirectory().c_str() ) == 1 ) &&
                 ( m1->_dontcd == false ) ) {
                tstr = AGUIX_catTrustedAndUnTrusted( "cd ", lm1->getCurrentDirectory().c_str() );
                fp.putString( tstr );
                _freesafe( tstr );
                fp.putString( "\n" );
            }
        }
        if ( m1->_global == false ) fp.putString( "./" );

        std::string res_str;
        std::string quoted_tmpoutput = AGUIX_catTrustedAndUnTrusted2( "", tmpoutput );

        if ( wpu->parse( m1->_command.c_str(),
                         res_str,
                         EXE_STRING_LEN - quoted_tmpoutput.size() - 3,
                         true,
                         WPUContext::PERSIST_FLAGS ) == WPUContext::PARSE_SUCCESS ) {
            string1 = Worker_secureCommandForShell( res_str.c_str() );

            if ( m1->_global ) {
                // as for now only store global programs and the file
                // in the persistent database
                if ( wpu->getPathDBFilename().empty() ) {
                    am->getWorker()->storePathPers( fullname,
                                                    res_str,
                                                    time( NULL ) );
                } else {
                    am->getWorker()->storePathPers( wpu->getPathDBFilename(),
                                                    res_str,
                                                    time( NULL ) );
                }
            }
        }

        if ( string1.length() > 0 ) {
          if ( m1->_start == STARTPROGOP_SHOW_OUTPUT ||
               m1->_start == STARTPROGOP_SHOW_OUTPUT_INT ||
               m1->_start == STARTPROGOP_SHOW_OUTPUT_CUSTOM_ATTRIBUTE ) {
            string1 += " >";
            string1 += quoted_tmpoutput;
          }
          fp.putString( string1.c_str() );
          fp.putString( "\n" );
        }

        std::string exestr;
        bool remove_after_exec = true;

        if ( ( m1->_start == STARTPROGOP_START_IN_TERMINAL_AND_WAIT4KEY )||
             ( m1->_start == STARTPROGOP_START_IN_TERMINAL ) ) {
          /* in terminal starten */
          if ( m1->_start == STARTPROGOP_START_IN_TERMINAL_AND_WAIT4KEY ) {
            fp.putStringExt( "echo %s\n", catalog.getLocale( 201 ) );
            fp.putString( "read x\n" );
          }

          exestr = Worker::getTerminalCommand( tmpname, tmpoutput, remove_after_exec );
        } else {
            exestr = AGUIXUtils::replace_percent_s_quoted( "/bin/sh %s", tmpname, NULL );
        }
        if ( m1->_start == STARTPROGOP_SHOW_OUTPUT ) {
          useint = true;
          if ( m1->_view_str.length() > 2 ) {
              if ( strstr( m1->_view_str.c_str(), "%s" ) != NULL ) {
                  useint = false;
              }
          }

          std::string real_view_str;

          if ( useint == true ) {
              real_view_str = AGUIXUtils::replace_percent_s_quoted( OUTPUT_BIN, tmpoutput, NULL );
          } else {
              real_view_str = AGUIXUtils::replace_percent_s_quoted( m1->_view_str, tmpoutput, NULL );
          }
          fp.putString( real_view_str.c_str() );

          fp.putString( "\n" );
        }
        fp.close();

        if ( fp.errors() == 0 ) {
          worker_chmod( tmpname, 0700 );
          removeTMPFiles = false;
          am->getWorker()->runCommand( exestr.c_str(),
                                       remove_after_exec ? tmpname : NULL,
                                       remove_after_exec ? tmpoutput : NULL,
                                       m1->_inbackground, pea );
        } else {
          Requester req( Worker::getAGUIX() );
          char *textstr = (char*)_allocsafe( strlen( catalog.getLocale( 647 ) ) + strlen( tmpname ) + 1 );

          sprintf( textstr, catalog.getLocale( 647 ), tmpname );
          std::string button = catalog.getLocale( 11 );
          req.request( catalog.getLocale( 347 ), textstr, button.c_str() );
          _freesafe( textstr );
        }
      }
      if ( removeTMPFiles == true ) {
        worker_unlink( tmpname );
        worker_unlink( tmpoutput );

        if ( pea.get() != NULL ) {
            pea->callback( 0 );
        }
      }
    } else {
      Requester req( Worker::getAGUIX() );
      char *textstr = (char*)_allocsafe( strlen( catalog.getLocale( 647 ) ) + strlen( "" ) + 1 );
      
      sprintf( textstr, catalog.getLocale( 647 ), "" );
      std::string button = catalog.getLocale( 11 );
      req.request( catalog.getLocale( 347 ), textstr, button.c_str() );
      _freesafe( textstr );
    }
    
    if ( tmpname != NULL ) _freesafe( tmpname );
    if ( tmpoutput != NULL ) _freesafe( tmpoutput );
  } else if ( m1->_startmode == StartSettings::HANDLE_TYPE ) {
    WCFiletype *ft = m1->_type;
    if ( ( ft != NULL ) && ( tfe != NULL ) ) {
      auto l = ft->getDoubleClickActions();
      ActionMessage amsg( am->getWorker() );
	
      amsg.mode = amsg.AM_MODE_SPECIAL;
	
      amsg.setFE( tfe );
      amsg.filetype = ft;
      amsg.m_action_descr = RefCount<ActionDescr>( new DoubleClickAction::DoubleClickActionDescr() );
      am->getWorker()->interpret( l, &amsg );
    }
  } else if ( m1->_startmode == StartSettings::HANDLE_ARCHIVE ) {
    std::string dir;

    if ( tfe != NULL ) {
      dir = tfe->fullname;
      dir += "#";
      dir += m1->_archive;

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

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

  if ( m1->_startmode != StartSettings::CANCEL ) {
      am->getWorker()->storePathPersIfInProgress( fullname );
  }

  return 0;
}

int
StartProgOp::configure()
{
  return doconfigure();
}

std::unique_ptr< StartProgOp::SettingsWidgets > StartProgOp::buildW( AWindow *win )
{
  SettingsWidgets *m1;

  AGUIX *aguix = Worker::getAGUIX();
  StringGadget *sg;
  CycleButton *cyb;
  ChooseButton *gcb, *ibcb, *dcdcb, *follow_active_cb, *watch_file_cb, *separate_each_entry_cb;
  const int cincw = AContainer::ACONT_MINH +
                    AContainer::ACONT_MINW +
                    AContainer::ACONT_MAXH;
  const int cincwnr = cincw +
                      AContainer::ACONT_NORESIZE;
  const int cfix = AContainer::ACONT_MINH +
                   AContainer::ACONT_MINW +
                   AContainer::ACONT_MAXH +
                   AContainer::ACONT_MAXW;
  
  AContainer *ac1 = win->setContainer( new AContainerBB( win, 1, 9 ), true );
  ac1->setMinSpace( 5 );
  ac1->setMaxSpace( 5 );

  ac1->add( new Text( aguix, 0, 0, catalog.getLocale( 638 ) ), 0, 0, cincwnr );

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

  ac1_1->add( new Text( aguix, 0, 0, catalog.getLocale( 331 ) ), 0, 0, cfix );
  cyb = (CycleButton*)ac1_1->add( new CycleButton( aguix, 0, 0, 100, 0 ), 1, 0, cincw );
  cyb->addOption(catalog.getLocale(332));
  cyb->addOption(catalog.getLocale(333));
  cyb->addOption(catalog.getLocale(334));
  cyb->addOption(catalog.getLocale(335));
  cyb->addOption( catalog.getLocale( 911 ) );
  cyb->addOption( catalog.getLocale( 1512 ) );
  cyb->addOption( catalog.getLocale( 1535 ) );
  cyb->resize(cyb->getMaxSize(),cyb->getHeight());
  switch(startprogstart) {
    case STARTPROGOP_START_IN_TERMINAL:
      cyb->setOption(1);
      break;
    case STARTPROGOP_START_IN_TERMINAL_AND_WAIT4KEY:
      cyb->setOption(2);
      break;
    case STARTPROGOP_SHOW_OUTPUT:
      cyb->setOption(3);
      break;
    case STARTPROGOP_SHOW_OUTPUT_INT:
      cyb->setOption( 4 );
      break;
    case STARTPROGOP_SHOW_OUTPUT_OTHER_SIDE:
      cyb->setOption( 5 );
      break;
    case STARTPROGOP_SHOW_OUTPUT_CUSTOM_ATTRIBUTE:
      cyb->setOption( 6 );
      break;
    default:
      cyb->setOption(0);
      break;
  }
  ac1_1->readLimits();
  cyb->setBubbleHelpText( catalog.getLocale( 1515 ) );
  
  AContainer *ac1_2 = ac1->add( new AContainer( win, 2, 1 ), 0, 2 );
  ac1_2->setMinSpace( 5 );
  ac1_2->setMaxSpace( 5 );
  ac1_2->setBorderWidth( 0 );

  ac1_2->add( new Text( aguix, 0, 0, catalog.getLocale( 337 ) ), 0, 0, cfix );
  sg = (StringGadget*)ac1_2->add( new StringGadget( aguix, 0, 0, 100, view_str.c_str(), 0 ), 1, 0, cincw );

  if ( startprogstart == STARTPROGOP_SHOW_OUTPUT ) {
      ac1_2->show();
  } else {
      ac1_2->hide();
  }

  gcb = (ChooseButton*)ac1->add( new ChooseButton( aguix, 0, 0, ( global == true ) ? 1 : 0,
						   catalog.getLocale( 343 ), LABEL_RIGHT, 0 ), 0, 3, cincwnr );
  gcb->setBubbleHelpText( catalog.getLocale( 1516 ) );
  ibcb = (ChooseButton*)ac1->add( new ChooseButton( aguix,
                                                    0,
                                                    0,
                                                    ( inbackground == true ) ? 1 : 0,
                                                    catalog.getLocale( 435 ),
                                                    LABEL_RIGHT,
                                                    0 ), 0, 4, cincwnr );
  ibcb->setBubbleHelpText( catalog.getLocale( 1517 ) );

  dcdcb = (ChooseButton*)ac1->add( new ChooseButton( aguix,
						     0,
						     0,
						     ( dontcd == true ) ? 1 : 0,
						     catalog.getLocale( 617 ),
						     LABEL_RIGHT,
						     0 ), 0, 5, cincwnr );
  dcdcb->setBubbleHelpText( catalog.getLocale( 1518 ) );

  follow_active_cb = ac1->addWidget( new ChooseButton( aguix,
                                                       0,
                                                       0,
                                                       ( m_follow_active == true ) ? 1 : 0,
                                                       catalog.getLocale( 1184 ),
                                                       LABEL_RIGHT,
                                                       0 ), 0, 6, cincwnr );
  follow_active_cb->setBubbleHelpText( catalog.getLocale( 1519 ) );
  watch_file_cb = ac1->addWidget( new ChooseButton( aguix,
                                                    0,
                                                    0,
                                                    ( m_watch_file == true ) ? 1 : 0,
                                                    catalog.getLocale( 1183 ),
                                                    LABEL_RIGHT,
                                                    0 ), 0, 7, cincwnr );
  watch_file_cb->setBubbleHelpText( catalog.getLocale( 1520 ) );

  separate_each_entry_cb = ac1->addWidget( new ChooseButton( aguix,
                                                    0,
                                                    0,
                                                    ( m_separate_each_entry == true ) ? 1 : 0,
                                                    catalog.getLocale( 329 ),
                                                    LABEL_RIGHT,
                                                    0 ), 0, 8, cincwnr );
  separate_each_entry_cb->setBubbleHelpText( catalog.getLocale( 1536 ) );

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

  m1 = new SettingsWidgets( ac1_2, sg, cyb, gcb, ibcb, dcdcb, follow_active_cb, watch_file_cb, separate_each_entry_cb );
  m1->updateVisibility();

  return std::unique_ptr< SettingsWidgets >( m1 );
}

int StartProgOp::doconfigure()
{
  AGUIX *aguix = Worker::getAGUIX();
  Button *okb,*cb;
  AWindow *win;
  AGMessage *msg;
  int endmode=-1;
  char *tstr;
  int i;
  const int cfix = AContainer::ACONT_MINH +
                   AContainer::ACONT_MINW +
                   AContainer::ACONT_MAXH +
                   AContainer::ACONT_MAXW;
  const int cmin = AContainer::ACONT_MINH +
                   AContainer::ACONT_MINW;
  
  tstr=(char*)_allocsafe(strlen(catalog.getLocale(293))+strlen(getDescription())+1);
  sprintf(tstr,catalog.getLocale(293),getDescription());
  win = new AWindow( aguix, 10, 10, 10, 10, tstr, AWindow::AWINDOW_DIALOG );
  win->create();
  _freesafe(tstr);

  AContainer *ac1 = win->setContainer( new AContainer( win, 1, 2 ), true );
  ac1->setMinSpace( 5 );
  ac1->setMaxSpace( 5 );

  AWindow *subwin1 = new AWindow( aguix, 10, 10, 10, 10, "" );
  ac1->add( subwin1, 0, 0, cmin );

  std::unique_ptr< SettingsWidgets > m1 = buildW( subwin1 );
  ac1->readLimits();

  AContainer *ac1_3 = ac1->add( new AContainer( win, 2, 1 ), 0, 1 );
  ac1_3->setMinSpace( 5 );
  ac1_3->setMaxSpace( -1 );
  ac1_3->setBorderWidth( 0 );
  okb =(Button*)ac1_3->add( new Button( aguix,
					0,
					0,
					catalog.getLocale( 11 ),
					0 ), 0, 0, cfix );
  cb = (Button*)ac1_3->add( new Button( aguix,
					0,
					0,
					catalog.getLocale( 8 ),
					0 ), 1, 0, cfix );

  okb->takeFocus();
  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==okb) {
	    if ( m1->validate() == 1 ) endmode = 0;
          } else if(msg->button.button==cb) endmode=1;
          break;
	case AG_STRINGGADGET_DEACTIVATE:
          if(msg->stringgadget.sg==m1->_sg) {
	    m1->validate();
          }
	  break;
        case AG_KEYPRESSED:
          if(win->isParent(msg->key.window,false)==true) {
            switch(msg->key.key) {
              case XK_1:
                i = m1->m_cyb->getSelectedOption() + 1;
                if ( i >= m1->m_cyb->getNrOfOptions() ) i = 0;
                m1->m_cyb->setOption( i );
                m1->updateVisibility();
                aguix->Flush();
                break;
              case XK_2:
                m1->_gcb->setState((m1->_gcb->getState()==true)?false:true);
                break;
              case XK_3:
                m1->_ibcb->setState( ( m1->_ibcb->getState() == true ) ? false : true );
                break;
              case XK_Return:
                if ( cb->getHasFocus() == false ) {
		  if ( m1->validate() == 1 ) endmode = 0;
                }
                break;
              case XK_Escape:
                endmode=1;
                break;
            }
          }
          break;
          case AG_CYCLEBUTTONCLICKED:
              if ( msg->cyclebutton.cyclebutton == m1->m_cyb ) {
                  m1->updateVisibility();
                  aguix->Flush();
              }
              break;
      }
      aguix->ReplyMessage(msg);
    }
  }
  
  if(endmode==0) {
    // ok
    view_str = m1->_sg->getText();
    switch ( m1->m_cyb->getSelectedOption() ) {
        case 1:
            startprogstart=STARTPROGOP_START_IN_TERMINAL;
            break;
        case 2:
            startprogstart=STARTPROGOP_START_IN_TERMINAL_AND_WAIT4KEY;
            break;
        case 3:
            startprogstart=STARTPROGOP_SHOW_OUTPUT;
            break;
        case 4:
            startprogstart = STARTPROGOP_SHOW_OUTPUT_INT;
            break;
        case 5:
            startprogstart = STARTPROGOP_SHOW_OUTPUT_OTHER_SIDE;
            break;
        case 6:
            startprogstart = STARTPROGOP_SHOW_OUTPUT_CUSTOM_ATTRIBUTE;
            break;
        default:
            startprogstart=STARTPROGOP_START_NORMAL;
            break;
    }
    global = m1->_gcb->getState();
    inbackground = m1->_ibcb->getState();
    dontcd = m1->_dcdcb->getState();
    m_follow_active = m1->m_follow_active_cb->getState();
    m_watch_file = m1->m_watch_file_cb->getState();
    m_separate_each_entry = m1->m_separate_each_entry_cb->getState();
  }
  
  delete win;

  return endmode;
}

void StartProgOp::setStart(startprogstart_t nv)
{
  startprogstart=nv;
}

void StartProgOp::setGlobal(bool nv)
{
  global=nv;
}

void StartProgOp::setRequestFlags(bool nv)
{
}

void StartProgOp::setInBackground( bool nv )
{
  inbackground = nv;
}

void StartProgOp::setDontCD( bool nv )
{
  dontcd = nv;
}

void StartProgOp::setFollowActive( bool nv )
{
    m_follow_active = nv;
}

void StartProgOp::setWatchFile( bool nv )
{
    m_watch_file = nv;
}

void StartProgOp::setSeparateEachEntry( bool nv )
{
    m_separate_each_entry = nv;
}

void StartProgOp::setViewStr( std::string nv )
{
  view_str = nv;
}

StartProgOp::SettingsWidgets::SettingsWidgets( AContainer *sg_ac,
                                               StringGadget *sg,
                                               CycleButton *cyb,
                                               ChooseButton *gcb,
                                               ChooseButton *ibcb,
                                               ChooseButton *dcdcb,
                                               ChooseButton *follow_active_cb,
                                               ChooseButton *watch_file_cb,
                                               ChooseButton *separate_each_entry_cb ) :
    m_sg_ac( sg_ac ),
    _sg( sg ),
    m_cyb( cyb ),
    _gcb( gcb ),
    _ibcb( ibcb ),
    _dcdcb( dcdcb ),
    m_follow_active_cb( follow_active_cb ),
    m_watch_file_cb( watch_file_cb ),
    m_separate_each_entry_cb( separate_each_entry_cb )
{
}

static bool isCorrectViewProg( const char *str )
{
    const char *pos;
    if ( strlen( str ) < 1 ) return true;
    pos = strstr( str, "%s" );
    if ( pos != NULL ) return true;
    return false;
}

int StartProgOp::SettingsWidgets::validate()
{
  const char *textstr;
  char *buttonstr;
  int erg;
  Requester req( Worker::getAGUIX() );

  if ( isCorrectViewProg( _sg->getText() ) == false ) {
    // show requester
    textstr = catalog.getLocale( 312 );
    buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 313 ) ) + 1 +
				   strlen( catalog.getLocale( 314 ) ) + 1 );
    sprintf( buttonstr, "%s|%s", catalog.getLocale( 313 ),
	     catalog.getLocale( 314 ) );
    erg = req.request( catalog.getLocale( 125 ), textstr, buttonstr );
    _freesafe( buttonstr );
    if ( erg == 0 ) {
      _sg->activate();
    } else {
      _sg->setText( "" );
    }
  } else return 1;
  return 0;
}

void StartProgOp::SettingsWidgets::updateVisibility()
{
    if ( m_cyb->getSelectedOption() == 5 ) {
        _gcb->hide();
        _ibcb->hide();
        _dcdcb->hide();
        m_follow_active_cb->show();
        m_watch_file_cb->show();
        m_separate_each_entry_cb->hide();
    } else if ( m_cyb->getSelectedOption() == 6 ) {
        _gcb->show();
        _ibcb->hide();
        _dcdcb->show();
        m_follow_active_cb->hide();
        m_watch_file_cb->hide();
        m_separate_each_entry_cb->show();
    } else {
        _gcb->show();
        _ibcb->show();
        _dcdcb->show();
        m_follow_active_cb->hide();
        m_watch_file_cb->hide();
        m_separate_each_entry_cb->hide();
    }
    if ( m_cyb->getSelectedOption() == 3 ) {
        m_sg_ac->show();
    } else {
        m_sg_ac->hide();
    }
}

std::unique_ptr< StartProgOp::StartSettings > StartProgOp::showGUI()
{
  AGUIX *aguix = Worker::getAGUIX();
  int w, h, sw;
  AWindow *win;
  int row;
  const int cincw = AContainer::ACONT_MINH +
                    AContainer::ACONT_MINW +
                    AContainer::ACONT_MAXH;
  const int cincwnr = cincw +
                      AContainer::ACONT_NORESIZE;
  const int cfix = AContainer::ACONT_MINH +
                   AContainer::ACONT_MINW +
                   AContainer::ACONT_MAXH +
                   AContainer::ACONT_MAXW;
  const int cmin = AContainer::ACONT_MINH +
                   AContainer::ACONT_MINW;
  int i;
  std::string str1;

  if ( gui_msg.length() < 1 )
    gui_msg = catalog.getLocale( 645 );

  win = new AWindow( aguix, 10, 10, 10, 10, getDescription() );
  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, gui_msg.c_str() ), 0, 0, cincwnr );

  Kartei *k1 = new Kartei( aguix, 10, 10, 10, 10, "" );
  ac1->add( k1, 0, 1, cmin );
  k1->create();

  AWindow *subwin11 = new AWindow( aguix, 0, 0, 10, 10, "" );
  k1->add( subwin11 );
  subwin11->create();
  
  AContainer *acsw11 = subwin11->setContainer( new AContainer( subwin11, 1, 3 ), true );
  acsw11->setMinSpace( 5 );
  acsw11->setMaxSpace( 5 );
  acsw11->setBorderWidth( 5 );

  RefCount<AFontWidth> lencalc( new AFontWidth( aguix, NULL ) );
  auto help_ts = std::make_shared< TextStorageString >( catalog.getLocale( 1076 ), lencalc );
  TextView *help_tv = (TextView*)acsw11->add( new TextView( aguix,
                                                            0, 0, 50, 80, "", help_ts ),
                                              0, 0, AContainer::CO_INCW );
  help_tv->setLineWrap( true );
  help_tv->maximizeX( 500 );
  help_tv->maximizeYLines( 10 );
  help_tv->showFrame( false );
  acsw11->readLimits();
  help_tv->show();
  help_tv->setAcceptFocus( false );

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

  AContainer *acsw11_1 = acsw11->add( new AContainer( subwin11, 2, 2 ), 0, 1 );
  acsw11_1->setMinSpace( 5 );
  acsw11_1->setMaxSpace( 5 );
  acsw11_1->setBorderWidth( 0 );

  std::string cmd_string;
  {
      char *tstr = AGUIX_catQuotedAndUnQuoted( "", default_file.c_str() );
      cmd_string = protectForParsing( tstr );
      _freesafe( tstr );
  }

  acsw11_1->add( new Text( aguix, 0, 0, catalog.getLocale( 639 ) ), 0, 0, cfix );

  AContainer *acsw11_1_1 = acsw11_1->add( new AContainer( subwin11, 2, 1 ), 1, 0 );
  acsw11_1_1->setMinSpace( 0 );
  acsw11_1_1->setMaxSpace( 0 );
  acsw11_1_1->setBorderWidth( 0 );

  m_cmd_sg = acsw11_1_1->addWidget( new StringGadget( aguix, 0,
                                                      0, 100, cmd_string.c_str(), 0 ), 0, 0, cincw );

  m_cmd_flag_b = acsw11_1_1->addWidget( new Button( aguix,
                                                    0,
                                                    0,
                                                    "F",
                                                    0 ), 1, 0, cfix );
  m_cmd_flag_b->setBubbleHelpText( catalog.getLocale( 1131 ) );

  acsw11_1->add( new Text( aguix, 0, 0, catalog.getLocale( 1035 ) ), 0, 1,
                 AContainer::ACONT_MINH +
                 AContainer::ACONT_MINW +
                 AContainer::ACONT_MAXW +
                 AContainer::ACONT_NORESIZE );
  m_completion_lv = acsw11_1->addWidget( new FieldListView( aguix, 0, 0, 100, 75, 0 ),
                                         1, 1,
                                         cmin );
  m_completion_lv->setHBarState( 2 );
  m_completion_lv->setVBarState( 2 );
  m_completion_lv->setNrOfFields( 3 );
  m_completion_lv->setGlobalFieldSpace( 5 );

  AWindow *subwin11_2 = new AWindow( aguix, 0, 0, 10, 10, "" );
  acsw11->add( subwin11_2, 0, 2, cmin );
  subwin11_2->create();
  std::unique_ptr< SettingsWidgets > m1 = buildW( subwin11_2 );
  acsw11->readLimits();

  AWindow *subwin12 = new AWindow( aguix, 0, 0, 10, 10, "" );
  k1->add( subwin12 );
  subwin12->create();

  std::unique_ptr< FiletypeWindow > ftw = buildFTWindow( subwin12 );

  AWindow *subwin13 = new AWindow( aguix, 0, 0, 10, 10, "" );
  k1->add( subwin13 );
  subwin13->create();

  std::unique_ptr< ArchiveWindow > arcw = buildArcWindow( subwin13 );

  str1 = catalog.getLocale( 641 );
  str1 += " - F1";
  k1->setOption( subwin11, 0, str1.c_str() );
  str1 = catalog.getLocale( 642 );
  str1 += " - F2";
  k1->setOption( subwin12, 1, str1.c_str() );
  str1 = catalog.getLocale( 643 );
  str1 += " - F3";
  k1->setOption( subwin13, 2, str1.c_str() );
  k1->maximize();
  k1->contMaximize();
  ac1->readLimits();

  AContainer *ac1_5 = ac1->add( new AContainer( win, 2, 1 ), 0, 2 );
  ac1_5->setMinSpace( 5 );
  ac1_5->setMaxSpace( -1 );
  ac1_5->setBorderWidth( 0 );
  Button *okb =(Button*)ac1_5->add( new Button( aguix,
                                                0,
                                                0,
                                                catalog.getLocale( 11 ),
                                                0 ), 0, 0, cfix );
  Button *cancelb = (Button*)ac1_5->add( new Button( aguix,
                                                     0,
                                                     0,
                                                     catalog.getLocale( 8 ),
                                                     0 ), 1, 0, cfix );
  win->setDoTabCycling( true );
  win->contMaximize( true );

  w = win->getWidth();
  h = win->getHeight();

  int rx, ry, rw, rh;

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

  sw = rw;
  sw = (int)( (double)sw * 0.8 );
  if ( sw < 200 ) sw = 200;
  if ( w < sw ) w = sw;
  win->resize( w, h );

  win->show();
  k1->show();

  m_cmd_sg->takeFocus();

  std::string cfgfile = Worker::getWorkerConfigDir();
#ifdef USEOWNCONFIGFILES
  cfgfile = NWC::Path::join( cfgfile, "startprog-history2" );
#else
  cfgfile = NWC::Path::join( cfgfile, "startprog-history" );
#endif

  PersistentStringList history( cfgfile );
  m_current_original_file = m_cmd_sg->getText();
  m_completion_enabled = true;
  m_previous_sg_content = "";

  cfgfile += "-filetype.json";

  auto filetype_history = AJSON::load( cfgfile );
  if ( ! AJSON::as_object( filetype_history ) ) {
      filetype_history = AJSON::make_object();
  }

  updateSGFromComplete( m_cmd_sg, history, filetype_history, m_completion_lv,
                        *m1 );

  AGMessage *msg;
  while((msg=aguix->GetMessage(NULL))!=NULL) aguix->ReplyMessage(msg);
  int ende=0;
  while(ende==0) {
    msg=aguix->WaitMessage(win);
    if(msg!=NULL) {
      switch ( msg->type ) {
          case AG_CLOSEWINDOW:
              if ( msg->closewindow.window == win->getWindow() ) ende = -1;
          case AG_BUTTONCLICKED:
              if ( msg->button.button == okb ) {
                  if ( m1->validate() == 1 ) ende = 1;
              } else if ( msg->button.button== cancelb ) ende = -1;
              else if ( msg->button.button == m_cmd_flag_b ) {
                  char *tstr = OwnOp::getFlag();
                  if ( tstr != NULL ) {
                      m_cmd_sg->insertAtCursor( tstr );
                      _freesafe( tstr );
                  }
              }
              break;
	case AG_STRINGGADGET_DEACTIVATE:
          if ( msg->stringgadget.sg == m1->_sg ) {
	    m1->validate();
	  }
	  break;
        case AG_FIELDLV_ONESELECT:
        case AG_FIELDLV_MULTISELECT:
          if ( msg->fieldlv.lv == arcw->_lv ) {
            row = arcw->_lv->getActiveRow();
            if ( arcw->_lv->isValidRow( row ) == true ) {
	      arcw->_sg->setText( arcw->_lv->getText( row, 0 ).c_str() );
            }
          } else if ( msg->fieldlv.lv == m_completion_lv ) {
              updateSGFromRow( msg->fieldlv.row, history, filetype_history, *m1 );
          }
          break;
	case AG_STRINGGADGET_OK:
	  if ( msg->stringgadget.sg == m_cmd_sg ) {
	    if ( m1->validate() == 1 ) ende = 1;
	  }
	  break;
    case AG_STRINGGADGET_CURSORCHANGE:
        if ( msg->stringgadget.sg == m_cmd_sg ) {
            m_completion_enabled = false;
        }
        break;
    case AG_STRINGGADGET_CONTENTCHANGE:
        if ( msg->stringgadget.sg == ftw->m_filter_sg ) {
            ftw->updateView();
        } else if ( msg->stringgadget.sg == m_cmd_sg ) {
            updateSGFromComplete( m_cmd_sg, history, filetype_history, m_completion_lv,
                                  *m1 );
        }
        break;
        case AG_KEYPRESSED:
          if ( win->isParent( msg->key.window, false ) == true ) {
            switch ( msg->key.key ) {
              case XK_1:
                i = m1->m_cyb->getSelectedOption() + 1;
                if ( i >= m1->m_cyb->getNrOfOptions() ) i = 0;
                m1->m_cyb->setOption( i );
                m1->updateVisibility();
                aguix->Flush();
                break;
              case XK_2:
                m1->_gcb->setState( ( m1->_gcb->getState() == true ) ? false : true );
                break;
              case XK_3:
                m1->_ibcb->setState( ( m1->_ibcb->getState() == true ) ? false : true );
                break;
              case XK_4:
                m1->_dcdcb->setState( ( m1->_dcdcb->getState() == true ) ? false : true );
                break;
              case XK_5:
                  m1->m_follow_active_cb->setState( ( m1->m_follow_active_cb->getState() == true ) ? false : true );
                  break;
              case XK_6:
                  m1->m_watch_file_cb->setState( ( m1->m_watch_file_cb->getState() == true ) ? false : true );
                  break;
              case XK_7:
                  m1->m_separate_each_entry_cb->setState( ( m1->m_separate_each_entry_cb->getState() == true ) ? false : true );
                  break;
              case XK_Return:
                  if ( cancelb->getHasFocus() == false ) {
                      if ( m1->validate() == 1 ) ende = 1;
                  }
                  break;
                case XK_Escape:
                    ende = -1;
                    break;
                case XK_F1:
                    //TODO Was passiert mit Focus, wenn ich per Key die Kartei wechsle?
                    k1->optionChange( 0 );
                    break;
                case XK_F2:
                    k1->optionChange( 1 );
                    break;
                case XK_F3:
                    k1->optionChange( 2 );
                    break;
                case XK_Down:
                    if ( m_completion_lv->getElements() > 0 ) {
                        int trow = m_completion_lv->getActiveRow();

                        trow++;

                        if ( trow >= m_completion_lv->getElements() ) {
                            trow = m_completion_lv->getElements() - 1;
                        }

                        m_completion_lv->setActiveRow( trow );
                        m_completion_lv->showActive();

                        updateSGFromRow( trow, history, filetype_history, *m1 );
                    }
                    break;
                case XK_Up:
                    if ( m_completion_lv->getElements() > 0 ) {
                        int trow = m_completion_lv->getActiveRow();

                        if ( trow > 0 ) {
                            trow--;
                        }

                        m_completion_lv->setActiveRow( trow );
                        m_completion_lv->showActive();

                        updateSGFromRow( trow, history, filetype_history, *m1 );
                    }
                    break;
            }
          }
          break;
          case AG_CYCLEBUTTONCLICKED:
              if ( msg->cyclebutton.cyclebutton == m1->m_cyb ) {
                  m1->updateVisibility();
                  aguix->Flush();
              }
              break;
      }
      aguix->ReplyMessage(msg);
    }
  }

  StartSettings *sets = new StartSettings();

  if ( ende == 1 ) {
      if ( k1->getCurOption() == 0 ) {
          sets->_startmode = StartSettings::STARTPROG;
          sets->_command = m_cmd_sg->getText();

          sets->_view_str = m1->_sg->getText();
          switch ( m1->m_cyb->getSelectedOption() ) {
              case 1:
                  sets->_start = STARTPROGOP_START_IN_TERMINAL;
                  break;
              case 2:
                  sets->_start = STARTPROGOP_START_IN_TERMINAL_AND_WAIT4KEY;
                  break;
              case 3:
                  sets->_start = STARTPROGOP_SHOW_OUTPUT;
                  break;
              case 4:
                  sets->_start = STARTPROGOP_SHOW_OUTPUT_INT;
                  break;
              case 5:
                  sets->_start = STARTPROGOP_SHOW_OUTPUT_OTHER_SIDE;
                  break;
              case 6:
                  sets->_start = STARTPROGOP_SHOW_OUTPUT_CUSTOM_ATTRIBUTE;
                  break;
              default:
                  sets->_start = STARTPROGOP_START_NORMAL;
                  break;
          }
          sets->_global = m1->_gcb->getState();
          sets->_inbackground = m1->_ibcb->getState();
          sets->_dontcd = m1->_dcdcb->getState();
          sets->m_follow_active = m1->m_follow_active_cb->getState();
          sets->m_watch_file = m1->m_watch_file_cb->getState();
          sets->m_separate_each_entry = m1->m_separate_each_entry_cb->getState();

          if ( AGUIXUtils::ends_with( sets->_command, m_current_original_file ) ) {
              std::string base = sets->_command;
              base.resize( base.length() - m_current_original_file.length() );

              AGUIXUtils::rstrip( base );

              if ( ! base.empty() ) {
                  if ( base[0] != ' ' ) {
                      history.removeEntry( base );
                      history.pushFrontEntry( base );

                      updateFiletypeHistory( filetype_history, base, *sets );
                  }
              }
          } else {
              std::string base = sets->_command;
              if ( AGUIXUtils::ends_with( base, "}" ) ) {
                  AGUIXUtils::rstrip( base );

                  if ( ! base.empty() ) {
                      if ( base[0] != ' ' ) {
                          history.removeEntry( base );
                          history.pushFrontEntry( base );

                          updateFiletypeHistory( filetype_history, base, *sets );
                      }
                  }
              }
          }

          AJSON::dump( filetype_history, cfgfile );
      } else if ( k1->getCurOption() == 1 ) {
          sets->_startmode = StartSettings::HANDLE_TYPE;

          row = ftw->_lv->getActiveRow();
          if ( ( ftw->_lv->isValidRow( row ) == true ) &&
               ( row < ftw->_flatlist->getNrOfEntries() ) ) {
              WCFiletype *ft = ftw->_flatlist->getEntry( row ).filetype;
              if ( ft != NULL ) {
                  sets->_type = ft;
              }
          }
      } else if ( k1->getCurOption() == 2 ) {
          sets->_startmode = StartSettings::HANDLE_ARCHIVE;
          sets->_archive = arcw->_sg->getText();
      }
  }
  delete win;

  m_cmd_sg = nullptr;
  m_cmd_flag_b = nullptr;

  return std::unique_ptr< StartSettings >( sets );
}

StartProgOp::StartSettings::StartSettings() : _startmode( CANCEL ),
                                              _type( NULL ),
                                              _start( STARTPROGOP_START_NORMAL ),
                                              _global( true ),
                                              _inbackground( false ),
                                              _dontcd( false )
{
}

std::unique_ptr< StartProgOp::FiletypeWindow > StartProgOp::buildFTWindow( AWindow *win )
{
  FiletypeWindow *m1;

  AGUIX *aguix = Worker::getAGUIX();
  FieldListView *lv;
  const int cmin = AContainer::ACONT_MINH +
                   AContainer::ACONT_MINW;
  const int cincw = AContainer::ACONT_MINH +
                    AContainer::ACONT_MINW +
                    AContainer::ACONT_MAXH;
  const int cincwnr = cincw +
                      AContainer::ACONT_NORESIZE;
  
  AContainer *ac1 = win->setContainer( new AContainerBB( win, 1, 3 ), true );
  ac1->setMinSpace( 5 );
  ac1->setMaxSpace( 5 );

  ac1->add( new Text( aguix, 0, 0, catalog.getLocale( 644 ) ), 0, 0, cincwnr );

  AContainer *ac1_sub1 = ac1->add( new AContainer( win, 2, 1 ),
                                   0, 1 );
  ac1_sub1->setMinSpace( 5 );
  ac1_sub1->setMaxSpace( 5 );

  ac1_sub1->add( new Text( aguix, 0, 0, catalog.getLocale( 793 ) ), 0, 0, AContainer::CO_FIX );
  auto sg = ac1_sub1->addWidget( new StringGadget( aguix, 0, 0, 50, "", 0 ),
                                 1, 0, cincw );

  lv = (FieldListView*)ac1->add( new FieldListView( aguix, 0, 0, 100, 100, 0 ), 0, 2, cmin );
  lv->setHBarState( 2 );
  lv->setVBarState( 2 );
  lv->setDisplayFocus( true );
  lv->setAcceptFocus( true );
  lv->setDefaultColorMode( FieldListView::PRECOLOR_ONLYACTIVE );

  std::unique_ptr< FlatTypeList > l1 = wconfig->getFlatTypeList();
  l1->buildLV( lv, NULL );

  win->contMaximize( true );
  
  m1 = new FiletypeWindow( lv, sg, std::move( l1 ) );

  return std::unique_ptr< FiletypeWindow >( m1 );
}

StartProgOp::FiletypeWindow::FiletypeWindow( FieldListView *lv,
                                             StringGadget *sg,
                                             std::unique_ptr< FlatTypeList > flatlist ) :
    _lv( lv ),
    m_filter_sg( sg ),
    _flatlist( std::move( flatlist ) )
{
}

StartProgOp::FiletypeWindow::~FiletypeWindow()
{
}

void StartProgOp::FiletypeWindow::updateView()
{
    _flatlist = std::make_unique< FlatTypeList >( wconfig->getFiletypes(),
                                                  m_filter_sg->getText() );
    _flatlist->buildLV( _lv, NULL );

    _lv->redraw();
}

std::unique_ptr< StartProgOp::ArchiveWindow > StartProgOp::buildArcWindow( AWindow *win )
{
  ArchiveWindow *m1;
  
  AGUIX *aguix = Worker::getAGUIX();
  FieldListView *lv;
  const int cmin = AContainer::ACONT_MINH +
                   AContainer::ACONT_MINW;
  const int cincw = AContainer::ACONT_MINH +
                    AContainer::ACONT_MINW +
                    AContainer::ACONT_MAXH;
  const int cincwnr = cincw +
                      AContainer::ACONT_NORESIZE;
  int row;
  std::vector<std::string> v;
  
  AContainer *ac1 = win->setContainer( new AContainerBB( win, 1, 3 ), true );
  ac1->setMinSpace( 5 );
  ac1->setMaxSpace( 5 );

  ac1->add( new Text( aguix, 0, 0, catalog.getLocale( 640 ) ), 0, 0, cincwnr );

  StringGadget *sg = (StringGadget*)ac1->add( new StringGadget( aguix, 0, 0, 100, "", 0 ), 0, 1, cincw );

  lv = (FieldListView*)ac1->add( new FieldListView( aguix, 0, 0, 100, 100, 0 ), 0, 2, cmin );
  lv->setHBarState( 2 );
  lv->setVBarState( 2 );
  lv->setDisplayFocus( true );
  lv->setAcceptFocus( true );

  v = AVFSSupport::getAVFSModules();

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

  win->contMaximize( true );
  m1 = new ArchiveWindow( lv, sg );

  return std::unique_ptr< ArchiveWindow >( m1 );
}

StartProgOp::ArchiveWindow::ArchiveWindow( FieldListView *lv,
					   StringGadget *sg ) : _lv( lv ),
								_sg( sg )
{
}

void StartProgOp::setGUIMsg( std::string msg )
{
  gui_msg = msg;
}

void StartProgOp::updateCompletionList( const std::string &base,
                                        PersistentStringList &history,
                                        std::shared_ptr< AJSON::JSONType > &filetype_history )
{

   std::list< std::string > l;
   std::set< std::string > commands_in_use;

   l = history.getList();

   m_current_completions.clear();

   auto jobj = AJSON::as_object( filetype_history );

   if ( jobj ) {
       if ( m_default_file_unknown ) {
           auto jmimetype = AJSON::as_object( jobj->get( "mimetype" ) );

           if ( jmimetype ) {
               auto jcommands = AJSON::as_array( jmimetype->get( m_default_file_mime_type ) );

               if ( jcommands ) {
                   for ( size_t pos = 0; pos < jcommands->size(); pos++ ) {
                       auto jcommand = AJSON::as_object( jcommands->get( pos ) );

                       if ( ! jcommand ) continue;

                       StartSettings settings;
                       if ( parse_history_json( jcommand, settings ) != 0 ) {
                           continue;
                       }

                       if ( AGUIXUtils::starts_with( settings._command, base ) ) {
                           m_current_completions.push_back( std::make_pair( MIMETYPE_COMPLETION,
                                                                            settings ) );
                           commands_in_use.insert( settings._command );
                       }
                   }
               }
           }
       }

       auto jft = AJSON::as_object( jobj->get( "filetype" ) );

       if ( jft ) {
           auto jcommands = AJSON::as_array( jft->get( m_default_file_filetype ) );

           if ( jcommands ) {
               for ( size_t pos = 0; pos < jcommands->size(); pos++ ) {
                   auto jcommand = AJSON::as_object( jcommands->get( pos ) );

                   if ( ! jcommand ) continue;

                   StartSettings settings;
                   if ( parse_history_json( jcommand, settings ) != 0 ) {
                       continue;
                   }

                   if ( AGUIXUtils::starts_with( settings._command, base ) ) {
                       if ( commands_in_use.count( settings._command ) == 0 ) {
                           m_current_completions.push_back( std::make_pair( FILETYPE_COMPLETION,
                                                                            settings ) );
                           commands_in_use.insert( settings._command );
                       }
                   }
               }
           }
       }

       auto jall = AJSON::as_array( jobj->get( "all" ) );

       if ( jall ) {
           for ( size_t pos = 0; pos < jall->size(); pos++ ) {
               auto jcommand = AJSON::as_object( jall->get( pos ) );

               if ( ! jcommand ) continue;

               StartSettings settings;
               if ( parse_history_json( jcommand, settings ) != 0 ) {
                   continue;
               }

               if ( AGUIXUtils::starts_with( settings._command, base ) ) {
                   if ( commands_in_use.count( settings._command ) == 0 ) {
                       m_current_completions.push_back( std::make_pair( ANY_COMPLETION,
                                                                        settings ) );
                       commands_in_use.insert( settings._command );
                   }
               }
           }
       }
   }
   
   for ( auto &s : l ) {
       if ( commands_in_use.count( s ) == 0 ) {
           if ( AGUIXUtils::starts_with( s, base ) ) {
               StartSettings settings;
               settings._command = s;

               settings._start = startprogstart;
               settings._dontcd = dontcd;
               settings._global = global;
               settings._inbackground = inbackground;
               settings._view_str = view_str;
               settings.m_follow_active = m_follow_active;
               settings.m_separate_each_entry = m_separate_each_entry;
               settings.m_watch_file = m_watch_file;

               m_current_completions.push_back( std::make_pair( ANY_COMPLETION, settings ) );
           }
       }
   }
}

void StartProgOp::apply_settings( const StartSettings &settings,
                                  SettingsWidgets &sw )
{
    sw._sg->setText( settings._view_str.c_str() );
    switch ( settings._start  ) {
        case STARTPROGOP_START_IN_TERMINAL:
            sw.m_cyb->setOption( 1 );
            break;
        case STARTPROGOP_START_IN_TERMINAL_AND_WAIT4KEY:
            sw.m_cyb->setOption( 2 );
            break;
        case STARTPROGOP_SHOW_OUTPUT:
            sw.m_cyb->setOption( 3 );
            break;
        case STARTPROGOP_SHOW_OUTPUT_INT:
            sw.m_cyb->setOption( 4 );
            break;
        case STARTPROGOP_SHOW_OUTPUT_OTHER_SIDE:
            sw.m_cyb->setOption( 5 );
            break;
        case STARTPROGOP_SHOW_OUTPUT_CUSTOM_ATTRIBUTE:
            sw.m_cyb->setOption( 6 );
            break;
        default:
            sw.m_cyb->setOption( 0 );
    }
    sw._gcb->setState( settings._global );
    sw._ibcb->setState( settings._inbackground );
    sw._dcdcb->setState( settings._dontcd );
    sw.m_follow_active_cb->setState( settings.m_follow_active );
    sw.m_watch_file_cb->setState( settings.m_watch_file );
    sw.m_separate_each_entry_cb->setState( settings.m_separate_each_entry );

    sw.updateVisibility();
}

void StartProgOp::updateSGFromComplete( StringGadget *sg,
                                        PersistentStringList &history,
                                        std::shared_ptr< AJSON::JSONType > &filetype_history,
                                        FieldListView *lv,
                                        SettingsWidgets &sw )
{
    std::string old_base;

    old_base = sg->getText();

    std::string prefix( old_base );

    if ( sg->getCursor() < (int)prefix.length() ) {
        prefix.resize( sg->getCursor() );
    }
    
    updateCompletionList( prefix, history, filetype_history );

    lv->setSize( m_current_completions.size() );

    int row = 0;
    for ( auto &s : m_current_completions ) {
        std::string s2( s.second._command );

        if ( ! AGUIXUtils::ends_with( s2, "}" ) ) {
            s2 += " ";
            s2 += m_current_original_file;
        }

        if ( s.first == FILETYPE_COMPLETION ) {
            lv->setText( row, 1, catalog.getLocale( 1321 ) );
        } else if ( s.first == MIMETYPE_COMPLETION ) {
            std::string info = AGUIXUtils::formatStringToString( catalog.getLocale( 1341 ),
                                                                 m_default_file_mime_type.c_str() );
            lv->setText( row, 1, info );
        } else {
            lv->setText( row, 1, catalog.getLocale( 1322 ) );
        }

        std::string settings = "[";

        switch ( s.second._start ) {
            case STARTPROGOP_START_IN_TERMINAL:
                settings += catalog.getLocale( 333 );
                if ( s.second._inbackground ) {
                    settings += ",";
                    settings += catalog.getLocale( 1546 );
                }
                break;
            case STARTPROGOP_START_IN_TERMINAL_AND_WAIT4KEY:
                settings += catalog.getLocale( 1540 );
                if ( s.second._inbackground ) {
                    settings += ",";
                    settings += catalog.getLocale( 1546 );
                }
                break;
            case STARTPROGOP_SHOW_OUTPUT:
                settings += catalog.getLocale( 1541 );
                if ( s.second._inbackground ) {
                    settings += ",";
                    settings += catalog.getLocale( 1546 );
                }
                break;
            case STARTPROGOP_SHOW_OUTPUT_INT:
                settings += catalog.getLocale( 1542 );
                if ( s.second._inbackground ) {
                    settings += ",";
                    settings += catalog.getLocale( 1546 );
                }
                break;
            case STARTPROGOP_SHOW_OUTPUT_OTHER_SIDE:
                settings += catalog.getLocale( 1543 );
                if ( s.second.m_follow_active ) {
                    settings += ",";
                    settings += catalog.getLocale( 1548 );
                }
                if ( s.second.m_watch_file ) {
                    settings += ",";
                    settings += catalog.getLocale( 1549 );
                }
                break;
            case STARTPROGOP_SHOW_OUTPUT_CUSTOM_ATTRIBUTE:
                settings += catalog.getLocale( 1544 );
                if ( s.second.m_separate_each_entry ) {
                    settings += ",";
                    settings += catalog.getLocale( 1547 );
                }
                break;
            default:
                settings += catalog.getLocale( 1545 );
                if ( s.second._inbackground ) {
                    settings += ",";
                    settings += catalog.getLocale( 1546 );
                }
                break;
        }

        settings += "]";

        lv->setText( row, 2, settings );

        lv->setText( row, 0, s2 );
        lv->setPreColors( row, FieldListView::PRECOLOR_ONLYACTIVE );

        row++;
    }
    lv->redraw();

    if ( ! m_completion_enabled ) return;

    if ( old_base == m_previous_sg_content ||
         old_base.length() < m_previous_sg_content.length() ) {
        m_completion_enabled = false;

        apply_settings( StartSettings(), sw );

        return;
    }

    if ( m_current_completions.empty() && ! prefix.empty() ) {
        std::string new_text( prefix );

        if ( ! AGUIXUtils::ends_with( new_text, "}" ) ) {
            new_text += " ";
            new_text += m_current_original_file;
        }

        sg->setText( new_text.c_str() );

        sg->redraw();

        m_completion_enabled = false;

        apply_settings( StartSettings(), sw );

        return;
    }

    if ( sg->getCursor() > 0 ) {
        auto settings = m_current_completions.front().second;

        std::string new_text( settings._command );

        m_previous_sg_content = new_text;

        if ( sg->getCursor() < (int)m_previous_sg_content.length() ) {
            m_previous_sg_content.resize( sg->getCursor() );
        }

        if ( ! AGUIXUtils::ends_with( new_text, "}" ) ) {
            new_text += " ";
            new_text += m_current_original_file;
        }

        sg->setText( new_text.c_str() );

        sg->setSelection( sg->getCursor(),
                          new_text.length() );

        sg->redraw();

        apply_settings( settings, sw );
    }
}

void StartProgOp::updateSGFromRow( int row,
                                   PersistentStringList &history,
                                   std::shared_ptr< AJSON::JSONType > &filetype_history,
                                   SettingsWidgets &sw )
{
    std::string t = m_completion_lv->getText( row, 0 );

    m_cmd_sg->setText( t.c_str() );
    m_cmd_sg->setSelection( m_cmd_sg->getCursor(),
                            t.length() );
    m_cmd_sg->redraw();

    m_completion_enabled = false;

    updateSGFromComplete( m_cmd_sg, history, filetype_history, m_completion_lv, sw );

    if ( row < (int)m_current_completions.size() ) {
        auto it = m_current_completions.begin();
        std::advance( it, row );

        if ( it != m_current_completions.end() ) {
            apply_settings( it->second, sw );
        }
    }
}

void StartProgOp::set_history_json( std::shared_ptr< AJSON::JSONObject > &jobj,
                                    const std::string &base,
                                    const StartSettings &settings )
{
    jobj->set( "command", AJSON::make_string( base ) );

    if ( settings._start != startprogstart ) {
        switch ( settings._start ) {
            case STARTPROGOP_START_IN_TERMINAL:
                jobj->set( "start", AJSON::make_string( "terminal" ) );
                break;
            case STARTPROGOP_START_IN_TERMINAL_AND_WAIT4KEY:
                jobj->set( "start", AJSON::make_string( "terminalwait" ) );
                break;
            case STARTPROGOP_SHOW_OUTPUT:
                jobj->set( "start", AJSON::make_string( "showoutput" ) );
                jobj->set( "viewstr", AJSON::make_string( settings._view_str ) );
                break;
            case STARTPROGOP_SHOW_OUTPUT_INT:
                jobj->set( "start", AJSON::make_string( "showoutputint" ) );
                break;
            case STARTPROGOP_SHOW_OUTPUT_OTHER_SIDE:
                jobj->set( "start", AJSON::make_string( "showoutputotherside" ) );
                break;
            case STARTPROGOP_SHOW_OUTPUT_CUSTOM_ATTRIBUTE:
                jobj->set( "start", AJSON::make_string( "showoutputcustomattribute" ) );
                break;
            default:
                jobj->set( "start", AJSON::make_string( "normal" ) );
                break;
        }
    }

    if ( settings._global != global ) {
        jobj->set( "global", AJSON::make_boolean( settings._global ) );
    }

    if ( settings._inbackground != inbackground ) {
        jobj->set( "inbackground", AJSON::make_boolean( settings._inbackground ) );
    }

    if ( settings._dontcd != dontcd ) {
        jobj->set( "dontcd", AJSON::make_boolean( settings._dontcd ) );
    }

    if ( settings.m_follow_active != m_follow_active ) {
        jobj->set( "followactive", AJSON::make_boolean( settings.m_follow_active ) );
    }

    if ( settings.m_watch_file != m_watch_file ) {
        jobj->set( "watchfile", AJSON::make_boolean( settings.m_watch_file ) );
    }

    if ( settings.m_separate_each_entry != m_separate_each_entry ) {
        jobj->set( "separateeachentry", AJSON::make_boolean( settings.m_separate_each_entry ) );
    }
}

int StartProgOp::parse_history_json( std::shared_ptr< AJSON::JSONObject > &jobj,
                                     StartSettings &settings )
{
    StartSettings res;

    res._start = startprogstart;
    res._dontcd = dontcd;
    res._global = global;
    res._inbackground = inbackground;
    res._view_str = view_str;
    res.m_follow_active = m_follow_active;
    res.m_separate_each_entry = m_separate_each_entry;
    res.m_watch_file = m_watch_file;

    if ( ! jobj ) {
        return -EINVAL;
    }

    auto jcmd = AJSON::as_string( jobj->get( "command" ) );

    if ( ! jcmd ) return -EINVAL;

    res._command = jcmd->get_value();

    auto jstart = AJSON::as_string( jobj->get( "start" ) );

    if ( jstart ) {
        if ( jstart->get_value() == "terminal" ) {
            res._start = STARTPROGOP_START_IN_TERMINAL;
        } else if ( jstart->get_value() == "terminalwait" ) {
            res._start = STARTPROGOP_START_IN_TERMINAL_AND_WAIT4KEY;
        } else if ( jstart->get_value() == "showoutput" ) {
            res._start = STARTPROGOP_SHOW_OUTPUT;
            auto jviewstr = AJSON::as_string( jobj->get( "viewstr" ) );
            if ( jviewstr ) {
                res._view_str = jviewstr->get_value();
            }
        } else if ( jstart->get_value() == "showoutputint" ) {
            res._start = STARTPROGOP_SHOW_OUTPUT_INT;
        } else if ( jstart->get_value() == "showoutputotherside" ) {
            res._start = STARTPROGOP_SHOW_OUTPUT_OTHER_SIDE;
        } else if ( jstart->get_value() == "showoutputcustomattribute" ) {
            res._start = STARTPROGOP_SHOW_OUTPUT_CUSTOM_ATTRIBUTE;
        } else {
            res._start = STARTPROGOP_START_NORMAL;
        }
    }

    auto jglobal = AJSON::as_boolean( jobj->get( "global" ) );
    if ( jglobal ) {
        res._global = jglobal->get_value();
    }
    auto jinbackground = AJSON::as_boolean( jobj->get( "inbackground" ) );
    if ( jinbackground ) {
        res._inbackground = jinbackground->get_value();
    }
    auto jdontct = AJSON::as_boolean( jobj->get( "dontcd" ) );
    if ( jdontct ) {
        res._dontcd = jdontct->get_value();
    }
    auto jfollowactive = AJSON::as_boolean( jobj->get( "followactive" ) );
    if ( jfollowactive ) {
        res.m_follow_active = jfollowactive->get_value();
    }
    auto jwatchfile = AJSON::as_boolean( jobj->get( "watchfile" ) );
    if ( jwatchfile ) {
        res.m_watch_file = jwatchfile->get_value();
    }
    auto jseparateeachentry = AJSON::as_boolean( jobj->get( "separateeachentry" ) );
    if ( jseparateeachentry ) {
        res.m_separate_each_entry = jseparateeachentry->get_value();
    }

    settings = res;

    return 0;
}

void StartProgOp::updateFiletypeHistory( std::shared_ptr< AJSON::JSONType > &filetype_history,
                                         const std::string &base,
                                         const StartSettings &settings )
{
    auto jobj = AJSON::as_object( filetype_history );

    if ( ! jobj ) return;

    auto jft = AJSON::as_object( jobj->get( "filetype" ) );

    if ( ! jft ) {
        jft = AJSON::as_object( jobj->set( "filetype", AJSON::make_object() ) );

        if ( ! jft ) return;
    }

    auto jold_commands = AJSON::as_array( jft->get( m_default_file_filetype ) );

    auto jnew_commands = AJSON::make_array();

    auto jnew_command = AJSON::make_object();
    set_history_json( jnew_command, base, settings );
    jnew_commands->append( jnew_command );

    if ( jold_commands ) {
        for ( size_t pos = 0; pos < jold_commands->size(); pos++ ) {
            auto jold_command = AJSON::as_object( jold_commands->get( pos ) );

            if ( ! jold_command ) continue;

            auto jcmd = AJSON::as_string( jold_command->get( "command" ) );

            if ( ! jcmd ) continue;

            if ( jcmd->get_value() == base ) continue;

            jnew_commands->append( jold_command );
        }
    }

    jft->set( m_default_file_filetype, jnew_commands );

    /**************
     * mime types *
     **************/
    if ( m_default_file_unknown ) {
        auto jmimetype = AJSON::as_object( jobj->get( "mimetype" ) );

        if ( ! jmimetype ) {
            jmimetype = AJSON::as_object( jobj->set( "mimetype", AJSON::make_object() ) );

            if ( ! jmimetype ) return;
        }

        auto jold_commands = AJSON::as_array( jmimetype->get( m_default_file_mime_type ) );

        auto jnew_commands = AJSON::make_array();

        auto jnew_command = AJSON::make_object();
        set_history_json( jnew_command, base, settings );
        jnew_commands->append( jnew_command );

        if ( jold_commands ) {
            for ( size_t pos = 0; pos < jold_commands->size(); pos++ ) {
                auto jold_command = AJSON::as_object( jold_commands->get( pos ) );

                if ( ! jold_command ) continue;

                auto jcmd = AJSON::as_string( jold_command->get( "command" ) );

                if ( ! jcmd ) continue;

                if ( jcmd->get_value() == base ) continue;

                jnew_commands->append( jold_command );
            }
        }

        jmimetype->set( m_default_file_mime_type, jnew_commands );
    }

    auto jall = AJSON::as_array( jobj->get( "all" ) );

    if ( ! jall ) {
        jall = AJSON::as_array( jobj->set( "all", AJSON::make_array() ) );

    }

    if ( jall ) {
        auto jnew_commands = AJSON::make_array();

        auto jnew_command = AJSON::make_object();
        set_history_json( jnew_command, base, settings );
        jnew_commands->append( jnew_command );

        for ( size_t pos = 0; pos < jall->size(); pos++ ) {
            auto jold_command = AJSON::as_object( jall->get( pos ) );

            if ( ! jold_command ) continue;

            auto jcmd = AJSON::as_string( jold_command->get( "command" ) );

            if ( ! jcmd ) continue;

            if ( jcmd->get_value() == base ) continue;

            jnew_commands->append( jold_command );
        }

        jobj->set( "all", jnew_commands );
    }
}
