/***************************************************************************
                         listpanel.cpp
                      -------------------
copyright            : (C) 2000 by Shie Erlich & Rafi Yanai
e-mail               : krusader@users.sourceforge.net
web site             : http://krusader.sourceforge.net
---------------------------------------------------------------------------
Description 
***************************************************************************

A 

db   dD d8888b. db    db .d8888.  .d8b.  d8888b. d88888b d8888b.
88 ,8P' 88  `8D 88    88 88'  YP d8' `8b 88  `8D 88'     88  `8D
88,8P   88oobY' 88    88 `8bo.   88ooo88 88   88 88ooooo 88oobY'
88`8b   88`8b   88    88   `Y8b. 88~~~88 88   88 88~~~~~ 88`8b
88 `88. 88 `88. 88b  d88 db   8D 88   88 88  .8D 88.     88 `88.
YP   YD 88   YD ~Y8888P' `8888Y' YP   YP Y8888D' Y88888P 88   YD

                                              S o u r c e    F i l e

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

#include <unistd.h>
#include <sys/param.h>
// TQt includes
#include <tqbitmap.h>
#include <tqwhatsthis.h>
#include <tqstringlist.h>
#include <tqstrlist.h>
#include <kurldrag.h>
#include <tqheader.h>
#include <tqtimer.h>
#include <tqregexp.h> 
#include <tqsplitter.h>
// TDE includes
#include <tdepopupmenu.h>
#include <kprocess.h>
#include <tdemessagebox.h>
#include <tdelocale.h>
#include <kmimetype.h>
#include <kurl.h>
#include <ktrader.h>
#include <kopenwith.h>
#include <kuserprofile.h>
#include <kiconloader.h>
#include <kcursor.h>
#include <tdemessagebox.h>
#include <kstandarddirs.h>
#include <tdeglobalsettings.h>
#include <tqtooltip.h>
#include <tqimage.h>
#include <tqtabbar.h>
#include <kdebug.h>
#include <kurlrequester.h>
#include <kurl.h> 
#include <kmountpoint.h>

// Krusader includes
#include "../krusader.h"
#include "../krslots.h"
#include "panelfunc.h"
#include "../kicons.h"
#include "../VFS/krpermhandler.h"
#include "listpanel.h"
#include "../krusaderview.h"
#include "../panelmanager.h"
#include "../defaults.h"
#include "../resources.h"
#include "../MountMan/kmountman.h"
#include "../Dialogs/krdialogs.h"
#include "../BookMan/krbookmarkbutton.h"
#include "../Dialogs/krspwidgets.h"
#include "../Dialogs/krspecialwidgets.h"
#include "../GUI/kcmdline.h"
#include "../Dialogs/percentalsplitter.h"
#include "krdetailedview.h"
#include "krbriefview.h"
#include "krpreviewpopup.h"
#include "../GUI/dirhistorybutton.h"
#include "../GUI/dirhistoryqueue.h"
#include "../GUI/mediabutton.h"
#include "../GUI/syncbrowsebutton.h"
#include "../krservices.h"
#include "panelpopup.h" 
#include "../UserAction/useractionpopupmenu.h"
#include "../Dialogs/popularurls.h"
#include "krpopupmenu.h"

#ifdef __LIBKONQ__
#include <konq_popupmenu.h>
#include <konqbookmarkmanager.h>
#endif

typedef TQValueList<KServiceOffer> OfferList;

#define URL(X) KURL::fromPathOrURL(X)

/////////////////////////////////////////////////////
// 					The list panel constructor       //
/////////////////////////////////////////////////////
ListPanel::ListPanel( TQString typeIn, TQWidget *parent, bool &left, const char *name ) :
      TQWidget( parent, name ), panelType( typeIn ), colorMask( 255 ), compareMode( false ), currDragItem( 0 ), statsAgent( 0 ), 
		quickSearch( 0 ), cdRootButton( 0 ), cdUpButton( 0 ), popupBtn(0), popup(0),inlineRefreshJob(0), _left( left ) {

   func = new ListPanelFunc( this );
   setAcceptDrops( true );
   layout = new TQGridLayout( this, 3, 3 );

   mediaButton = new MediaButton( this, "mediaButton" );
   connect( mediaButton, TQ_SIGNAL( pressed() ), this, TQ_SLOT( slotFocusOnMe() ) );
   connect( mediaButton, TQ_SIGNAL( openUrl( const KURL& ) ), func, TQ_SLOT( openUrl( const KURL& ) ) );

   status = new KrSqueezedTextLabel( this );
   krConfig->setGroup( "Look&Feel" );
   status->setFont( krConfig->readFontEntry( "Filelist Font", _FilelistFont ) );
   status->setBackgroundMode( PaletteBackground );
   status->setFrameStyle( TQFrame::Box | TQFrame::Raised );
   status->setLineWidth( 1 );		// a nice 3D touch :-)
   status->setText( "" );        // needed for initialization code!
   status->enableDrops( true );
   int sheight = TQFontMetrics( status->font() ).height() + 4;
   status->setMaximumHeight( sheight );
   TQWhatsThis::add
      ( status, i18n( "The statusbar displays information about the FILESYSTEM "
                      "which holds your current directory: Total size, free space, "
                      "type of filesystem, etc." ) );
   connect( status, TQ_SIGNAL( clicked() ), this, TQ_SLOT( slotFocusOnMe() ) );
   connect( status, TQ_SIGNAL( dropped( TQDropEvent *) ), this, TQ_SLOT( handleDropOnStatus(TQDropEvent *) ) );

   // ... create the history button
   dirHistoryQueue = new DirHistoryQueue( this );
   historyButton = new DirHistoryButton( dirHistoryQueue, this, "historyButton" );
   connect( historyButton, TQ_SIGNAL( pressed() ), this, TQ_SLOT( slotFocusOnMe() ) );
   connect( historyButton, TQ_SIGNAL( openUrl( const KURL& ) ), func, TQ_SLOT( openUrl( const KURL& ) ) );

	bookmarksButton = new KrBookmarkButton(this);
	connect( bookmarksButton, TQ_SIGNAL( pressed() ), this, TQ_SLOT( slotFocusOnMe() ) );
   connect( bookmarksButton, TQ_SIGNAL( openUrl( const KURL& ) ), func, TQ_SLOT( openUrl( const KURL& ) ) );
	TQWhatsThis::add
      ( bookmarksButton, i18n( "Open menu with bookmarks. You can also add "
                               "current location to the list, edit bookmarks "
                               "or add subfolder to the list." ) );
										 
   TQHBoxLayout *totalsLayout = new TQHBoxLayout;
	totals = new KrSqueezedTextLabel( this );
   krConfig->setGroup( "Look&Feel" );
   totals->setFont( krConfig->readFontEntry( "Filelist Font", _FilelistFont ) );
   totals->setFrameStyle( TQFrame::Box | TQFrame::Raised );
   totals->setBackgroundMode( PaletteBackground );
   totals->setLineWidth( 1 );		// a nice 3D touch :-)
   totals->setMaximumHeight( sheight );
   totals->enableDrops( true );
   TQWhatsThis::add
      ( totals, i18n( "The totals bar shows how many files exist, "
                      "how many selected and the bytes math" ) );
   connect( totals, TQ_SIGNAL( clicked() ), this, TQ_SLOT( slotFocusOnMe() ) );
   connect( totals, TQ_SIGNAL( dropped( TQDropEvent *) ), this, TQ_SLOT( handleDropOnTotals(TQDropEvent *) ) );  
   
	// a cancel button for the inplace refresh mechanism
	inlineRefreshCancelButton = new KPushButton(this);
	inlineRefreshCancelButton->setFixedSize( 22, 20 );
	inlineRefreshCancelButton->setPixmap(krLoader->loadIcon("cancel", TDEIcon::Toolbar, 16));
	connect(inlineRefreshCancelButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(inlineRefreshCancel()));

	// a quick button to open the popup panel
	popupBtn = new TQToolButton( this, "popupbtn" );
	popupBtn->setFixedSize( 22, 20 );
	popupBtn->setPixmap(krLoader->loadIcon("1uparrow", TDEIcon::Toolbar, 16));
	connect(popupBtn, TQ_SIGNAL(clicked()), this, TQ_SLOT(togglePanelPopup()));
	TQToolTip::add(  popupBtn, i18n( "Open the popup panel" ) );
	totalsLayout->addWidget(totals);
	totalsLayout->addWidget(inlineRefreshCancelButton); inlineRefreshCancelButton->hide();
	totalsLayout->addWidget(popupBtn);
   
   quickSearch = new KrQuickSearch( this );
   krConfig->setGroup( "Look&Feel" );
   quickSearch->setFont( krConfig->readFontEntry( "Filelist Font", _FilelistFont ) );
   quickSearch->setFrameStyle( TQFrame::Box | TQFrame::Raised );
   quickSearch->setLineWidth( 1 );		// a nice 3D touch :-)
   quickSearch->setMaximumHeight( sheight );

   TQHBox * hbox = new TQHBox( this );

	// clear-origin button
	bool clearButton = krConfig->readBoolEntry("Clear Location Bar Visible", _ClearLocation);
	if (clearButton){
		clearOrigin = new TQToolButton(hbox, "clearorigin");
		clearOrigin->setPixmap(krLoader->loadIcon("locationbar_erase", TDEIcon::Toolbar, 16));
		TQToolTip::add(  clearOrigin, i18n( "Clear the location bar" ) );
	}
	
	QuickNavLineEdit *qnle = new QuickNavLineEdit(this);
   origin = new KURLRequester( qnle, hbox );
   TQPixmap pixMap = origin->button() ->iconSet() ->pixmap( TQIconSet::Small, TQIconSet::Normal );
   origin->button() ->setFixedSize( pixMap.width() + 4, pixMap.height() + 4 );
   TQWhatsThis::add
      ( origin, i18n( "Use superb TDE file dialog to choose location. " ) );
   origin->setShowLocalProtocol( false );
   origin->lineEdit() ->setURLDropsEnabled( true );
   origin->lineEdit() ->installEventFilter( this );
   TQWhatsThis::add
      ( origin->lineEdit(), i18n( "Name of directory where you are. You can also "
                                  "enter name of desired location to move there. "
                                  "Use of Net protocols like ftp or fish is possible." ) );
   origin->setMode( KFile::Directory | KFile::ExistingOnly );
   connect( origin, TQ_SIGNAL( returnPressed( const TQString& ) ), func, TQ_SLOT( openUrl( const TQString& ) ) );
   connect( origin, TQ_SIGNAL( returnPressed( const TQString& ) ), this, TQ_SLOT( slotFocusOnMe() ) );
   connect( origin, TQ_SIGNAL( urlSelected( const TQString& ) ), func, TQ_SLOT( openUrl( const TQString& ) ) );
   connect( origin, TQ_SIGNAL( urlSelected( const TQString& ) ), this, TQ_SLOT( slotFocusOnMe() ) );
   
	// this is here on purpose, do not move up!
	if (clearButton) {
		clearOrigin->setFixedSize( 20, origin->button() ->height() );
		connect(clearOrigin, TQ_SIGNAL(clicked()), origin->lineEdit(), TQ_SLOT(clear()));
		connect(clearOrigin, TQ_SIGNAL(clicked()), origin->lineEdit(), TQ_SLOT(setFocus()));
	}
	//
   
   cdOtherButton = new TQToolButton( hbox, "cdOtherButton" );
   cdOtherButton->setFixedSize( 20, origin->button() ->height() );
   cdOtherButton->setText( i18n( "=" ) );
	TQToolTip::add(  cdOtherButton, i18n( "Equal" ) );
   connect( cdOtherButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( slotFocusAndCDOther() ) );

   cdUpButton = new TQToolButton( hbox, "cdUpButton" );
   cdUpButton->setFixedSize( 20, origin->button() ->height() );
   cdUpButton->setText( i18n( ".." ) );
	TQToolTip::add(  cdUpButton, i18n( "Up" ) );
   connect( cdUpButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( slotFocusAndCDup() ) );

   cdHomeButton = new TQToolButton( hbox, "cdHomeButton" );
   cdHomeButton->setFixedSize( 20, origin->button() ->height() );
   cdHomeButton->setText( i18n( "~" ) );
	TQToolTip::add(  cdHomeButton, i18n( "Home" ) );
   connect( cdHomeButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( slotFocusAndCDHome() ) );

   cdRootButton = new TQToolButton( hbox, "cdRootButton" );
   cdRootButton->setFixedSize( 20, origin->button() ->height() );
   cdRootButton->setText( i18n( "/" ) );
	TQToolTip::add(  cdRootButton, i18n( "Root" ) );
   connect( cdRootButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( slotFocusAndCDRoot() ) );

   // ... creates the button for sync-browsing
   syncBrowseButton = new SyncBrowseButton( hbox );

   setPanelToolbar();

   header = new TQHeader( this );
   header->hide();

	// create a splitter to hold the view and the popup
	splt = new PercentalSplitter(this);
	splt->setChildrenCollapsible(true);
	splt->setOrientation(TQt::Vertical);

	createView();
	
   // make sure that a focus/path change reflects in the command line and activePanel
   connect( this, TQ_SIGNAL( cmdLineUpdate( TQString ) ), SLOTS, TQ_SLOT( slotCurrentChanged( TQString ) ) );
   connect( this, TQ_SIGNAL( activePanelChanged( ListPanel * ) ), SLOTS, TQ_SLOT( slotSetActivePanel( ListPanel * ) ) );
	
	// add a popup
	popup = new PanelPopup(splt, left);
	connect(popup, TQ_SIGNAL(selection(const KURL&)), SLOTS, TQ_SLOT(refresh(const KURL&)));
	connect(popup, TQ_SIGNAL(hideMe()), this, TQ_SLOT(togglePanelPopup()));
	popup->hide();
	
   // finish the layout
	layout->addMultiCellWidget( hbox, 0, 0, 0, 3 );
   layout->addWidget( mediaButton, 1, 0 );
   layout->addWidget( status, 1, 1 );
   layout->addWidget( historyButton, 1, 2 );
   layout->addWidget( bookmarksButton, 1, 3 );
   layout->addMultiCellWidget( header, 2, 2, 0, 3 );
   layout->addMultiCellWidget( splt, 3, 3, 0, 3 );
   layout->addMultiCellWidget( quickSearch, 4, 4, 0, 3 );
   quickSearch->hide();
   layout->addMultiCellLayout( totalsLayout, 5, 5, 0, 3 );
   //filter = ALL;
}

void ListPanel::createView()
{
	header->hide();
	if( panelType == "Brief" )
	{
		view = new KrBriefView( header, splt, _left, krConfig );
		view->init();
		
		connect( dynamic_cast<KrBriefView*>( view ), TQ_SIGNAL( middleButtonClicked( KrViewItem * ) ), SLOTS, TQ_SLOT( newTab( KrViewItem * ) ) );
		connect( dynamic_cast<KrBriefView*>( view ), TQ_SIGNAL( currentChanged( KrViewItem * ) ), 
			SLOTS, TQ_SLOT( updatePopupPanel( KrViewItem* ) ) );

		// connect quicksearch
		connect( quickSearch, TQ_SIGNAL( textChanged( const TQString& ) ),
			dynamic_cast<KrBriefView*>( view ), TQ_SLOT( quickSearch( const TQString& ) ) );
		connect( quickSearch, TQ_SIGNAL( otherMatching( const TQString&, int ) ),
			dynamic_cast<KrBriefView*>( view ), TQ_SLOT( quickSearch( const TQString& , int ) ) );
		connect( quickSearch, TQ_SIGNAL( stop( TQKeyEvent* ) ),
			dynamic_cast<KrBriefView*>( view ), TQ_SLOT( stopQuickSearch( TQKeyEvent* ) ) );
		connect( quickSearch, TQ_SIGNAL( process( TQKeyEvent* ) ),
			dynamic_cast<KrBriefView*>( view ), TQ_SLOT( handleQuickSearchEvent( TQKeyEvent* ) ) );
	} else { /* Detailed */
		panelType = "Detailed";
		view = new KrDetailedView( splt, _left, krConfig );
		view->init();
		connect( dynamic_cast<KrDetailedView*>( view ), TQ_SIGNAL( middleButtonClicked( KrViewItem * ) ), SLOTS, TQ_SLOT( newTab( KrViewItem * ) ) );
		connect( dynamic_cast<KrDetailedView*>( view ), TQ_SIGNAL( currentChanged( KrViewItem * ) ), 
			SLOTS, TQ_SLOT( updatePopupPanel( KrViewItem * ) ) );
		// connect quicksearch
		connect( quickSearch, TQ_SIGNAL( textChanged( const TQString& ) ),
			dynamic_cast<KrDetailedView*>( view ), TQ_SLOT( quickSearch( const TQString& ) ) );
		connect( quickSearch, TQ_SIGNAL( otherMatching( const TQString&, int ) ),
			dynamic_cast<KrDetailedView*>( view ), TQ_SLOT( quickSearch( const TQString& , int ) ) );
		connect( quickSearch, TQ_SIGNAL( stop( TQKeyEvent* ) ),
			dynamic_cast<KrDetailedView*>( view ), TQ_SLOT( stopQuickSearch( TQKeyEvent* ) ) );
		connect( quickSearch, TQ_SIGNAL( process( TQKeyEvent* ) ),
			dynamic_cast<KrDetailedView*>( view ), TQ_SLOT( handleQuickSearchEvent( TQKeyEvent* ) ) );
	}

   connect( view->op(), TQ_SIGNAL( renameItem( const TQString &, const TQString & ) ),
            func, TQ_SLOT( rename( const TQString &, const TQString & ) ) );
   connect( view->op(), TQ_SIGNAL( executed( TQString& ) ), func, TQ_SLOT( execute( TQString& ) ) );
   connect( view->op(), TQ_SIGNAL( needFocus() ), this, TQ_SLOT( slotFocusOnMe() ) );
   connect( view->op(), TQ_SIGNAL( selectionChanged() ), this, TQ_SLOT( slotUpdateTotals() ) );
   connect( view->op(), TQ_SIGNAL( itemDescription( TQString& ) ), krApp, TQ_SLOT( statusBarUpdate( TQString& ) ) );
   connect( view->op(), TQ_SIGNAL( contextMenu( const TQPoint & ) ), this, TQ_SLOT( popRightClickMenu( const TQPoint & ) ) );
   connect( view->op(), TQ_SIGNAL( emptyContextMenu( const TQPoint &) ), 
   	this, TQ_SLOT( popEmptyRightClickMenu( const TQPoint & ) ) );
   connect( view->op(), TQ_SIGNAL( letsDrag( TQStringList, TQPixmap ) ), this, TQ_SLOT( startDragging( TQStringList, TQPixmap ) ) );
   connect( view->op(), TQ_SIGNAL( gotDrop( TQDropEvent * ) ), this, TQ_SLOT( handleDropOnView( TQDropEvent * ) ) );
}

void ListPanel::changeType( const TQString & type )
{
   if( panelType != type )
   {
      panelType = type;
      delete view;
      createView();

      slotStartUpdate();

      if( panelType == "Brief" )
         dynamic_cast<KrBriefView*>( view )->show();
      else /* Detailed */
         dynamic_cast<KrDetailedView*>( view )->show();
   }
}

ListPanel::~ListPanel() {
   delete func;
   delete view;
   delete status;
   delete bookmarksButton;
   delete totals;
   delete quickSearch;
   delete origin;
   delete cdRootButton;
   delete cdHomeButton;
   delete cdUpButton;
   delete cdOtherButton;
   delete syncBrowseButton;
   delete layout;
}

int ListPanel::getProperties()
{
   int props = 0;
   if( syncBrowseButton->state() == SYNCBROWSE_CD )
      props |= PROP_SYNC_BUTTON_ON;
   return props;
}

void ListPanel::setProperties( int prop )
{
   if( prop & PROP_SYNC_BUTTON_ON )
       syncBrowseButton->setOn( true );
   else
       syncBrowseButton->setOn( false );
}

bool ListPanel::eventFilter ( TQObject * watched, TQEvent * e ) {
	if( e->type() == TQEvent::KeyPress && origin->lineEdit() == watched ) {
		TQKeyEvent *ke = (TQKeyEvent *)e;
		
		if( ( ke->key() ==  Key_Down ) && ( ke->state() == ControlButton ) ) {
			slotFocusOnMe();
			return true;
		}
	}
	return false;
}


void ListPanel::togglePanelPopup() {
	if (popup->isHidden()) {
		if (popupSizes.count() > 0) {
			dynamic_cast<TQSplitter*>(popup->parent())->setSizes(popupSizes);
		} else { // on the first time, resize to 50%
			TQValueList<int> lst;
			lst << height()/2 << height()/2;
			dynamic_cast<TQSplitter*>(popup->parent())->setSizes(lst);
		}
		
		popup->show();
		popupBtn->setPixmap(krLoader->loadIcon("1downarrow", TDEIcon::Toolbar, 16));
		TQToolTip::add(  popupBtn, i18n( "Close the popup panel" ) );
	} else {
		popupSizes.clear();
		popupSizes = dynamic_cast<TQSplitter*>(popup->parent())->sizes();
		popup->hide();
		popupBtn->setPixmap(krLoader->loadIcon("1uparrow", TDEIcon::Toolbar, 16));
		TQToolTip::add(  popupBtn, i18n( "Open the popup panel" ) );
		
		TQValueList<int> lst;
		lst << height() << 0;
		dynamic_cast<TQSplitter*>(popup->parent())->setSizes(lst);
		if( ACTIVE_PANEL )
			ACTIVE_PANEL->slotFocusOnMe();
	}
}

KURL ListPanel::virtualPath() const {
	return func->files()->vfs_getOrigin(); 
}

TQString ListPanel::realPath() const { 
	return _realPath.path(); 
}


void ListPanel::setPanelToolbar() {
   krConfig->setGroup( "Look&Feel" );

   bool panelToolBarVisible = krConfig->readBoolEntry( "Panel Toolbar visible", _PanelToolBar );

   if ( panelToolBarVisible && ( krConfig->readBoolEntry( "Root Button Visible", _cdRoot ) ) )
      cdRootButton->show();
   else
      cdRootButton->hide();

   if ( panelToolBarVisible && ( krConfig->readBoolEntry( "Home Button Visible", _cdHome ) ) )
      cdHomeButton->show();
   else
      cdHomeButton->hide();

   if ( panelToolBarVisible && ( krConfig->readBoolEntry( "Up Button Visible", _cdUp ) ) )
      cdUpButton->show();
   else
      cdUpButton->hide();

   if ( panelToolBarVisible && ( krConfig->readBoolEntry( "Equal Button Visible", _cdOther ) ) )
      cdOtherButton->show();
   else
      cdOtherButton->hide();

   if ( !panelToolBarVisible || ( krConfig->readBoolEntry( "Open Button Visible", _Open ) ) )
      origin->button() ->show();
   else
      origin->button() ->hide();

   if ( panelToolBarVisible && ( krConfig->readBoolEntry( "SyncBrowse Button Visible", _syncBrowseButton ) ) )
      syncBrowseButton->show();
   else
      syncBrowseButton->hide();
}

void ListPanel::slotUpdateTotals() {
   totals->setText( view->statistics() );
}

void ListPanel::slotFocusAndCDOther() {
   slotFocusOnMe();
   func->openUrl( otherPanel->func->files() ->vfs_getOrigin() );

}

void ListPanel::slotFocusAndCDHome() {
   slotFocusOnMe();
   func->openUrl( TQString( "~" ), TQString() );
}

void ListPanel::slotFocusAndCDup() {
   slotFocusOnMe();
   func->dirUp();
}

void ListPanel::slotFocusAndCDRoot() {
   slotFocusOnMe();
   func->openUrl( TQString( "/" ), TQString() );
}

void ListPanel::select( KRQuery query, bool select) {
   if ( !query.isNull() ) {
      if ( select )
         view->select( query );
      else
         view->unselect( query );
   }
}

void ListPanel::select( bool select, bool all ) {
   if ( all )
   {
      if ( select )
         view->select( KRQuery( "*" ) );
      else
         view->unselect( KRQuery( "*" ) );
   }
   else {
      KRQuery query = KRSpWidgets::getMask( ( select ? i18n( " Select Files " ) : i18n( " Unselect Files " ) ) );
      // if the user canceled - quit
      if ( query.isNull() )
         return ;
      if ( select )
         view->select( query );
      else
         view->unselect( query );
   }
}

void ListPanel::invertSelection() {
   view->invertSelection();
}

void ListPanel::compareDirs() {
   krConfig->setGroup( "Private" );
   int compareMode = krConfig->readNumEntry( "Compare Mode", 0 );
   krConfig->setGroup( "Look&Feel" );
   bool selectDirs = krConfig->readBoolEntry( "Mark Dirs", false );
  
   KrViewItem *item, *otherItem;
  
   for( item = view->getFirst(); item != 0; item = view->getNext( item ) )
   {
      if( item->name() == ".." )
         continue;
      
      for( otherItem = otherPanel->view->getFirst(); otherItem != 0 && otherItem->name() != item->name() ;
         otherItem = otherPanel->view->getNext( otherItem ) );
           
      bool isSingle = ( otherItem == 0 ), isDifferent = false, isNewer = false;
   
      if( func->getVFile(item)->vfile_isDir() && !selectDirs )
      {
         item->setSelected( false );
         continue;
      }
      
      if( otherItem )
      {
         if( !func->getVFile(item)->vfile_isDir() )
            isDifferent = ITEM2VFILE(otherPanel,otherItem)->vfile_getSize() != func->getVFile(item)->vfile_getSize();
         isNewer = func->getVFile(item)->vfile_getTime_t() > ITEM2VFILE(otherPanel, otherItem)->vfile_getTime_t();
      }

      switch( compareMode )
      {
      case 0:
         item->setSelected( isNewer || isSingle );
         break;
      case 1:
         item->setSelected( isNewer );
         break;
      case 2:
         item->setSelected( isSingle );
         break;
      case 3:
         item->setSelected( isDifferent || isSingle );
         break;
      case 4:
         item->setSelected( isDifferent );
         break;
      }
   }
   
   view->updateView();
}

void ListPanel::slotFocusOnMe() {
   // give this VFS the focus (the path bar)
   // we start by calling the KVFS function
   krConfig->setGroup( "Look&Feel" );

   // take care of the 'otherpanel'
   TQPalette q( otherPanel->status->palette() );
   q.setColor( TQColorGroup::Foreground, TDEGlobalSettings::textColor() );
   q.setColor( TQColorGroup::Background, TDEGlobalSettings::baseColor() );

   otherPanel->status->setPalette( q );
   otherPanel->totals->setPalette( q );
   otherPanel->view->prepareForPassive();

   // now, take care of this panel
   TQPalette p( status->palette() );
   p.setColor( TQColorGroup::Foreground, TDEGlobalSettings::highlightedTextColor() );
   p.setColor( TQColorGroup::Background, TDEGlobalSettings::highlightColor() );
   status->setPalette( p );
   totals->setPalette( p );

   view->prepareForActive();
   emit cmdLineUpdate( realPath() );
   emit activePanelChanged( this );

   func->refreshActions();
   
   Krusader::actDetailedView->setEnabled( panelType != "Detailed" ); // enable/disable the detailed view action
   Krusader::actBriefView->setEnabled( panelType != "Brief" );       // enable/disable the brief view action

   if( panelType == "Brief" )
   {
      KrBriefView * v = dynamic_cast<KrBriefView *>( view );
      if ( v )
         v->refreshColors();
   }
   else /* detailed */
   {
      KrDetailedView * v = dynamic_cast<KrDetailedView *>( view );
      if ( v )
         v->refreshColors();
   }
   
   if( otherPanel->panelType == "Brief" )
   {
      KrBriefView * v = dynamic_cast<KrBriefView *>( otherPanel->view );
      if ( v )
         v->refreshColors();
   }
   else /* detailed */
   {   
      KrDetailedView *v = dynamic_cast<KrDetailedView *>( otherPanel->view );
      if ( v )
         v->refreshColors();
   }
}

// this is used to start the panel, AFTER setOther() has been used
//////////////////////////////////////////////////////////////////
void ListPanel::start( KURL url, bool immediate ) {
   KURL virt;
   
   virt = url;
   
   if ( !virt.isValid() )
      virt = URL("/");
   if( virt.isLocalFile() ) _realPath = virt;
   else _realPath = URL("/");

   if( immediate )
     func->immediateOpenUrl( virt );
   else
     func->openUrl( virt );

   slotFocusOnMe();
   setJumpBack( virt );
}

void ListPanel::slotStartUpdate() {
   while ( func->inRefresh )
      ; // wait until the last refresh finish
   func->inRefresh = true;  // make sure the next refresh wait for this one
	if (inlineRefreshJob)
		inlineRefreshListResult(0);

	if( this == ACTIVE_PANEL ){
		slotFocusOnMe();
	}

   setCursor( KCursor::workingCursor() );
   view->clear();

   if ( func->files() ->vfs_getType() == vfs::NORMAL )
      _realPath = virtualPath();
   this->origin->setURL( vfs::pathOrURL( virtualPath() ) );
   emit pathChanged( this );
   emit cmdLineUpdate( realPath() );	// update the command line

   slotGetStats( virtualPath() );
   slotUpdate();
   if ( compareMode ) {
      otherPanel->view->clear();
      otherPanel->slotUpdate();
   }
   // return cursor to normal arrow
   setCursor( KCursor::arrowCursor() );
   slotUpdateTotals();
	krApp->popularUrls->addUrl(virtualPath());
}

void ListPanel::slotUpdate() {
   // if we are not at the root add the ".." entery
   TQString protocol = func->files() ->vfs_getOrigin().protocol();
   bool isFtp = ( protocol == "ftp" || protocol == "smb" || protocol == "sftp" || protocol == "fish" );

   TQString origin = virtualPath().prettyURL(-1);
   if ( origin.right( 1 ) != "/" && !( ( func->files() ->vfs_getType() == vfs::FTP ) && isFtp &&
                                       origin.find( '/', origin.find( ":/" ) + 3 ) == -1 ) ) {
      view->addItems( func->files() );
   } else
      view->addItems( func->files(), false );

   func->inRefresh = false;
}


void ListPanel::slotGetStats( const KURL& url ) {
   if ( !url.isLocalFile() ) {
      status->setText( i18n( "No space information on non-local filesystems" ) );
      return ;
   }

	// check for special filesystems;
	TQString path = url.path(); // must be local url
	if ( path.left(4) == "/dev") {
		status->setText(i18n( "No space information on [dev]" ));
		return;
	}
#if defined(BSD)
	if ( path.left( 5 ) == "/procfs" ) { // /procfs is a special case - no volume information
		status->setText(i18n( "No space information on [procfs]" ));
		return;
	}
#else
	if ( path.left( 5 ) == "/proc" ) { // /proc is a special case - no volume information
		status->setText(i18n( "No space information on [proc]" ));
		return;
	}
#endif

   status->setText( i18n( "Mt.Man: working ..." ) );
	statsAgent = KDiskFreeSp::findUsageInfo( path );
   connect( statsAgent, TQ_SIGNAL( foundMountPoint( const TQString &, unsigned long, unsigned long, unsigned long ) ),
            this, TQ_SLOT( gotStats( const TQString &, unsigned long, unsigned long, unsigned long ) ) );
}

void ListPanel::gotStats( const TQString &mountPoint, unsigned long kBSize,
                          unsigned long,  unsigned long kBAvail ) {
	int perc = 0;
	if (kBSize != 0) { // make sure that if totalsize==0, then perc=0
		perc = (int)(((float)kBAvail / (float)kBSize)*100.0);
	}
	// mount point information - find it in the list first
	KMountPoint::List lst = KMountPoint::currentMountPoints();
	TQString fstype = i18n("unknown");
   for (KMountPoint::List::iterator it = lst.begin(); it != lst.end(); ++it) {
		if ((*it)->mountPoint() == mountPoint) {
			fstype = (*it)->mountType();
			break;
		}
	}
	
	TQString stats = i18n( "%1 free out of %2 (%3%) on %4 [ (%5) ]" )
        .arg( TDEIO::convertSizeFromKB( kBAvail ) )
        .arg( TDEIO::convertSizeFromKB( kBSize ) ).arg( perc )
        .arg( mountPoint ).arg( fstype );
	status->setText( stats );
}

void ListPanel::handleDropOnTotals( TQDropEvent *e ) {
  handleDropOnView( e, totals );
}

void ListPanel::handleDropOnStatus( TQDropEvent *e ) {
  handleDropOnView( e, status );
}

void ListPanel::handleDropOnView( TQDropEvent *e, TQWidget *widget ) {
   // if copyToPanel is true, then we call a simple vfs_addfiles
   bool copyToDirInPanel = false;
   bool dragFromOtherPanel = false;
   bool dragFromThisPanel = false;
   bool isWritable = func->files() ->vfs_isWritable();

   vfs* tempFiles = func->files();
   vfile *file;
   KrViewItem *i = 0;
   if( widget == 0 )
   {
      i = view->getKrViewItemAt( e->pos() );
      widget = this;
   }

   if ( e->source() == otherPanel )
      dragFromOtherPanel = true;
   if ( e->source() == this )
      dragFromThisPanel = true;

   if ( i ) {
      file = func->files() ->vfs_search( i->name() );

      if ( !file ) { // trying to drop on the ".."
            copyToDirInPanel = true;
      } else {
         if ( file->vfile_isDir() ) {
            copyToDirInPanel = true;
            isWritable = file->vfile_isWriteable();
            if ( isWritable ) {
               // keep the folder_open icon until we're finished, do it only
               // if the folder is writeable, to avoid flicker
            }
         } else
            if ( e->source() == this )
               return ; // no dragging onto ourselves
      }
   } else    // if dragged from this panel onto an empty spot in the panel...
      if ( dragFromThisPanel ) {  // leave!
         e->ignore();
         return ;
      }

   if ( !isWritable && getuid() != 0 ) {
      e->ignore();
      KMessageBox::sorry( 0, i18n( "Can't drop here, no write permissions." ) );
      return ;
   }
   //////////////////////////////////////////////////////////////////////////////
   // decode the data
   KURL::List URLs;
   if ( !KURLDrag::decode( e, URLs ) ) {
      e->ignore(); // not for us to handle!
      return ;
   }

   bool isLocal = true;
   for( unsigned u = 0; u != URLs.count(); u++ )
     if( !URLs[ u ].isLocalFile() ) {
       isLocal = false;
       break;
     }

   TDEIO::CopyJob::CopyMode mode = TDEIO::CopyJob::Copy;

   // the KURL::List is finished, let's go
   // --> display the COPY/MOVE/LINK menu
   TQPopupMenu popup( this );
   popup.insertItem( i18n( "Copy Here" ), 1 );
   if ( func->files() ->vfs_isWritable() )
      popup.insertItem( i18n( "Move Here" ), 2 );
   if ( func->files() ->vfs_getType() == vfs::NORMAL &&
         isLocal )
      popup.insertItem( i18n( "Link Here" ), 3 );
   popup.insertItem( i18n( "Cancel" ), 4 );
   TQPoint tmp = widget->mapToGlobal( e->pos() );
   int result = popup.exec( tmp );
   switch ( result ) {
         case 1 :
         mode = TDEIO::CopyJob::Copy;
         break;
         case 2 :
         mode = TDEIO::CopyJob::Move;
         break;
         case 3 :
         mode = TDEIO::CopyJob::Link;
         break;
         case - 1 :         // user pressed outside the menu
         case 4:
         return ; // cancel was pressed;
   }

   TQString dir = "";
   if ( copyToDirInPanel ) {
      dir = i->name();
   }
   TQWidget *notify = ( !e->source() ? 0 : e->source() );
   tempFiles->vfs_addFiles( &URLs, mode, notify, dir );
}

void ListPanel::startDragging( TQStringList names, TQPixmap px ) {
   KURL::List * urls = func->files() ->vfs_getFiles( &names );

   if ( urls->isEmpty() ) { // avoid draging empty urls
      delete urls;
      return ;
   }

	KURLDrag *d = new KURLDrag(*urls, this);
   d->setPixmap( px, TQPoint( -7, 0 ) );
   d->dragCopy();

   delete urls; // free memory
}

// pops a right-click menu for items
void ListPanel::popRightClickMenu( const TQPoint &loc ) {
   // run it, on the mouse location
   int j = TQFontMetrics( font() ).height() * 2;
   KrPopupMenu::run(TQPoint( loc.x() + 5, loc.y() + j ), this);
}

void ListPanel::popEmptyRightClickMenu( const TQPoint &loc ) {
	KrPopupMenu::run(loc, this);
}

void ListPanel::setFilter( KrViewProperties::FilterSpec f ) {
   switch ( f ) {
         case KrViewProperties::All :
         //case KrView::EXEC:
         break;
         case KrViewProperties::Custom :
         filterMask = KRSpWidgets::getMask( i18n( " Select Files " ) );
         // if the user canceled - quit
         if ( filterMask.isNull() )
            return;
         view->setFilterMask( filterMask );
         break;
         default:
         return ;
   }
   view->setFilter( f ); // do that in any case
   func->files()->vfs_invalidate();
   func->refresh();
}

TQString ListPanel::getCurrentName() {
   TQString name = view->getCurrentItem();
   if ( name != ".." )
      return name;
   else
      return TQString();
}

void ListPanel::prepareToDelete() {
   view->setNameToMakeCurrent( view->firstUnmarkedBelowCurrent() );
}

void ListPanel::keyPressEvent( TQKeyEvent *e ) {
   switch ( e->key() ) {
         case Key_Enter :
         case Key_Return :
         if ( e->state() & ControlButton ) {
         	if (e->state() & AltButton) {
         		vfile *vf = func->files()->vfs_search(view->getCurrentKrViewItem()->name());
         		if (vf && vf->vfile_isDir()) SLOTS->newTab(vf->vfile_getUrl());
         	} else {
					SLOTS->insertFileName( ( e->state() & ShiftButton ) != 0 );
            }
         } else e->ignore();
         break;
         case Key_Right :
         case Key_Left :
         if ( e->state() == ControlButton ) {
            // user pressed CTRL+Right/Left - refresh other panel to the selected path if it's a
            // directory otherwise as this one
            if ( ( _left && e->key() == Key_Right ) || ( !_left && e->key() == Key_Left ) ) {
               KURL newPath;
               KrViewItem *it = view->getCurrentKrViewItem();

               if( it->name() == ".." ) {
                  newPath = func->files()->vfs_getOrigin().upURL();
               } else {
                  vfile *v = func->getVFile( it );
                  if ( v && v->vfile_isDir() && v->vfile_getName() != ".." ) {
                     newPath = v->vfile_getUrl();
                  } else {
                     newPath = func->files() ->vfs_getOrigin();
                  }
               }
               otherPanel->func->openUrl( newPath );
            } else func->openUrl( otherPanel->func->files() ->vfs_getOrigin() );
            return ;
         } else
            e->ignore();
         break;

         case Key_Down :
         if ( e->state() == ControlButton ) { // give the keyboard focus to the command line
            if ( MAIN_VIEW->cmdLine->isVisible() )
               MAIN_VIEW->cmdLineFocus();
            else 
               MAIN_VIEW->focusTerminalEmulator();
            return ;
         } else if ( e->state() == ( ControlButton | ShiftButton ) ) { // give the keyboard focus to TE
           MAIN_VIEW->focusTerminalEmulator();
         } else
            e->ignore();
         break;

			case Key_Up :
          if ( e->state() == ControlButton ) { // give the keyboard focus to the command line
            origin->lineEdit()->setFocus();
            return ;
          } else
             e->ignore();
			break;

         default:
         // if we got this, it means that the view is not doing
         // the quick search thing, so send the characters to the commandline, if normal key
         if ( e->state() == TQt::NoButton )
            MAIN_VIEW->cmdLine->addText( e->text() );

         //e->ignore();
   }
}

void ListPanel::slotItemAdded(vfile *vf) {
	view->addItem(vf);
}

void ListPanel::slotItemDeleted(const TQString& name) {
	view->delItem(name);
}

void ListPanel::slotItemUpdated(vfile *vf) {
	view->updateItem(vf);
}

void ListPanel::slotCleared() {
	view->clear();
}

void ListPanel::showEvent( TQShowEvent *e ) {
  panelActive();
  TQWidget::showEvent(e);
}

void ListPanel::hideEvent( TQHideEvent *e ) {
  panelInactive();
  TQWidget::hideEvent(e);
}

void ListPanel::panelActive() {
	// don't refresh when not active (ie: hidden, application isn't focussed ...)
	func->files()->vfs_enableRefresh(true);
}

void ListPanel::panelInactive() {
	func->files()->vfs_enableRefresh(false);
}

void ListPanel::slotJobStarted(TDEIO::Job* job) {
	// disable the parts of the panel we don't want touched
	//static_cast<KrDetailedView*>(view)->setEnabled(false);
	status->setEnabled(false);
	origin->setEnabled(false);
	cdRootButton->setEnabled(false);
   cdHomeButton->setEnabled(false);
   cdUpButton->setEnabled(false);
   cdOtherButton->setEnabled(false);
	popupBtn->setEnabled(false);
	popup->setEnabled(false);
   bookmarksButton->setEnabled(false);
   historyButton->setEnabled(false);
   syncBrowseButton->setEnabled(false);

	// connect to the job interface to provide in-panel refresh notification
	connect( job, TQ_SIGNAL( infoMessage( TDEIO::Job*, const TQString & ) ),
		TQ_SLOT( inlineRefreshInfoMessage( TDEIO::Job*, const TQString & ) ) );
	connect( job, TQ_SIGNAL( percent( TDEIO::Job*, unsigned long ) ),
      TQ_SLOT( inlineRefreshPercent( TDEIO::Job*, unsigned long ) ) );		
	connect(job,TQ_SIGNAL(result(TDEIO::Job*)),
         this,TQ_SLOT(inlineRefreshListResult(TDEIO::Job*)));
	connect(job,TQ_SIGNAL(canceled(TDEIO::Job*)),
         this,TQ_SLOT(inlineRefreshListResult(TDEIO::Job*)));
			
	inlineRefreshJob = job;
	
	totals->setText(i18n(">> Reading..."));
	inlineRefreshCancelButton->show();
}

void ListPanel::inlineRefreshCancel() {
	if (inlineRefreshJob) {
		inlineRefreshJob->kill(false);
		inlineRefreshJob = 0;
	}
}

void ListPanel::inlineRefreshPercent( TDEIO::Job*, unsigned long perc) {
	TQString msg = TQString(">> %1: %2 % complete...").arg(i18n("Reading")).arg(perc);
	totals->setText(msg);
}

void ListPanel::inlineRefreshInfoMessage( TDEIO::Job*, const TQString &msg ) {
	totals->setText(">> " + i18n("Reading: ") + msg);
}

void ListPanel::inlineRefreshListResult(TDEIO::Job*) {
	inlineRefreshJob = 0;
	// reenable everything
	//static_cast<KrDetailedView*>(view)->setEnabled(true);
	status->setEnabled(true);
	origin->setEnabled(true);
	cdRootButton->setEnabled(true);
   cdHomeButton->setEnabled(true);
   cdUpButton->setEnabled(true);
   cdOtherButton->setEnabled(true);
	popupBtn->setEnabled(true);
	popup->setEnabled(true);
   bookmarksButton->setEnabled(true);
   historyButton->setEnabled(true);
   syncBrowseButton->setEnabled(true);
	
	inlineRefreshCancelButton->hide();
}

void ListPanel::jumpBack() {
	func->openUrl( _jumpBackURL );
}

void ListPanel::setJumpBack( KURL url ) {
	_jumpBackURL = url;
}

#include "listpanel.moc"
