/* This file is part of the KDE project

   Autocompletion code:
   Copyright (C) 2009 Timothy Pearson <kb9vqf@pearsoncomputing.net>

   Copyright (C) 1999-2002,2003 Dawit Alemayehu <adawit@kde.org>
   Copyright (C) 2000 Malte Starostik <starosti@zedat.fu-berlin.de>
   Copyright (C) 2003 Sven Leiber <s.leiber@web.de>

   Kdesu integration:
   Copyright (C) 2000 Geert Jansen <jansen@kde.org>

   Original authors:
   Copyright (C) 1997 Matthias Ettrich <ettrich@kde.org>
   Copyright (C) 1997 Torben Weis [ Added command completion ]
   Copyright (C) 1999 Preston Brown <pbrown@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include <pwd.h>
#include <string.h>
#include <errno.h>

#include <tqvbox.h>
#include <tqlabel.h>
#include <tqbitmap.h>
#include <tqfile.h>
#include <tqslider.h>
#include <tqlayout.h>
#include <tqgroupbox.h>
#include <tqcheckbox.h>
#include <tqregexp.h>
#include <tqwhatsthis.h>
#include <tqstylesheet.h>

#include <dcopclient.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kprocess.h>
#include <kcombobox.h>
#include <klineedit.h>
#include <kapplication.h>
#include <kdebug.h>
#include <kpassdlg.h>
#include <krun.h>
#include <kwin.h>
#include <kdesu/su.h>
#include <kstandarddirs.h>
#include <kconfig.h>
#include <kiconloader.h>
#include <kpushbutton.h>
#include <kguiitem.h>
#include <kstdguiitem.h>
#include <kmimetype.h>
#include <kurifilter.h>
#include <kcompletionbox.h>

#include "minicli.moc"
#include "minicli_ui.h"
#include "kdesktopsettings.h"

#define KDESU_ERR strerror(errno)

Minicli::Minicli( TQWidget *parent, const char *name)
        :KDialog( parent, name, false, WType_TopLevel ),
         m_autoCheckedRunInTerm(false)
{
  m_pURLCompletion = 0L;

  setPlainCaption( i18n("Run Command") );
  KWin::setIcons( winId(), DesktopIcon("run"), SmallIcon("run") );

  TQVBoxLayout* mainLayout = new TQVBoxLayout( this, 0, KDialog::spacingHint() );
  m_dlg = new MinicliDlgUI (this);
  mainLayout->addWidget(m_dlg);

  m_dlg->lbRunIcon->setPixmap(DesktopIcon("kmenu"));
  m_dlg->lbComment->setAlignment( Qt::WordBreak );

  m_dlg->cbCommand->setDuplicatesEnabled( false );
  m_dlg->cbCommand->setTrapReturnKey( true );

  // Options button...
  m_dlg->pbOptions->setGuiItem (KGuiItem( i18n("&Options >>"), "configure" ));

  // Run button...
  m_dlg->pbRun->setGuiItem (KGuiItem( i18n("&Run"), "run" ));

  // Cancel button...
  m_dlg->pbCancel->setGuiItem ( KStdGuiItem::cancel() );

  if (!kapp->authorize("shell_access"))
    m_dlg->pbOptions->hide();

  m_dlg->pbRun->setEnabled(!m_dlg->cbCommand->currentText().isEmpty());
  m_dlg->pbRun->setDefault( true );

  // Do not show the advanced group box on startup...
  m_dlg->gbAdvanced->hide();

  // URI Filter meta object...
  m_filterData = new KURIFilterData();

  // Create a timer object...
  m_parseTimer = new TQTimer(this);

  m_FocusWidget = 0;

  m_prevCached = false;
  m_iPriority = 50;
  m_iScheduler = StubProcess::SchedNormal;

  m_dlg->leUsername->setText("root");

  // Autocomplete system
  m_filesystemAutocomplete = 0;
  m_histfilesystemAutocomplete = 0;
  m_pURLCompletion = new KURLCompletion();
  //m_pURLCompletion->setCompletionMode( KGlobalSettings::completionMode() );
  connect( m_pURLCompletion, TQT_SIGNAL( match(const TQString&) ), TQT_SLOT( slotMatch(const TQString&) ));

  // Main widget buttons...
  connect( m_dlg->pbRun, TQT_SIGNAL(clicked()), this, TQT_SLOT(accept()) );
  connect( m_dlg->pbCancel, TQT_SIGNAL(clicked()), this, TQT_SLOT(reject()) );
  connect( m_dlg->pbOptions, TQT_SIGNAL(clicked()), TQT_SLOT(slotAdvanced()) );
  connect( m_parseTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotParseTimer()) );

  connect( m_dlg->cbCommand, TQT_SIGNAL( textChanged( const TQString& ) ),
           TQT_SLOT( slotCmdChanged(const TQString&) ) );

  connect( m_dlg->cbCommand, TQT_SIGNAL( returnPressed() ),
           m_dlg->pbRun, TQT_SLOT( animateClick() ) );

  m_dlg->cbCommand->setHistoryEditorEnabled( true );
  connect( m_dlg->cbCommand, TQT_SIGNAL(removed( const TQString&) ), TQT_SLOT(saveConfig()) );

  // Advanced group box...
  connect(m_dlg->cbPriority, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotChangeScheduler(bool)));
  connect(m_dlg->slPriority, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(slotPriority(int)));
  connect(m_dlg->cbRealtime, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotRealtime(bool)));
  connect(m_dlg->cbAutocomplete, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotAutocompleteToggled(bool)));
  connect(m_dlg->cbAutohistory, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotAutohistoryToggled(bool)));
  connect(m_dlg->cbRunAsOther, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotChangeUid(bool)));
  connect(m_dlg->leUsername, TQT_SIGNAL(lostFocus()), TQT_SLOT(updateAuthLabel()));
  connect(m_dlg->cbRunInTerminal, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotTerminal(bool)));

  m_dlg->slPriority->setValue(50);

  loadConfig();
}

Minicli::~Minicli()
{
  delete m_filterData;
  delete m_pURLCompletion;
}

void Minicli::setCommand(const TQString& command)
{
  m_dlg->cbCommand->lineEdit()->setText(command);
  m_dlg->cbCommand->lineEdit()->deselect();
  int firstSpace = command.find(' ');
  if (firstSpace > 0) {
    m_dlg->cbCommand->lineEdit()->setSelection(firstSpace+1, command.length());
  }
}

TQSize Minicli::sizeHint() const
{
  int maxWidth = qApp->desktop()->screenGeometry((TQWidget*)this).width();
  if (maxWidth < 603)
  {
    // a sensible max for smaller screens
    maxWidth = (maxWidth > 240) ? 240 : maxWidth;
  }
  else
  {
    maxWidth = maxWidth * 2 / 5;
  }

  return TQSize(maxWidth, -1);
}

void Minicli::show()
{
  KWin::setState( winId(), NET::StaysOnTop );
  KDialog::show();
}

void Minicli::loadConfig()
{
  TQStringList histList = KDesktopSettings::history();
  int maxHistory = KDesktopSettings::historyLength();
  m_terminalAppList = KDesktopSettings::terminalApps();

  if (m_terminalAppList.isEmpty())
    m_terminalAppList << "ls"; // Default

  bool block = m_dlg->cbCommand->signalsBlocked();
  m_dlg->cbCommand->blockSignals( true );
  m_dlg->cbCommand->setMaxCount( maxHistory );
  m_dlg->cbCommand->setHistoryItems( histList );
  m_dlg->cbCommand->blockSignals( block );

  m_dlg->cbAutocomplete->setChecked( KDesktopSettings::miniCLIFilesystemAutoComplete() );
  m_dlg->cbAutohistory->setChecked( KDesktopSettings::miniCLIHistoryAndFilesystemAutoComplete() );

  m_filesystemAutocomplete = KDesktopSettings::miniCLIFilesystemAutoComplete();
  m_histfilesystemAutocomplete = KDesktopSettings::miniCLIHistoryAndFilesystemAutoComplete();

  if (m_histfilesystemAutocomplete == true) {
      m_dlg->cbAutocomplete->setDisabled( true );
  }
  else {
      m_dlg->cbAutocomplete->setDisabled( false );
  }

  TQStringList compList = KDesktopSettings::completionItems();
  if( compList.isEmpty() )
    m_dlg->cbCommand->completionObject()->setItems( histList );
  else
    m_dlg->cbCommand->completionObject()->setItems( compList );

  int mode = KDesktopSettings::completionMode();
  m_dlg->cbCommand->setCompletionMode( (KGlobalSettings::Completion) mode );

  KCompletionBox* box = m_dlg->cbCommand->completionBox();
  if (box)
    box->setActivateOnSelect( false );

  m_finalFilters = KURIFilter::self()->pluginNames();
  m_finalFilters.remove("kuriikwsfilter");

  m_middleFilters = m_finalFilters;
  m_middleFilters.remove("localdomainurifilter");

  // Provide username completions. Use saner and configurable maximum values.
  int maxEntries = KDesktopSettings::maxUsernameCompletions();
  TQStringList users;

  struct passwd *pw;
  setpwent();
  for (int count=0; ((pw = getpwent()) != 0L) && (count < maxEntries); count++)
    users << TQString::fromLocal8Bit(pw->pw_name);
  endpwent();

  KCompletion *completion = new KCompletion;
  completion->setOrder(KCompletion::Sorted);
  completion->insertItems (users);

  m_dlg->leUsername->setCompletionObject(completion, true);
  m_dlg->leUsername->setCompletionMode(KGlobalSettings::completionMode());
  m_dlg->leUsername->setAutoDeleteCompletionObject( true );

}

void Minicli::saveConfig()
{
  KDesktopSettings::setHistory( m_dlg->cbCommand->historyItems() );
  KDesktopSettings::setTerminalApps( m_terminalAppList );
  //KDesktopSettings::setCompletionItems( m_dlg->cbCommand->completionObject()->items() );
  KDesktopSettings::setCompletionMode( m_dlg->cbCommand->completionMode() );
  KDesktopSettings::setMiniCLIFilesystemAutoComplete( m_filesystemAutocomplete );
  KDesktopSettings::setMiniCLIHistoryAndFilesystemAutoComplete( m_histfilesystemAutocomplete );
  KDesktopSettings::writeConfig();
}

void Minicli::clearHistory()
{
  m_dlg->cbCommand->clearHistory();
  saveConfig();
}

void Minicli::accept()
{
  TQString cmd = m_dlg->cbCommand->currentText().stripWhiteSpace();
  if (!cmd.isEmpty() && (cmd[0].isNumber() || (cmd[0] == '(')) &&
      (TQRegExp("[a-zA-Z\\]\\[]").search(cmd) == -1))
  {
     TQString result = calculate(cmd);
     if (!result.isEmpty())
        m_dlg->cbCommand->setCurrentText(result);
     return;
  }

  bool logout = (cmd == "logout");
  bool lock = (cmd == "lock");

  if( !logout && !lock && runCommand() == 1 )
     return;

  m_dlg->cbCommand->addToHistory( m_dlg->cbCommand->currentText().stripWhiteSpace() );
  reset();
  saveConfig();
  TQDialog::accept();

  if ( logout )
  {
     kapp->propagateSessionManager();
     kapp->requestShutDown();
  }
  if ( lock )
  {
     TQCString appname( "kdesktop" );
     int kicker_screen_number = qt_xscreen();
     if ( kicker_screen_number )
         appname.sprintf("kdesktop-screen-%d", kicker_screen_number);
     kapp->dcopClient()->send(appname, "KScreensaverIface", "lock()", "");
  }
}

void Minicli::reject()
{
  reset();
  TQDialog::reject();
}

void Minicli::reset()
{
  if( m_dlg->gbAdvanced->isShown() )
    slotAdvanced();

  bool block = m_dlg->cbCommand->signalsBlocked();
  m_dlg->cbCommand->blockSignals( true );
  m_dlg->cbCommand->clearEdit();
  m_dlg->cbCommand->setFocus();
  m_dlg->cbCommand->reset();
  m_dlg->cbCommand->blockSignals( block );
  m_dlg->pbRun->setEnabled( false );

  m_iPriority = 50;
  m_iScheduler = StubProcess::SchedNormal;

  m_dlg->cbRunInTerminal->setChecked(false);
  m_dlg->cbRunAsOther->setChecked(false);

  m_dlg->leUsername->setText("root");

  m_dlg->cbPriority->setChecked(false);

  m_dlg->slPriority->setValue(m_iPriority);

  m_dlg->cbRealtime->setChecked( m_iScheduler == StubProcess::SchedRealtime );
  m_dlg->lePassword->erase();

  m_FocusWidget = 0;
  m_iconName = TQString::null;
  m_prevIconName = TQString::null;

  m_prevCached = false;
  updateAuthLabel();
  setIcon();
}

void Minicli::keyPressEvent( TQKeyEvent* e )
{
  if ( e->key() == Qt::Key_Escape )
  {
    e->accept();
    m_dlg->pbCancel->animateClick();
    return;
  }

  TQDialog::keyPressEvent( e );
}

TQString Minicli::terminalCommand (const TQString& cmd, const TQString& args)
{
  TQString terminal = KDesktopSettings::terminalApplication().stripWhiteSpace();
  if (terminal.endsWith("konsole"))
    terminal += " --noclose";

  if( args.isEmpty() )
    terminal += TQString(" -e /bin/sh -c \"%1\"").arg(cmd);
  else
    terminal += TQString(" -e /bin/sh -c \"%1 %2\"").arg(cmd).arg(args);

  if (!m_terminalAppList.contains(cmd))
    m_terminalAppList << cmd;

  return terminal;
}

int Minicli::runCommand()
{
  m_parseTimer->stop();

  // Make sure we have an updated data
  parseLine( true );

  // Ignore empty commands...
  if ( m_dlg->cbCommand->currentText().isEmpty() )
    return 1;

  TQString cmd;
  KURL uri = m_filterData->uri();
  if ( uri.isLocalFile() && !uri.hasRef() && uri.query().isEmpty() )
    cmd = uri.path();
  else
    cmd = uri.url();
    
  TQCString asn;
  if( qApp->desktop()->isVirtualDesktop())
  {
    asn = KStartupInfo::createNewStartupId();
    KStartupInfoId id;
    id.initId( asn );
    KStartupInfoData data;
    data.setXinerama( qApp->desktop()->screenNumber( this ));
    KStartupInfo::sendChange( id, data );
  }

  // Determine whether the application should be run through
  // the command line (terminal) interface...
  bool useTerminal = m_dlg->cbRunInTerminal->isChecked();

  kdDebug (1207) << "Use terminal ? " << useTerminal << endl;

  if (!kapp->authorize("shell_access"))
    useTerminal = false;

  if( needsKDEsu() )
  {
    TQCString user;
    struct passwd *pw;

    if (m_dlg->cbRunAsOther)
    {
      pw = getpwnam(m_dlg->leUsername->text().local8Bit());
      if (pw == 0L)
      {
          KMessageBox::sorry( this, i18n("<qt>The user <b>%1</b> "
          "does not exist on this system.</qt>").arg(m_dlg->leUsername->text()));
        return 1;
      }
    }
    else
    {
      pw = getpwuid(getuid());
      if (pw == 0L)
      {
          KMessageBox::error( this, i18n("You do not exist.\n"));
        return 1;
      }
    }

    user = pw->pw_name;

    {
      // Scoped. we want this object to go away before the fork
      // (maybe not necessary, but can't hurt) according to the cvs log,
      // creating the SuProcess object in the parent and using it in the
      // child creates crashes, but we need to check password before the
      // fork in order to not get in trouble with X for the messagebox

      SuProcess proc_checkpwd;
      proc_checkpwd.setUser( user );

      if (m_dlg->cbPriority->isChecked())
      {
        proc_checkpwd.setPriority(m_iPriority);
        proc_checkpwd.setScheduler(m_iScheduler);
      }

      if (proc_checkpwd.checkInstall(m_dlg->lePassword->password()) != 0)
      {
          KMessageBox::sorry(this, i18n("Incorrect password; please try again."));
        return 1;
      }
    }

    TQApplication::flushX();

    int pid = fork();

    if (pid < 0)
    {
      kdError(1207) << "fork(): " << KDESU_ERR << "\n";
      return -1;
    }

    if (pid > 0)
      return 0;

    // From here on, this is child...

    SuProcess proc;
    proc.setUser(user);

    if (m_dlg->cbPriority->isChecked())
    {
      proc.setPriority(m_iPriority);
      proc.setScheduler(m_iScheduler);
    }

    TQCString command;

    if (useTerminal)
      command = terminalCommand( cmd, m_filterData->argsAndOptions() ).local8Bit();
    else
    {
      command = cmd.local8Bit();
      if( m_filterData->hasArgsAndOptions() )
        command += m_filterData->argsAndOptions().local8Bit();
    }

    proc.setCommand(command);

    // Block SIGCHLD because SuProcess::exec() uses waitpid()
    sigset_t sset;
    sigemptyset(&sset);
    sigaddset(&sset, SIGCHLD);
    sigprocmask(SIG_BLOCK, &sset, 0L);
    proc.setTerminal(true);
    proc.setErase(true);
    _exit(proc.exec(m_dlg->lePassword->password()));
    return 0;
  }
  else
  {
    TQString exec;

    // yes, this is a hack, but there is no way of doing it
    // through SuProcess without providing the user password
    if (m_iPriority < 50)
    {
      // from kdesu_stub.c
      int val = 20 - (int) (((double) m_iPriority) * 40 / 100 + 0.5);
      cmd = "nice -n " + TQString::number( val ) + " " + cmd;
    }

    if (useTerminal)
    {
      cmd = terminalCommand( cmd, m_filterData->argsAndOptions() );
      kdDebug(1207) << "Terminal command: " << cmd << endl;
    }
    else
    {
      switch( m_filterData->uriType() )
      {
        case KURIFilterData::LOCAL_FILE:
        case KURIFilterData::LOCAL_DIR:
        case KURIFilterData::NET_PROTOCOL:
        case KURIFilterData::HELP:
        {
          // No need for kfmclient, KRun does it all (David)
          (void) new KRun( m_filterData->uri(), parentWidget(), asn );
          return 0;
        }
        case KURIFilterData::EXECUTABLE:
        {
          if( !m_filterData->hasArgsAndOptions() )
          {
            // Look for desktop file
            KService::Ptr service = KService::serviceByDesktopName(cmd);
            if (service && service->isValid() && service->type() == "Application")
            {
              notifyServiceStarted(service);
              KRun::run(*service, KURL::List(), parentWidget(), asn );
              return 0;
            }
          }
        }
        // fall-through to shell case
        case KURIFilterData::SHELL:
        {
          if (kapp->authorize("shell_access"))
          {
            exec = cmd;

            if( m_filterData->hasArgsAndOptions() )
              cmd += m_filterData->argsAndOptions();

            break;
          }
          else
          {
            KMessageBox::sorry( this, i18n("<center><b>%1</b></center>\n"
                                      "You do not have permission to execute "
                                      "this command.")
                                      .arg( TQStyleSheet::convertFromPlainText(cmd) ));
            return 1;
          }
        }
        case KURIFilterData::UNKNOWN:
        case KURIFilterData::ERROR:
        default:
        {
          // Look for desktop file
          KService::Ptr service = KService::serviceByDesktopName(cmd);
          if (service && service->isValid() && service->type() == "Application")
          {
            notifyServiceStarted(service);
            KRun::run(*service, KURL::List(), parentWidget(), asn );
            return 0;
          }

          service = KService::serviceByName(cmd);
          if (service && service->isValid() && service->type() == "Application")
          {
            notifyServiceStarted(service);
            KRun::run(*service, KURL::List(), parentWidget(), asn );
            return 0;
          }

          KMessageBox::sorry( this, i18n("<center><b>%1</b></center>\n"
                                    "Could not run the specified command.")
                                    .arg( TQStyleSheet::convertFromPlainText(cmd) ));
          return 1;
        }
      }
    }

    if ( KRun::runCommand( cmd, exec, m_iconName, parentWidget(), asn ) )
      return 0;
    else
    {
      KMessageBox::sorry( this, i18n("<center><b>%1</b></center>\n"
                                "The specified command does not exist.").arg(cmd) );
      return 1; // Let the user try again...
    }
  }
}

void Minicli::notifyServiceStarted(KService::Ptr service)
{
    // Inform other applications (like the quickstarter applet)
    // that an application was started
    TQByteArray params;
    TQDataStream stream(params, IO_WriteOnly);
    stream << "minicli" << service->storageId();
    kdDebug() << "minicli appLauncher dcop signal: " << service->storageId() << endl;
    KApplication::kApplication()->dcopClient()->emitDCOPSignal("appLauncher",
        "serviceStartedByStorageId(TQString,TQString)", params);
}

void Minicli::slotCmdChanged(const TQString& text)
{
  bool isEmpty = text.isEmpty();
  m_dlg->pbRun->setEnabled( !isEmpty );

  if( isEmpty )
  {
    // Reset values to default
    m_filterData->setData(KURL());

    // Empty String is certainly no terminal application
    slotTerminal(false);

    // Reset the icon if needed...
    const TQPixmap pixmap = DesktopIcon("kmenu");

    if ( pixmap.serialNumber() != m_dlg->lbRunIcon->pixmap()->serialNumber())
      m_dlg->lbRunIcon->setPixmap(pixmap);

    return;
  }
  else if ((m_filesystemAutocomplete == true) && ( m_pURLCompletion )) {
    // Attempt to autocomplete the entered URL if it starts with the / character, meaning I am looking for something on the filesystem
    // Also use autocompletion if it appears that I am using some kind of ioslave, except the http:// ioslave
    m_urlCompletionStarted = true; // flag for slotMatch()

    if ((text.startsWith( "/" ) || text.startsWith( "~" ) || (text.contains("://", false) != 0)) && (text.contains("http://", false) == 0)) {
        TQString completion = m_pURLCompletion->makeCompletion( text );
    }
  }

  m_parseTimer->start(250, true);
}

// Handle match() from m_pURLCompletion
void Minicli::slotMatch( const TQString &match )
{
  TQString current_text;
  TQStringList histList = KDesktopSettings::history();
  int maxHistory = KDesktopSettings::historyLength();
  int maxAutocompletion = KDesktopSettings::miniCLIAutocompletionLength();

  if ( match.isEmpty() ) // this case is handled directly
    return;

  // Check flag to avoid match() raised by rotation
  if ( m_urlCompletionStarted ) {
    m_urlCompletionStarted = false;

    if (m_filesystemAutocomplete == true) {
        bool block = m_dlg->cbCommand->signalsBlocked();
        m_dlg->cbCommand->blockSignals( true );
        TQStringList items = m_pURLCompletion->allMatches();
        items.sort();
        if (m_histfilesystemAutocomplete == true) {
            // Add the history to the list
            histList += items;
            maxHistory += maxAutocompletion;
        }
        else {
            histList = items;
            maxHistory = maxAutocompletion;
        }
        current_text = m_dlg->cbCommand->currentText();
        //histList.prepend ( current_text );	// Add the current text to the autocompletion list
        m_dlg->cbCommand->setMaxCount( maxHistory );
        m_dlg->cbCommand->completionObject()->setItems( histList );
        m_dlg->cbCommand->setCurrentText( current_text );
        m_dlg->cbCommand->blockSignals( block );
    }
  }
}

void Minicli::slotAdvanced()
{
  if (m_dlg->gbAdvanced->isHidden())
  {
    m_dlg->gbAdvanced->show();
    m_dlg->pbOptions->setText(i18n("&Options <<"));

    // Set the focus back to the widget that had it to begin with, i.e.
    // do not put the focus on the "Options" button.
    m_FocusWidget = focusWidget();

    if( m_FocusWidget )
      m_FocusWidget->setFocus();
  }
  else
  {
    m_dlg->gbAdvanced->hide();
    m_dlg->pbOptions->setText(i18n("&Options >>"));

    if( m_FocusWidget && m_FocusWidget->parent() != m_dlg->gbAdvanced )
      m_FocusWidget->setFocus();
  }
  adjustSize();
}

void Minicli::slotParseTimer()
{
  //kdDebug (1207) << "Minicli::slotParseTimer: Timed out..." << endl;
  parseLine( false );
}

void Minicli::parseLine( bool final )
{
  TQString cmd = m_dlg->cbCommand->currentText().stripWhiteSpace();
  m_filterData->setData( cmd );

  if( final )
    KURIFilter::self()->filterURI( *(m_filterData), m_finalFilters );
  else
    KURIFilter::self()->filterURI( *(m_filterData), m_middleFilters );

  bool isTerminalApp = ((m_filterData->uriType() == KURIFilterData::EXECUTABLE) &&
                        m_terminalAppList.contains(m_filterData->uri().url()));

  if( !isTerminalApp )
  {
    m_iconName = m_filterData->iconName();
    setIcon();
  }

  if ( isTerminalApp && final && !m_dlg->cbRunInTerminal->isChecked() )
  {
    m_terminalAppList.remove( m_filterData->uri().url() );
    isTerminalApp = false;
  }
  else
  {
    bool wasAutoChecked = m_autoCheckedRunInTerm;
    bool willBeAutoChecked = isTerminalApp && !m_dlg->cbRunInTerminal->isChecked();
    slotTerminal(isTerminalApp || (m_dlg->cbRunInTerminal->isChecked() && !m_autoCheckedRunInTerm));
    if (!wasAutoChecked && willBeAutoChecked)
        m_autoCheckedRunInTerm = true;
  }

  kdDebug (1207) << "Command: " << m_filterData->uri().url() << endl;
  kdDebug (1207) << "Arguments: " << m_filterData->argsAndOptions() << endl;
}

void Minicli::setIcon ()
{
  if( m_iconName.isEmpty() || m_iconName == "unknown" || m_iconName == "kde" )
    m_iconName = TQString::fromLatin1("kmenu");

  TQPixmap icon = DesktopIcon( m_iconName );

  if ( m_iconName == "www" )
  {
    // Not using KIconEffect::overlay as that requires the same size
    // for the icon and the overlay, also the overlay definately doesn't
    // have a more that one-bit alpha channel here
    TQPixmap overlay( locate ( "icon", KMimeType::favIconForURL( m_filterData->uri() ) + ".png" ) );
    if ( !overlay.isNull() )
    {
      int x = icon.width() - overlay.width();
      int y = icon.height() - overlay.height();
      if ( icon.mask() )
      {
        TQBitmap mask = *icon.mask();
        bitBlt( &mask, x, y,
                overlay.mask() ? const_cast<TQBitmap *>(overlay.mask()) : &overlay,
                0, 0, overlay.width(), overlay.height(),
                overlay.mask() ? OrROP : SetROP );
        icon.setMask(mask);
      }
      bitBlt( &icon, x, y, &overlay );
    }
  }

  m_dlg->lbRunIcon->setPixmap( icon );
}

void Minicli::updateAuthLabel()
{
  if ((m_dlg->cbPriority->isChecked() && (m_iPriority > 50)) ||
      (m_iScheduler != StubProcess::SchedNormal))
  {
    if (!m_prevCached && !m_dlg->leUsername->text().isEmpty())
    {
      //kdDebug(1207) << k_funcinfo << "Caching: user=" << m_dlg->leUsername->text() <<
      //  ", checked=" << m_dlg->cbRunAsOther->isChecked() << endl;

      m_prevUser = m_dlg->leUsername->text();
      m_prevPass = m_dlg->lePassword->text();
      m_prevChecked = m_dlg->cbRunAsOther->isChecked();
      m_prevCached = true;
    }
    if (m_dlg->leUsername->text() != TQString::fromLatin1("root"))
      m_dlg->lePassword->setText(TQString::null);
    m_dlg->leUsername->setText(TQString::fromLatin1("root"));
    m_dlg->cbRunAsOther->setChecked(true);
    m_dlg->cbRunAsOther->setEnabled(false);
    m_dlg->leUsername->setEnabled(false);
    m_dlg->lbUsername->setEnabled(true);
    m_dlg->lePassword->setEnabled(true);
    m_dlg->lbPassword->setEnabled(true);
  }
  else if (m_dlg->cbRunAsOther->isEnabled() &&
    m_dlg->cbRunAsOther->isChecked() && !m_dlg->leUsername->text().isEmpty())
  {
    m_dlg->lePassword->setEnabled(true);
    m_dlg->lbPassword->setEnabled(true);
  }
  else
  {
    if (m_prevCached)
    {
      m_dlg->leUsername->setText(m_prevUser);
      m_dlg->lePassword->setText(m_prevPass);
      m_dlg->cbRunAsOther->setChecked(m_prevChecked);
      m_dlg->leUsername->setEnabled(m_prevChecked);
      m_dlg->lbUsername->setEnabled(m_prevChecked);
    }
    else
    {
      m_dlg->cbRunAsOther->setChecked(false);
      m_dlg->leUsername->setEnabled(false);
      m_dlg->lbUsername->setEnabled(false);
    }
    m_dlg->cbRunAsOther->setEnabled(true);
    m_dlg->lePassword->setEnabled(false);
    m_dlg->lbPassword->setEnabled(false);
    m_prevCached = false;
  }
}

void Minicli::slotTerminal(bool enable)
{
  m_dlg->cbRunInTerminal->setChecked(enable);
  m_autoCheckedRunInTerm = false;

  if (enable)
  {
    m_prevIconName = m_iconName;
    m_iconName = TQString::fromLatin1( "konsole" );
    setIcon();
  }
  else if (!m_prevIconName.isEmpty())
  {
      m_iconName = m_prevIconName;
      setIcon();
  }
}

void Minicli::slotChangeUid(bool enable)
{
  m_dlg->leUsername->setEnabled(enable);
  m_dlg->lbUsername->setEnabled(enable);

  if(enable)
  {
    m_dlg->leUsername->selectAll();
    m_dlg->leUsername->setFocus();
  }

  updateAuthLabel();
}

void Minicli::slotChangeScheduler(bool enable)
{
  m_dlg->slPriority->setEnabled(enable);
  m_dlg->lbLowPriority->setEnabled(enable);
  m_dlg->lbHighPriority->setEnabled(enable);

  updateAuthLabel();
}

bool Minicli::needsKDEsu()
{
  return ((m_dlg->cbPriority->isChecked() && ((m_iPriority > 50) ||
          (m_iScheduler != StubProcess::SchedNormal))) ||
          (m_dlg->cbRunAsOther->isChecked() && !m_dlg->leUsername->text().isEmpty()));
}

void Minicli::slotRealtime(bool enabled)
{
  m_iScheduler = enabled ? StubProcess::SchedRealtime : StubProcess::SchedNormal;

  if (enabled)
  {
    if (KMessageBox::warningContinueCancel(this,
                i18n("Running a realtime application can be very dangerous. "
                    "If the application misbehaves, the system might hang "
                    "unrecoverably.\nAre you sure you want to continue?"),
                i18n("Warning - Run Command"), KGuiItem(i18n("&Run Realtime")),TQString::null,KMessageBox::Notify|KMessageBox::PlainCaption)
        != KMessageBox::Continue )
    {
      m_iScheduler = StubProcess::SchedNormal;
      m_dlg->cbRealtime->setChecked(false);
    }
  }

  updateAuthLabel();
}

void Minicli::slotAutocompleteToggled(bool enabled)
{
  if (enabled)
  {
    // Enable filesystem autocompletion
    m_filesystemAutocomplete = true;
  }
  else {
    // Enable history only autocompletion
    m_filesystemAutocomplete = false;
  }

  TQString current_text = m_dlg->cbCommand->currentText();
  m_dlg->cbCommand->setCurrentText( current_text );    // Force an update of the autocompletion list
}

void Minicli::slotAutohistoryToggled(bool enabled)
{
  if (enabled)
  {
    // Enable history and filesystem autocompletion
    m_histfilesystemAutocomplete = true;
    m_filesystemAutocomplete = true;
    m_dlg->cbAutocomplete->setChecked( true );
    m_dlg->cbAutocomplete->setDisabled ( true );
  }
  else {
    // Disable history and filesystem autocompletion
    m_histfilesystemAutocomplete = false;
    m_dlg->cbAutocomplete->setDisabled ( false );
  }

  TQString current_text = m_dlg->cbCommand->currentText();
  m_dlg->cbCommand->setCurrentText( current_text );    // Force an update of the autocompletion list
}

void Minicli::slotPriority(int priority)
{
  // Provide a way to easily return to the default priority
  if ((priority > 40) && (priority < 60))
  {
    priority = 50;
    m_dlg->slPriority->setValue(50);
  }

  m_iPriority = priority;

  updateAuthLabel();
}

TQString Minicli::calculate(const TQString &exp)
{
   TQString result, cmd;
   const TQString bc = KStandardDirs::findExe("bc");
   if ( !bc.isEmpty() )
      cmd = TQString("echo %1 | %2").arg(KProcess::quote(TQString("scale=8; ")+exp), KProcess::quote(bc));
   else
      cmd = TQString("echo $((%1))").arg(exp);
   FILE *fs = popen(TQFile::encodeName(cmd).data(), "r");
   if (fs)
   {
       { // scope for QTextStream
           TQTextStream ts(fs, IO_ReadOnly);
           result = ts.read().stripWhiteSpace();
       }
       pclose(fs);
   }
   return result;
}

void Minicli::fontChange( const TQFont & )
{
   adjustSize();
}

// vim: set et ts=2 sts=2 sw=2: