/* $Id: kmainwidget.cpp,v 1.63 2000/04/21 14:06:56 koss Exp $
   $Log: kmainwidget.cpp,v $
   Revision 1.63  2000/04/21 14:06:56  koss
   Little GUI reorganization for new individual transfer dialogs.

   Revision 1.62  2000/04/20 19:11:13  koss
   Some renaming for consistence :
      ConfigDlg to DlgTransferConfig.
      PreferencesDlg to DlgPreferences.
   Added dlgProgress as a new individual progress dialog.
   Fixed application quitting.
   Fixed action update after job cancelation.

   Revision 1.61  2000/04/16 17:01:13  koss
   Added handling of cancel event.
   Converted path() to url() for correct handling.
   Fixed individual progress dialogs.

   Revision 1.60  2000/04/16 09:57:51  koss
   Reopen lineedit dialog when the URL is malformed and put the wrong one
   in the lineedit.

   Revision 1.59  2000/04/11 20:42:19  koss
   Ported to XML framework.

   Revision 1.58  2000/04/10 20:42:35  koss
   Reworked version information.
   Now passing aboutdata to khelpmenu.
   Fixed whatsthis, but it works only for menu items and not for
   toolbar. Is it a bug ?
   Some fixes for actions, still not fully working.

   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., 675 Mass Ave, Cambridge, MA 02139, USA.

   */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

#ifdef __svr4__
#define map BULLSHIT    // on Solaris it conflicts with STL ?
#include <net/if.h>
#undef map
#include <sys/sockio.h>  // needed for SIOCGIFFLAGS
#else
#include <net/if.h>
#endif

#include <qdir.h>
#include <qpainter.h>
#include <qclipboard.h>
#include <qregexp.h>
#include <qdragobject.h>
#include <qwhatsthis.h>
#include <qtooltip.h>
#include <qtimer.h>
#include <qdropsite.h>

#include <kfiledialog.h>
#include <kapp.h>
#include <kstddirs.h>
#include <kiconloader.h>
#include <kaudioplayer.h>
#include <kdebug.h>
#include <kurl.h>
#include <klineeditdlg.h>
#include <klocale.h>
#include <kglobal.h>
#include <kwin.h>
#include <kmessagebox.h>
#include <kaction.h>
#include <kstdaction.h>
#include <khelpmenu.h>
#include <kedittoolbar.h>
#include <kkeydialog.h>

#include "settings.h"
#include "transfer.h"
#include "transferlist.h"
#include "kmainwidget.h"
#include "kfileio.h"
#include "preferences.h"
#include "logwindow.h"
#include "docking.h"
#include "droptarget.h"

#include "version.h"


KMainWidget* kmain = 0L;

QGuardedPtr<DropTarget> kdrop = 0L;
QGuardedPtr<DockWidget> kdock = 0L;

Settings ksettings; // this object contains all settings

static int sockets_open();

// socket constants
int ipx_sock = -1;	/* IPX socket			*/
int ax25_sock = -1;	/* AX.25 socket			*/
int inet_sock = -1;	/* INET socket			*/
int ddp_sock = -1;	/* Appletalk DDP socket		*/


KMainWidget::KMainWidget() : KTMainWindow( "" ) {
  b_online = TRUE;
  b_viewLogWindow = FALSE;
  b_viewPreferences = FALSE;

  kmain = this;

  // Set log time, needed for the name of log file
  QDate date = QDateTime::currentDateTime().date();
  QTime time = QDateTime::currentDateTime().time();
  QString tmp;
  tmp.sprintf( "log%d:%d:%d-%d:%d:%d", date.day(), date.month(), date.year(),
	       time.hour(), time.minute(), time.second() );

  logFileName = locateLocal("appdata", "logs/");
  logFileName += tmp;

  // Clear clipboard
  kapp->clipboard()->setText("");

  // Load all settings from KConfig
  ksettings.load();

  // Setup log window
  logWindow = new LogWindow();

  setCaption( CAITOOVERSION );

  setupGUI();
  setupWhatsThis();

  log( i18n("Welcome to Caitoo") );

  setView( myTransferList );

  connect( kapp, SIGNAL( saveYourself() ), SLOT( slotSaveYourself() ) );

  // Enable dropping
  setAcceptDrops( true );

  // Setup connection timer
  connectionTimer = new QTimer( this );
  connect( connectionTimer, SIGNAL( timeout() ), SLOT( slotCheckConnection() ) );

  // setup socket for checking connection
  if ((_sock = sockets_open()) < 0) {
    log( i18n("Couldn't create valid socket"), false );
  } else {
    connectionTimer->start( 5000 );  // 5 second interval for checking connection
  }

  checkOnline(); 
  if ( ! b_online ) {
    log( i18n("Starting offline") );
  }

  // Setup animation timer
  animTimer = new QTimer( this );
  animCounter = 0;
  connect( animTimer, SIGNAL( timeout() ), SLOT( slotAnimTimeout() ) );

  if ( ksettings.b_useAnimation ) {
    animTimer->start( 400 );
  } else {
    animTimer->start( 1000 );
  }

  // Setup transfer timer for scheduled downloads and checkQueue()
  transferTimer = new QTimer( this );
  connect( transferTimer, SIGNAL( timeout() ), SLOT( slotTransferTimeout() ) );
  transferTimer->start( 10000 ); // 10 secs time interval 

  // Setup autosave timer
  autosaveTimer = new QTimer( this );
  connect( autosaveTimer, SIGNAL( timeout() ), SLOT( slotAutosaveTimeout() ) );
  setAutoSave();

  // Setup clipboard timer
  clipboardTimer = new QTimer( this );
  connect( clipboardTimer, SIGNAL( timeout() ), SLOT( slotCheckClipboard() ) );
  if ( ksettings.b_autoPaste ) {
    clipboardTimer->start( 1000 );
  }

  currentDirectory = "file:" + QDir::currentDirPath();
  readTransfers();

  // Setup special windows
  kdrop = new DropTarget();
  kdock = new DockWidget( this );
  // Set geometry
//   if ( ksettings.mainProperties != "" ) {
//     setGeometry(KWM::setProperties(winId(), ksettings.mainProperties));
//   } else {
//     resize( 650, 180 );
//   }
  resize( 650, 180 ); // TODO : remove

  // update actions
  m_paUseAnimation->setChecked( ksettings.b_useAnimation );
  m_paUseSound->setChecked( ksettings.b_useSound );
  m_paExpertMode->setChecked( ksettings.b_expertMode );
  m_paUseLastDir->setChecked( ksettings.b_useLastDir );
  if ( ksettings.connectionType != PERMANENT ) {
    m_paAutoDisconnect->setChecked(ksettings.b_autoDisconnect);
  }
  setAutoDisconnect();

  m_paAutoShutdown->setChecked( ksettings.b_autoShutdown );
  m_paOfflineMode->setChecked( ksettings.b_offlineMode );
  m_paAutoPaste->setChecked( ksettings.b_autoPaste );
  m_paShowStatusbar->setChecked( ksettings.b_showStatusbar );
  m_paShowLog->setChecked( b_viewLogWindow );

  switch ( ksettings.windowStyle ) {

  case DROP_TARGET:
    m_paDropTarget->setChecked(true);
    break;

  case DOCKED:
    m_paDockWindow->setChecked(true);
    break;

  default:
    setWindowStyle();
    break;
  }

}


KMainWidget::~KMainWidget () {
  if ( animTimer ) {
    animTimer->stop();
    delete animTimer;
  }
  
  delete (DropTarget *) kdrop;

  writeTransfers();

  writeLog();
}


void KMainWidget::log( const char *message, bool statusbar ) {
  kDebugInfo( 5001, message );
  logWindow->logGeneral( message );

  if ( statusbar ) {
    statusBar()->message( message, 1000);
  }
}


void KMainWidget::slotSaveYourself() {
  writeTransfers();
  ksettings.save();
}


void KMainWidget::setupGUI() {

  // setup transfer list
  myTransferList = new TransferList(this, "transferList");

  setListFont();

  connect( myTransferList, SIGNAL( selectionChanged() ),
	   this, SLOT( slotUpdateActions() ) );
  connect( myTransferList, SIGNAL( transferSelected( Transfer* ) ), 
	   this, SLOT( slotOpenIndividual() ) );
  connect( myTransferList, SIGNAL( popupMenu( Transfer* ) ), 
	   this, SLOT( slotPopupMenu( Transfer* ) ) );

  // file actions
  m_paOpenTransfer = KStdAction::open( this, SLOT( slotOpenTransfer() ), actionCollection(), "open_transfer" );
  m_paPasteTransfer = KStdAction::paste( this, SLOT( slotPasteTransfer() ), actionCollection(), "paste_transfer" );

  m_paExportTransfers = new KAction( i18n("&Export Transfer List"), 0, this,
				     SLOT( slotExportTransfers()), actionCollection(), "export_transfers" );
  m_paImportTransfers = new KAction( i18n("&Import Transfer List"), 0, this,
				     SLOT( slotImportTransfers()), actionCollection(), "import_transfers" );

  m_paImportText = new KAction( i18n("Import Text &File"), 0, this,
				SLOT( slotImportTextFile()), actionCollection(), "import_text" );

  m_paQuit = KStdAction::quit( this, SLOT( slotQuit() ), actionCollection(), "quit" );


  // transfer actions
  m_paCopy = new KAction( i18n("&Copy URL to clipboard"), 0, this,
			  SLOT( slotCopyToClipboard()), actionCollection(), "copy_url" );
  m_paIndividual = new KAction( i18n("&Open individual window"), 0, this,
				SLOT( slotOpenIndividual()), actionCollection(), "open_individual" );

  m_paMoveToBegin = new KAction( i18n("Move to &beginning"), 0, this,
				 SLOT( slotMoveToBegin()), actionCollection(), "move_begin" );

  m_paMoveToEnd = new KAction( i18n("Move to &end"), 0, this,
			       SLOT( slotMoveToEnd()), actionCollection(), "move_end" );

  m_paResume = new KAction( i18n("&Resume"), QIconSet(BarIcon("tool_resume")), 0, this,
			    SLOT( slotResumeCurrent()), actionCollection(), "resume" );
  m_paPause = new KAction( i18n("&Pause"), QIconSet(BarIcon("tool_pause")), 0, this,
			   SLOT( slotPauseCurrent()), actionCollection(), "pause" );
  m_paDelete = new KAction( i18n("&Delete"), QIconSet(BarIcon("tool_delete")), 0, this,
			    SLOT( slotDeleteCurrent()), actionCollection(), "delete" );
  m_paRestart = new KAction( i18n("Re&start"), QIconSet(BarIcon("tool_restart")), 0, this,
			     SLOT( slotRestartCurrent()), actionCollection(), "restart" );

  m_paQueue = new KRadioAction( i18n("&Queue"), QIconSet(BarIcon("tool_queue")), 0, this,
				SLOT( slotQueueCurrent()), actionCollection(), "queue" );
  m_paTimer = new KRadioAction( i18n("&Timer"), QIconSet(BarIcon("tool_timer")), 0, this,
				SLOT( slotTimerCurrent()), actionCollection(), "timer" );
  m_paDelay = new KRadioAction( i18n("De&lay"), QIconSet(BarIcon("tool_delay")), 0, this,
				SLOT( slotDelayCurrent()), actionCollection(), "delay" );

  m_paQueue->setExclusiveGroup( "TransferMode" );
  m_paTimer->setExclusiveGroup( "TransferMode" );
  m_paDelay->setExclusiveGroup( "TransferMode" );
  
  // options actions
  m_paUseAnimation = new KToggleAction( i18n("Use &Animation"), 0, this,
					SLOT( slotToggleAnimation()), actionCollection(), "toggle_animation" );

  m_paUseSound = new KToggleAction( i18n("Use &Sound"), 0, this,
				    SLOT( slotToggleSound()), actionCollection(), "toggle_sound" );

  m_paPreferences = new KToggleAction( i18n("P&references"), QIconSet(BarIcon("tool_preferences")), 0, this,
				       SLOT( slotTogglePreferences()), actionCollection(), "preferences" );

  m_paExpertMode = new KToggleAction( i18n("&Expert mode"), QIconSet(BarIcon("tool_expert")), 0, this,
				      SLOT( slotToggleExpertMode()), actionCollection(), "expert_mode" );

  m_paUseLastDir = new KToggleAction( i18n("&Use-last-directory mode"), QIconSet(BarIcon("tool_uselastdir")), 0, this,
				      SLOT( slotToggleUseLastDir()), actionCollection(), "use_last_dir" );

  m_paAutoDisconnect = new KToggleAction( i18n("Auto-&disconnect mode"), QIconSet(BarIcon("tool_disconnect")), 0, this,
					  SLOT( slotToggleAutoDisconnect()), actionCollection(), "auto_disconnect" );

  m_paAutoShutdown = new KToggleAction( i18n("Auto-s&hutdown mode"), QIconSet(BarIcon("tool_shutdown")), 0, this,
				    SLOT( slotToggleAutoShutdown()), actionCollection(), "auto_shutdown" );

  m_paOfflineMode = new KToggleAction( i18n("&Offline mode"), QIconSet(BarIcon("tool_offline_mode")), 0, this,
				       SLOT( slotToggleOfflineMode()), actionCollection(), "offline_mode" );

  m_paAutoPaste = new KToggleAction( i18n("Auto-pas&te mode"), QIconSet(BarIcon("tool_clipboard")), 0, this,
				     SLOT( slotToggleAutoPaste()), actionCollection(), "auto_paste" );

  KStdAction::keyBindings( this, SLOT( slotConfigureKeys() ), actionCollection(), "configure_keybinding" );

  KStdAction::configureToolbars( this, SLOT( slotConfigureToolbars() ), actionCollection(), "configure_toolbars" );

  // view actions
  m_paShowStatusbar = KStdAction::showStatusbar( this, SLOT( slotToggleStatusbar() ), actionCollection(), "show_statusbar" );

  m_paShowLog = new KToggleAction( i18n("Show &Log Window"), QIconSet(BarIcon("tool_logwindow")), 0, this,
				   SLOT( slotToggleLogWindow()), actionCollection(), "toggle_log" );

  m_paDropTarget = new KRadioAction( i18n("Drop &target"), QIconSet(BarIcon("tool_drop_target")), 0, this,
				     SLOT( slotDropTarget()), actionCollection(), "drop_target" );

  m_paDockWindow = new KRadioAction( i18n("&Dock window"), QIconSet(BarIcon("tool_dock")), 0, this,
				     SLOT( slotDock()), actionCollection(), "dock_window" );
  
  m_paDropTarget->setExclusiveGroup( "WindowMode" );
  m_paDockWindow->setExclusiveGroup( "WindowMode" );

  menuHelp = new KHelpMenu( this, KGlobal::instance()->aboutData() );
  KStdAction::whatsThis( menuHelp, SLOT( contextHelpActivated() ), actionCollection(), "whats_this" );

  createGUI("caitooui.rc");

  toolBar()->setBarPos( ksettings.toolbarPosition );

  // setup statusbar
  statusBar()->insertFixedItem( i18n(" Transfers : %1 ").arg( 99 ), ID_TOTAL_TRANSFERS);
  statusBar()->insertFixedItem( i18n(" Files : %1 ").arg( 555 ), ID_TOTAL_FILES);
  statusBar()->insertFixedItem( i18n(" Size : %1 kB ").arg( "134.56" ), ID_TOTAL_SIZE);
  statusBar()->insertFixedItem( i18n(" Time : 00:00:00 "), ID_TOTAL_TIME);
  statusBar()->insertFixedItem( i18n(" %1 kB/s ").arg("123.34"), ID_TOTAL_SPEED);

  if ( !ksettings.b_showStatusbar ) {
    enableStatusBar( KStatusBar::Hide );
  } else {
    enableStatusBar( KStatusBar::Show );
  }

  updateStatusBar();
}


void KMainWidget::setupWhatsThis() {
  QString tmp;

  tmp = i18n( "<b>Resume</b> button starts selected transfers\n"
	      "and sets their mode to <i>queued</i>." );
  m_paResume->setWhatsThis( tmp );

  tmp = i18n( "<b>Pause</b> button stops selected transfers\n"
	      "and sets their mode to <i>delayed</i>." );
  m_paPause->setWhatsThis( tmp );

  tmp = i18n( "<b>Delete</b> button removes selected transfers\n"
	      "from the list." );
  m_paDelete->setWhatsThis( tmp );

  tmp = i18n( "<b>Restart</b> button is a convenience button\n"
	      "that simply does Pause and Resume." );
  m_paRestart->setWhatsThis( tmp );

  tmp = i18n( "<b>Queued</b> button sets the mode of selected\n"
	      "transfers to <i>queued</i>.\n"
	      "\n"
	      "It is a radio button, you can select between\n"
	      "three modes." );
  m_paQueue->setWhatsThis( tmp );

  tmp = i18n( "<b>Scheduled</b> button sets the mode of selected\n"
	      "transfers to <i>scheduled</i>.\n"
	      "\n"
	      "It is a radio button, you can select between\n"
	      "three modes." );
  m_paTimer->setWhatsThis( tmp );

  tmp = i18n( "<b>Delayed</b> button sets the mode of selected\n"
	      "transfers to <i>delayed</i>."
	      "This also causes the selected transfers to stop.\n"
	      "\n"
	      "It is a radio button, you can select between\n"
	      "three modes." );
  m_paDelay->setWhatsThis( tmp );

  tmp = i18n( "<b>Preferences</b> button opens a preferences dialog\n"
	      "where you can set various options.\n"
	      "\n"
	      "Some of these options can be more easily set with toolbar.");
  m_paPreferences->setWhatsThis( tmp );

  tmp = i18n( "<b>Log window</b> button opens a log window.\n"
	      "Log window records all program events that occur\n"
	      "during the run of Caitoo." );
  m_paShowLog->setWhatsThis( tmp );

  tmp = i18n( "<b>Paste transfer</b> button adds an URL from\n"
	      "the clipboard as a new transfer.\n"
	      "\n"
	      "This way you can easily copy&paste URL's between\n"
	      "applications." );
  m_paPasteTransfer->setWhatsThis( tmp );

  tmp = i18n( "<b>Expert mode</b> button toggles the expert mode\n"
	      "on and off.\n"
	      "\n"
	      "Expert mode is recommended for experienced users.\n"
	      "When set, you will not be \"bothered\" by confirmation\n"
	      "messages.\n"
	      "<b>Important !</b>\n"
	      "Turn it on if you are using auto-disconnect or\n"
	      "auto-shutdown features and you want Caitoo to disconnect\n"
	      "without asking." );
  m_paExpertMode->setWhatsThis( tmp );

  tmp = i18n( "<b>Use last directory</b> button toggles the\n"
	      "use-last-directory feature on and off.\n"
	      "\n"
	      "When set, Caitoo will ignore the directory settings\n"
	      "and put all new added transfers into the directory\n"
	      "where ther last transfer was put.");
  m_paUseLastDir->setWhatsThis( tmp );

  tmp = i18n( "<b>Auto disconnect</b> button toggles the auto-disconnect\n"
	      "mode on and off.\n"
	      "\n"
	      "When set, Caitoo will disconnect automatically\n"
	      "after all queued transfers are finished.\n"
	      "\n"
	      "<b>Important !</b>\n"
	      "Turn on also the expert mode when you want Caitoo\n"
	      "to disconnect without asking." );
  m_paAutoDisconnect->setWhatsThis( tmp );

  tmp = i18n( "<b>Auto shutdown</b> button toggles the auto-shutdown\n"
	      "mode on and off.\n"
	      "\n"
	      "When set, Caitoo will quit automatically\n"
	      "after all queued transfers are finished.\n"
	      "<b>Important !</b>\n"
	      "Turn on also the expert mode when you want Caitoo\n"
	      "to quit without asking." );
  m_paAutoShutdown->setWhatsThis( tmp );

  tmp = i18n( "<b>Offline mode</b> button toggles the offline mode\n"
	      "on and off.\n"
	      "\n"
	      "When set, Caitoo will act as it was not connected\n"
	      "to the Internet.\n"
	      "\n"
	      "You can browse offline, while still being able to add\n"
	      "new transfers as queued." );
  m_paOfflineMode->setWhatsThis( tmp );

  tmp = i18n( "<b>Auto paste</b> button toggles the auto-paste mode\n"
	      "on and off.\n"
	      "\n"
	      "When set, Caitoo will periodically scan the clipboard\n"
	      "for URL's and paste them automatically." );
  m_paAutoPaste->setWhatsThis( tmp );

  tmp = i18n( "<b>Drop target</b> button toggles the window style\n"
	      "between a normal window and a drop target.\n"
	      "\n"
	      "When set, the main window will be hidden and\n"
	      "instead a small shaped window will appear.\n"
	      "\n"
	      "You can show/hide a normal window with a simple click\n"
	      "on a shaped window." );
  m_paDropTarget->setWhatsThis( tmp );

  tmp = i18n( "<b>Dock widget</b> button toggles the window style\n"
	      "between a normal window and a dock widget.\n"
	      "\n"
	      "When set, the main window will be hidden and\n"
	      "instead a dock widget will appear on a panel.\n"
	      "\n"
	      "You can show/hide a normal window with a simple click\n"
	      "on a dock widget." );
  m_paDockWindow->setWhatsThis( tmp );
}


void KMainWidget::setupTransferList() {
}


void KMainWidget::slotConfigureKeys()
{
  KKeyDialog::configureKeys(actionCollection(), xmlFile());
}



void KMainWidget::slotConfigureToolbars()
{
  KEditToolbar edit(factory());
  edit.exec();
}


void KMainWidget::slotImportTextFile() {
  QString filename;
  QString list;
  int i, j;

  filename = KFileDialog::getOpenURL( currentDirectory ).url();
  if ( filename.isEmpty() ) {
    return;
  }

  list = kFileToString( filename );

  i = 0;
  while ( ( j = list.find('\n', i) ) != -1 ) {
    QString newtransfer = list.mid(i,j-i);
    addTransfer( newtransfer );
    i = j + 1;
  }
}


void KMainWidget::slotImportTransfers() {
  readTransfers( true );
}


void KMainWidget::readTransfers( bool ask_for_name ) {
  kDebugInfo( 5001, "Reading transfers" );

  QString txt;
  if ( ask_for_name ) {
    txt = KFileDialog::getOpenURL( currentDirectory).url();
  } else {
    txt = locateLocal("appdata", "transfers");
  }

  if ( txt.isEmpty() ) {
    return;
  }

  myTransferList->readTransfers( txt );

  checkQueue();
  slotTransferTimeout();

  myTransferList->clearSelection();
}


void KMainWidget::slotExportTransfers() {
  writeTransfers( true );
}


void KMainWidget::writeTransfers( bool ask_for_name ) {
  kDebugInfo( 5001, "Writing transfers" );

  QString str;

  QString txt;
  if ( ask_for_name ) {
    txt = KFileDialog::getSaveURL( currentDirectory).url();
  } else {
    txt = locateLocal("appdata", "transfers");
  }

  myTransferList->writeTransfers( txt );
}


void KMainWidget::writeLog() {
  //   kDebugInfo( 5001, "Writing log to file : %s", logFileName.ascii() );

  kCStringToFile( logWindow->getText().ascii(), logFileName.ascii(), false, false );
}


void KMainWidget::slotQuit() {
  Transfer *item;
  TransferIterator it( myTransferList );

  for ( ; it.current(); ++it ) {
    item = it.current();
    if ( item->getStatus() == Transfer::ST_RUNNING && !ksettings.b_expertMode ) {
      if ( KMessageBox::warningYesNo( this, i18n("Some transfers are still running.\nAre you sure you want to close Caitoo ?"),
				      i18n("Warning") ) != KMessageBox::Yes ) {
	return;
      }
    }
  }

  ksettings.save();
  delete this;

  kapp->quit();
}


void KMainWidget::slotResumeCurrent() {
  TransferIterator it( myTransferList );

  for ( ; it.current(); ++it ) {
    if ( it.current()->isSelected() ) {
      it.current()->slotResume();
    }
  }

  slotUpdateActions();
}


void KMainWidget::slotPauseCurrent() {
  TransferIterator it( myTransferList );

  for ( ; it.current(); ++it ) {
    if ( it.current()->isSelected() ) {
      it.current()->slotPause();
    }
  }

  slotUpdateActions();
}


void KMainWidget::slotRestartCurrent() {
  TransferIterator it( myTransferList );

  for ( ; it.current(); ++it ) {
    if ( it.current()->isSelected() ) {
      it.current()->slotRestart();
    }
  }

  slotUpdateActions();
}


void KMainWidget::slotDeleteCurrent() {
  TransferIterator it( myTransferList );

  while ( it.current() ) {
    if ( it.current()->isSelected() ) {
      if ( !ksettings.b_expertMode ) {
	if ( KMessageBox::questionYesNo(this, i18n("Are you sure you want to delete this transfer ?"),
					i18n("Question") ) != KMessageBox::Yes ) {
	  return;
	}
      }
      it.current()->slotRemove();

      // don't need to update counts - they are updated automatically when
      // calling remove()

    } else {
      it++; // update counts
    }
  }

  checkQueue(); // needed !
}


void KMainWidget::pauseAll() {
  log( i18n("Pausing all jobs"), false );

  TransferIterator it( myTransferList );

  for ( ; it.current(); ++it ) {
    it.current()->slotPauseOffline();
  }
}


void KMainWidget::slotQueueCurrent() {
  TransferIterator it( myTransferList );

  for ( ; it.current(); ++it ) {
    if ( it.current()->isSelected() ) {
      it.current()->slotQueue();
    }
  }

  myTransferList->clearSelection();
}


void KMainWidget::slotTimerCurrent() {
  TransferIterator it( myTransferList );

  for ( ; it.current(); ++it ) {
    if ( it.current()->isSelected() ) {
      it.current()->slotSchedule();
    }
  }

  myTransferList->clearSelection();
}


void KMainWidget::slotDelayCurrent() {
  TransferIterator it( myTransferList );

  for ( ; it.current(); ++it ) {
    if ( it.current()->isSelected() ) {
      it.current()->slotDelay();
    }
  }

  myTransferList->clearSelection();
}


void KMainWidget::slotOpenTransfer() {
  QString newtransfer;
  bool ok = false;

  while ( !ok ) {
    newtransfer = KLineEditDlg::getText( i18n("Open transfer :"), newtransfer, &ok, this);

    // user presses cancel
    if ( !ok ) {
      return;
    }
    
    KURL url( newtransfer );
    if ( url.isMalformed() ) {
      KMessageBox::error( this, i18n("Malformed URL :\n") + newtransfer,
			  i18n( "Error" ) );
      ok = false;
    }
  }

  addTransfer( newtransfer );
}



void KMainWidget::slotCheckClipboard() {
  QString clipData = kapp->clipboard()->text();
  if ( clipData != lastClipboard ) {
    kDebugInfo( 5001, "New clipboard event");

    lastClipboard = clipData.copy();
    if ( clipData.isEmpty() || clipData.stripWhiteSpace().isEmpty() ) {
      return;
    }

    KURL url( lastClipboard.stripWhiteSpace() );
    if ( ! url.isMalformed() && ksettings.b_autoPaste ) {
      slotPasteTransfer();
    }
  }
}


void KMainWidget::slotPasteTransfer() {
  QString newtransfer;

  newtransfer = kapp->clipboard()->text();
  newtransfer = newtransfer.stripWhiteSpace();

  if ( ! ksettings.b_expertMode ) {
    KLineEditDlg *box = new KLineEditDlg( i18n("Open transfer :"),
					  newtransfer, this );
    box->show();
    
    if (!box->result()) {   // cancelled
      return;
    }
    
    newtransfer = box->text();
    delete box;
  }

  if ( ! newtransfer.isEmpty() ) {
    addTransfer( newtransfer );
  }
}


void KMainWidget::addTransfer( QString s, QString d ) {
  KURL url( s );

  // don't download file URL's TODO : uncomment
//   if ( ! strcmp( url.protocol(), "file" ) ) {
//     kDebugInfo( 5001, "File protocol not accepted !" );
//     return;
//   }

  if ( url.isMalformed() ) {
    if ( ! ksettings.b_expertMode ) {
      KMessageBox::error( this, i18n("Malformed URL :\n") + s,
			  i18n( "Error" ) );
    }
    return;
  }

  // if we find this URL in the list
  if ( myTransferList->find( s ) ) {
    if ( ! ksettings.b_expertMode ) {
      KMessageBox::error( this, i18n( "Already saving URL \n" ) + s,
			  i18n( "Error" ) );
    }
    return;
  }

  // Setup destination

  // first set destination directory to current directory ( which is also last used )
  QString destDir = currentDirectory;

  if ( ! ksettings.b_useLastDir ) {
    // check wildcards for default directory
    DirList::Iterator it;
    for ( it = ksettings.defaultDirList.begin(); it != ksettings.defaultDirList.end(); ++it ) {
      QRegExp rexp( (*it).extRegexp );
      rexp.setWildcard( true );
      
      if ( ( rexp.match( url.filename() ) ) != -1 ) {
	destDir = (*it).defaultDir;
	break;
      }
    }
  }

  KURL dest;

  if ( d.isNull() ) {  // if we didn't provide destination
    if (!ksettings.b_expertMode ) { // open the filedialog for confirmation
      KFileDialog *dlg = new KFileDialog( destDir, "*",
					  0L , "filedialog", true );
      dlg->setSelection( destDir + "/" + url.filename() );
      
      int res = dlg->exec();
      
      if ( !res ) {
	delete dlg;
	return;
      } else {
	dest = dlg->selectedURL();
	currentDirectory = dest.url();
	delete dlg;
      }
    } else {  // in expert mode don't open the filedialog
      dest = destDir + "/" + url.filename();
    }
  } else {
    dest = d;
  }

  QString file = url.filename();

  Transfer *item = myTransferList->addTransfer();

  item->setSrc( s );
  item->setDest( dest );

  item->updateAll();

  myTransferList->clearSelection();

  if ( ksettings.b_useSound ) {
    KAudioPlayer::play( ksettings.audioAdded );
  }

  checkQueue();
}


void KMainWidget::checkQueue() {
  uint numRun = 0;
  int status;

  kDebugInfo( 5001,"Checking queue");

  Transfer *item;

  if ( !ksettings.b_offlineMode && b_online ) {

    TransferIterator it( myTransferList );
    // count running transfers
    for ( ; it.current(); ++it ) {
      status = it.current()->getStatus();
      if ( status == Transfer::ST_RUNNING ||
	   status == Transfer::ST_TRYING ||
	   status == Transfer::ST_SIZE_CHECK ||
	   status == Transfer::ST_SEARCH )
	numRun++;
    }

    it.reset();
    for ( ; it.current() && numRun < ksettings.maxSimultaneousConnections; ++it ) {
      item = it.current();
      status = item->getStatus();
      if ( ( status == Transfer::ST_STOPPED ||
	     status == Transfer::ST_RETRYING ||
	     status == Transfer::ST_SIZE_CHECK ||
	     status == Transfer::ST_SEARCH ) &&
	   item->getMode() == Transfer::MD_QUEUED ) {
	log( i18n("Starting another queued job.") );
	
	if ( status == Transfer::ST_SEARCH ) {
	  item->slotSearch();
	} else if ( status == Transfer::ST_SIZE_CHECK ) {
	  item->getSize();
	} else {
	  item->slotResume();
	  numRun++;
	}
      }
    }
  }

  int stat = myTransferList->downloadStatus();
  
  if ( stat != TransferList::STATUS_LOADING) { // disconnect only when we are not downloading
    if ( ksettings.b_autoDisconnect ) {
      disconnect();
    }
    
    // shutdown only when there are no scheduled downloads
    if ( stat != TransferList::STATUS_SCHEDULED && ksettings.b_autoShutdown ) {
      log( i18n("Quitting ...") );
      slotQuit();
    }
  }

  slotUpdateActions();
  updateStatusBar();
}


void KMainWidget::slotAnimTimeout(){
  bool isTransfer;

  animCounter++;
  if ( animCounter == myTransferList->getPhasesNum() ) {
    animCounter = 0;
  }

  // update status of all items of transferList
  isTransfer = myTransferList->updateStatus( animCounter );

  if ( this->isVisible() ) {
    updateStatusBar();
  }

  // update dock widget or drop target
  if ( ksettings.windowStyle == DOCKED || ksettings.windowStyle == DROP_TARGET ) {
    int count = 0;
    int progindex[4];

    for ( int i = 0; i < 4; i++ ) {
      progindex[i] = 0;
    }

    if ( isTransfer ) {
      TransferIterator it( myTransferList );
      Transfer *item;
      while ( count < 4 &&  it.current() ) {
	item = it.current();
	if ( ( item->getStatus() == Transfer::ST_RUNNING ||
	       item->getStatus() == Transfer::ST_TRYING )
	     && item->getMode() == Transfer::MD_QUEUED ) {
	  progindex[count] = item->getPercent();
	  count++;
	}
	it++;
      }

      if ( progindex[0] == 0 ) {  // this is a hack, so that dock widget and drop target show
	progindex[0]++;        // transfer in progress, even if percent is = 0
      }
    }

    if ( ksettings.windowStyle == DOCKED ) {
      kdock->setAnim( progindex[0], progindex[1], progindex[2], b_online );
    } else {
      kdrop->setAnim( progindex[0], progindex[1], progindex[2], progindex[3], b_online );
    }
  }

}


void KMainWidget::slotTransferTimeout() {

  Transfer *item;
  TransferIterator it( myTransferList );

  bool flag = false;

  for ( ; it.current(); ++it ) {
    item = it.current();
    if ( item->getMode() == Transfer::MD_SCHEDULED &&
	 item->getStartTime() <= QDateTime::currentDateTime() ) {
      item->setMode( Transfer::MD_QUEUED );
      flag = true;
    }
  }

  if ( flag ) {
      checkQueue();
  }

  if ( ksettings.b_autoDisconnect && ksettings.b_timedDisconnect &&
       ksettings.disconnectTime <= QTime::currentTime() &&
       ksettings.disconnectDate == QDate::currentDate() ) {
    disconnect();
  }
}


void KMainWidget::slotAutosaveTimeout() {
  writeTransfers();
}


void KMainWidget::slotStatusChanged( Transfer *item, int _operation ) {
  kDebugInfo( 5001, "slotStatusChanged" );

  switch ( _operation ) {
  case Transfer::OP_FINISHED:
    delete item;  // falling through
    
  case Transfer::OP_FINISHED_KEEP:
    if ( ksettings.b_useSound ) {
      if ( myTransferList->downloadStatus() == TransferList::STATUS_EMPTY ) {
	KAudioPlayer::play( ksettings.audioFinishedAll );
      } else {
	KAudioPlayer::play( ksettings.audioFinished );
      }
      break;
    }
    
  case Transfer::OP_RESUMED:
    if ( ksettings.b_useSound ) {
      KAudioPlayer::play( ksettings.audioStarted );
    }
    break;
    
  case Transfer::OP_CANCELED:
    delete item;
    break;

  case Transfer::OP_REMOVED:
    delete item;
    return;  // checkQueue() will be called only once after all deletions

  case Transfer::OP_SCHEDULED:
    slotTransferTimeout();  // this will check schedule times
    return;  // checkQueue() is called from slotTransferTimeout()

  case Transfer::OP_SIZE_CHECKED:
  case Transfer::OP_QUEUED:
  case Transfer::OP_DELAYED:
    break;
  }

  checkQueue();
}


void KMainWidget::dragEnterEvent(QDragEnterEvent* event) {
  event->accept( QUriDrag::canDecode(event) || QTextDrag::canDecode(event) );
}


void KMainWidget::dropEvent( QDropEvent* event ) {
  QStrList list;
  QString str;
  if ( QUriDrag::decode(event, list) ) {
    addDropTransfers( &list );
  } else if ( QTextDrag::decode(event, str) ) {
    addTransfer( str );
  }
}


void KMainWidget::addDropTransfers( QStrList *list ) {
  QString s;

  for ( s = list->first(); s != 0L; s = list->next() ) {
    addTransfer( s );
  }

  myTransferList->clearSelection();
}


void KMainWidget::slotCopyToClipboard() {
  Transfer *item = (Transfer*) myTransferList->currentItem();

  if ( item ) {
    QClipboard *cb = QApplication::clipboard();
    cb->setText( item->getSrc().url() );
    myTransferList->clearSelection();
  }
}


void KMainWidget::slotMoveToBegin() {
  myTransferList->moveToBegin( (Transfer*) myTransferList->currentItem() );
}


void KMainWidget::slotMoveToEnd() {
  myTransferList->moveToEnd( (Transfer*) myTransferList->currentItem() );
}


void KMainWidget::slotOpenIndividual() {
  Transfer *item = (Transfer*) myTransferList->currentItem();

  if ( item ) {
    item->showIndividual();
  }
}


void KMainWidget::closeEvent( QCloseEvent * ){
  slotQuit();
}


void KMainWidget::setAutoSave() {
  autosaveTimer->stop();
  if ( ksettings.b_autoSave ) {
    autosaveTimer->start( ksettings.autoSaveInterval*60000 );
  }
}


void KMainWidget::setAutoDisconnect() {
  // disable action when we are connected permanently
  m_paAutoDisconnect->setEnabled( ksettings.connectionType != PERMANENT );
}


void KMainWidget::slotToggleStatusbar() {
  ksettings.b_showStatusbar = !ksettings.b_showStatusbar;

  if ( !ksettings.b_showStatusbar ) {
    statusBar()->hide();
  } else {
    statusBar()->show();
  }

  resizeEvent( 0L );
}


void KMainWidget::slotTogglePreferences() {
  b_viewPreferences = !b_viewPreferences;

  if ( b_viewPreferences ) {
    prefDlg = new DlgPreferences( 0L );
  } else {
    delete prefDlg;
  }
}


void KMainWidget::slotToggleLogWindow() {
  b_viewLogWindow = !b_viewLogWindow;

  if ( b_viewLogWindow ) {
    logWindow->show();
  } else {
    logWindow->hide();
  }
}


void KMainWidget::slotToggleAnimation() {
  ksettings.b_useAnimation = !ksettings.b_useAnimation;

  if ( !ksettings.b_useAnimation && animTimer->isActive() ) {
    animTimer->stop();
    animTimer->start( 1000 );
    animCounter = 0;
  } else {
    animTimer->stop();
    animTimer->start( 400 );
  }
}


void KMainWidget::slotToggleSound() {
  ksettings.b_useSound = !ksettings.b_useSound;
}


void KMainWidget::slotToggleOfflineMode() {
  ksettings.b_offlineMode = !ksettings.b_offlineMode;

  if ( ksettings.b_offlineMode ) {
    log( i18n("Offline mode on.") );
    pauseAll();
  } else {
    log( i18n("Offline mode off.") );
  }

  checkQueue();
}


void KMainWidget::slotToggleExpertMode() {
  ksettings.b_expertMode = !ksettings.b_expertMode;

  if ( ksettings.b_expertMode ) {
    log( i18n("Expert mode on.") );
  } else {
    log( i18n("Expert mode off.") );
  }
}


void KMainWidget::slotToggleUseLastDir() {
  ksettings.b_useLastDir = !ksettings.b_useLastDir;

  if ( ksettings.b_useLastDir ) {
    log( i18n("Use last directory on.") );
  } else {
    log( i18n("Use last directory off.") );
  }
}


void KMainWidget::slotToggleAutoDisconnect() {
  ksettings.b_autoDisconnect = !ksettings.b_autoDisconnect;

  if ( ksettings.b_autoDisconnect ) {
    log( i18n("Auto disconnect on.") );
  } else {
    log( i18n("Auto disconnect off.") );
  }
}


void KMainWidget::slotToggleAutoShutdown() {
  ksettings.b_autoShutdown = !ksettings.b_autoShutdown;

  if ( ksettings.b_autoShutdown ) {
    log( i18n("Auto shutdown on.") );
  } else {
    log( i18n("Auto shutdown off.") );
  }
}


void KMainWidget::slotToggleAutoPaste() {
  ksettings.b_autoPaste = !ksettings.b_autoPaste;

  if ( ksettings.b_autoPaste ) {
    log( i18n("Auto paste on.") );
  } else {
    log( i18n("Auto paste off.") );
  }
}


void KMainWidget::slotDock() {
  if ( ksettings.windowStyle == DOCKED ) {
    ksettings.windowStyle = NORMAL;
  } else {
    ksettings.windowStyle = DOCKED;
  }
  setWindowStyle();
}


void KMainWidget::slotDropTarget() {
  if ( ksettings.windowStyle == DROP_TARGET ) {
    ksettings.windowStyle = NORMAL;
  } else {
    ksettings.windowStyle = DROP_TARGET;
  }
  setWindowStyle();
}


void KMainWidget::slotPopupMenu( Transfer *item ) {
  myTransferList->clearSelection();
  myTransferList->setSelected( item, true );

  // select current item
  myTransferList->setCurrentItem( item );

  // set action properties only for this item
  slotUpdateActions();

  // popup transfer menu at the position
  QWidget *menu = guiFactory()->container( "transfer", this );
  ((QPopupMenu*)menu)->popup(QCursor::pos() );
}


void KMainWidget::setListFont() {
  myTransferList->setFont( ksettings.listViewFont );
}


void KMainWidget::setWindowStyle(){
  switch ( ksettings.windowStyle ) {
  case NORMAL:
    this->show();
    kdock->hide();
    kdrop->hide();
    //    KWM::switchToDesktop( KWM::desktop( winId() ) );
    break;
   
  case DOCKED:
    this->hide();
    kdock->show();
    kdrop->hide();
    break;

  case DROP_TARGET:
    this->hide();
    kdock->hide();
    kdrop->show();
    break;
  }
}


void KMainWidget::slotUpdateActions() {

  // disable all signals
  m_paQueue->blockSignals( true );
  m_paTimer->blockSignals( true );
  m_paDelay->blockSignals( true );

  // at first turn off all buttons like when nothing is selected
  m_paQueue->setChecked( false );
  m_paTimer->setChecked( false );
  m_paDelay->setChecked( false );

  m_paQueue->setEnabled( false );
  m_paTimer->setEnabled( false );
  m_paDelay->setEnabled( false );

  m_paDelete->setEnabled( false );
  m_paResume->setEnabled( false );
  m_paPause->setEnabled( false );
  m_paRestart->setEnabled( false );

  m_paCopy->setEnabled( false );
  m_paIndividual->setEnabled( false );
  m_paMoveToBegin->setEnabled( false );
  m_paMoveToEnd->setEnabled( false );

  Transfer *item;
  Transfer *first_item = 0L;
  TransferIterator it( myTransferList );
  int index = 0;

  for ( ; it.current(); ++it ) {
    if ( it.current()->isSelected() ) {
      item = it.current();

      index++; // counting number of selected items
      if ( index == 1 ) {
	first_item = item; // store first selected item
      }

      // enable PAUSE, RESUME and RESTART only when we are online and not in offline mode
      if ( ! ksettings.b_offlineMode && b_online ) {
	if ( item == first_item ) {
	  switch ( item->getStatus() ) {
	  case Transfer::ST_RUNNING :
	  case Transfer::ST_TRYING :
	    m_paResume->setEnabled( false );
	    m_paPause->setEnabled( true );
	    m_paRestart->setEnabled( true );
	    break;
	  case Transfer::ST_STOPPED :
	    m_paResume->setEnabled( true );
	    m_paPause->setEnabled( false );
	    m_paRestart->setEnabled( false );
	    break;
	  }
	} else if ( item->getStatus() != first_item->getStatus() ) {
	  // disable all when all selected items don't have the same status
	  m_paResume->setEnabled( false );
	  m_paPause->setEnabled( false );
	  m_paRestart->setEnabled( false );
	}
      }

      if ( item == first_item ) {
	m_paDelete->setEnabled( true );
	m_paCopy->setEnabled( true );
	m_paIndividual->setEnabled( true );
	m_paMoveToBegin->setEnabled( true );
	m_paMoveToEnd->setEnabled( true );

	if ( item->getStatus() != Transfer::ST_FINISHED ) {
	  m_paQueue->setEnabled( true );
	  m_paTimer->setEnabled( true );
	  m_paDelay->setEnabled( true );

	  switch ( item->getMode() ) {
	  case Transfer::MD_QUEUED :
	    m_paQueue->setChecked( true );
	    break;
	  case Transfer::MD_SCHEDULED :
	    m_paTimer->setChecked( true );
	    break;
	  case Transfer::MD_DELAYED :
	    m_paDelay->setChecked( true );
	    break;
	  }
	}
      } else if ( item->getMode() != first_item->getMode() ) {
	// unset all when all selected items don't have the same mode
	m_paQueue->setChecked( false );
	m_paTimer->setChecked( false );
	m_paDelay->setChecked( false );

	m_paQueue->setEnabled( false );
	m_paTimer->setEnabled( false );
	m_paDelay->setEnabled( false );
      }

    }  // when item is selected
  } // loop

  // enable all signals
  m_paQueue->blockSignals( false );
  m_paTimer->blockSignals( false );
  m_paDelay->blockSignals( false );
}


void KMainWidget::updateStatusBar() {
  Transfer *item;
  QString tmpstr;

  int totalFiles = 0;
  int totalSize = 0;
  int totalSpeed = 0;
  QTime remTime;

  TransferIterator it( myTransferList );

  for ( ; it.current(); ++it ) {
    item = it.current();
    if ( item->getTotalSize() != 0 ) {
      totalSize += ( item->getTotalSize() - item->getProcessedSize() );
    }
    totalFiles += ( item->getTotalFiles() - item->getProcessedFiles() );
    totalSpeed += item->getSpeed();
    
    if ( item->getRemainingTime() > remTime ) {
      remTime = item->getRemainingTime();
    }
  }

  statusBar()->changeItem( i18n( " Transfers : %1 ").arg( myTransferList->childCount() ),
			   ID_TOTAL_TRANSFERS);
  statusBar()->changeItem( i18n( " Files : %1 ").arg( totalFiles ), ID_TOTAL_FILES);
  statusBar()->changeItem( i18n( " Size : %1 ").arg( KIO::convertSize( totalSize ) ),
			   ID_TOTAL_SIZE);
  statusBar()->changeItem( i18n( " Time : %1 ").arg( remTime.toString() ), ID_TOTAL_TIME);
  statusBar()->changeItem( i18n( " %1/s ").arg( KIO::convertSize( totalSpeed ) ),
			   ID_TOTAL_SPEED);
}


void KMainWidget::disconnect() {
  if ( !b_online ) {
      return;
  }

  if ( !ksettings.b_expertMode ) {
    if ( KMessageBox::questionYesNo(this, i18n("Do you really want to disconnect ?"),
				    i18n("Question") ) != KMessageBox::Yes ) {
      return;
    }
  }
  log( i18n("Disconnecting ...") );
  system( ksettings.disconnectCommand.ascii() );
}


void KMainWidget::slotCheckConnection() {
  checkOnline();
}


void KMainWidget::checkOnline() {
  bool old = b_online;

  struct ifreq ifr;
  memset( & ifr, 0, sizeof (ifreq) );

  // setup the device name according to the type of connection and link number
  sprintf ( ifr.ifr_name, "%s%d",
	    ConnectionDevices[ksettings.connectionType].ascii(), ksettings.linkNumber );

  bool flag = false;
  if ( ksettings.connectionType != PERMANENT ) {
    // get the flags for particular device
    if (ioctl(_sock, SIOCGIFFLAGS, &ifr) < 0) {
      flag = true;
      b_online = false;
    } else if (ifr.ifr_flags == 0) {
      kDebugInfo( 5001, "Can't get flags from interface %s", ifr.ifr_name );
      b_online = false;
    } else if (ifr.ifr_flags & IFF_UP) { //   if (ifr.ifr_flags & IFF_RUNNING)
      b_online = true;
    } else {
      b_online = false;
    }
  } else {
    b_online = true; // PERMANENT connection
  }

  m_paOfflineMode->setEnabled( b_online );

  if ( b_online != old ) {
    if ( flag ) { // so that we write this only once when connection is changed
      kDebugInfo( 5001, "Unknown interface %s", ifr.ifr_name );
    }

    if ( b_online ) {
      log( i18n("We are online !") );
      checkQueue();
    } else {
      log( i18n("We are offline !") );
      pauseAll();
    }
  }
}


// Helper method for opening device socket
static int sockets_open() {
  inet_sock=socket(AF_INET, SOCK_DGRAM, 0);
  ipx_sock=socket(AF_IPX, SOCK_DGRAM, 0);
#ifdef AF_AX25
  ax25_sock=socket(AF_AX25, SOCK_DGRAM, 0);
#else
  ax25_sock=-1;
#endif
  ddp_sock=socket(AF_APPLETALK, SOCK_DGRAM, 0);
  /*
   *	Now pick any (exisiting) useful socket family for generic queries
   */
  if(inet_sock!=-1)
    return inet_sock;
  if(ipx_sock!=-1)
    return ipx_sock;
  if(ax25_sock!=-1)
    return ax25_sock;
  /*
   *	If this is -1 we have no known network layers and its time to jump.
   */
	 
  return ddp_sock;
}
