/* This file is based on tdefiledetailview.cpp v1.43 from the KDE libs. Original
   copyright follows.
*/
/* This file is part of the KDE libraries
   Copyright (C) 1997 Stephan Kulow <coolo@kde.org>
                 2000, 2001 Carsten Pfeiffer <pfeiffer@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.
*/

// TQt
#include <tqbitmap.h>
#include <tqevent.h>
#include <tqheader.h>
#include <tqkeycode.h>
#include <tqpainter.h>
#include <tqpixmap.h>

// KDE 
#include <tdeapplication.h>
#include <kdebug.h>
#include <tdefileitem.h>
#include <tdeglobalsettings.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <kurldrag.h>
#include <kwordwrap.h>

// Local
#include "archive.h"
#include "dragpixmapgenerator.h"
#include "filedetailviewitem.h"
#include "filedetailview.moc"
#include "timeutils.h"
namespace Gwenview {


static TQPixmap createShownItemPixmap(int size, const TQColor& color) {
	TQPixmap pix(size, size);
	pix.fill(TQt::red);
	TQPainter painter(&pix);
	int margin = 2;
	
	TQPointArray pa(3);
	int arrowSize = size/2 - margin;
	int center = size/2 - 1;
	pa[0] = TQPoint((size - arrowSize) / 2, center - arrowSize);
	pa[1] = TQPoint((size + arrowSize) / 2, center);
	pa[2] = TQPoint(pa[0].x(),              center + arrowSize);

	painter.setBrush(color);
	painter.setPen(color);
	painter.drawPolygon(pa);
	painter.end();

	pix.setMask(pix.createHeuristicMask());
	
	return pix;
}


FileDetailView::FileDetailView(TQWidget *parent, const char *name)
	: TDEListView(parent, name), FileViewBase()
{
	mSortingCol = COL_NAME;
	mBlockSortingSignal = false;

	addColumn( i18n( "Name" ) );
	addColumn( i18n( "Size" ) );
	addColumn( i18n( "Date" ) );
	addColumn( i18n( "Permissions" ) );
	addColumn( i18n( "Owner" ) );
	addColumn( i18n( "Group" ) );
	setShowSortIndicator( true );
	setAllColumnsShowFocus( true );

	connect( header(), TQ_SIGNAL( sectionClicked(int)),
			 TQ_SLOT(slotSortingChanged(int) ));


	connect( this, TQ_SIGNAL( returnPressed(TQListViewItem *) ),
			 TQ_SLOT( slotActivate( TQListViewItem *) ) );

	connect( this, TQ_SIGNAL( clicked(TQListViewItem *, const TQPoint&, int)),
			 TQ_SLOT( selected( TQListViewItem *) ) );
	connect( this, TQ_SIGNAL( doubleClicked(TQListViewItem *, const TQPoint&, int)),
			 TQ_SLOT( slotActivate( TQListViewItem *) ) );

	connect( this, TQ_SIGNAL(contextMenuRequested( TQListViewItem *,
												const TQPoint &, int )),
			 this, TQ_SLOT( slotActivateMenu( TQListViewItem *, const TQPoint& )));

	TQListView::setSelectionMode( TQListView::Extended );
	connect( this, TQ_SIGNAL( selectionChanged() ),
			 TQ_SLOT( slotSelectionChanged() ));

	// FileViewStack need to be aware of sort changes, to update the sort menu
	connect( sig, TQ_SIGNAL(sortingChanged(TQDir::SortSpec)),
		this, TQ_SIGNAL(sortingChanged(TQDir::SortSpec)) );
	
	setSorting( sorting() );


	mResolver =
		new KMimeTypeResolver<FileDetailViewItem,FileDetailView>( this );

	setDragEnabled(true);
	
	setAcceptDrops(true);
	setDropVisualizer(false);
	setDropHighlighter(false);

	int size = IconSize(TDEIcon::Small);
	mShownItemUnselectedPixmap = createShownItemPixmap(size, colorGroup().highlight());
	mShownItemSelectedPixmap = createShownItemPixmap(size, colorGroup().highlightedText());
}


FileDetailView::~FileDetailView()
{
	delete mResolver;
}


void FileDetailView::setSelected( const KFileItem *info, bool enable )
{
	if (!info) return;
	FileDetailViewItem *item = viewItem(info);
	if (item) TDEListView::setSelected(item, enable);
}

void FileDetailView::setCurrentItem( const KFileItem *item )
{
	if (!item) return;
	FileDetailViewItem *listItem = viewItem(item);
	if (listItem) TDEListView::setCurrentItem(listItem);
}

KFileItem * FileDetailView::currentFileItem() const
{
	FileDetailViewItem *current = static_cast<FileDetailViewItem*>( currentItem() );
	if ( current ) return current->fileInfo();

	return 0L;
}

void FileDetailView::clearSelection()
{
	TDEListView::clearSelection();
}

void FileDetailView::selectAll()
{
	TDEListView::selectAll( true );
}

void FileDetailView::invertSelection()
{
	TDEListView::invertSelection();
}

void FileDetailView::slotActivateMenu (TQListViewItem *item,const TQPoint& pos )
{
	if ( !item ) {
		sig->activateMenu( 0, pos );
		return;
	}
	FileDetailViewItem *i = (FileDetailViewItem*) item;
	sig->activateMenu( i->fileInfo(), pos );
}

void FileDetailView::clearView()
{
	mResolver->m_lstPendingMimeIconItems.clear();
	mShownFileItem=0L;
	TDEListView::clear();
}

void FileDetailView::insertItem( KFileItem *i )
{
	KFileView::insertItem( i );

	FileDetailViewItem *item = new FileDetailViewItem( (TQListView*) this, i );

	setSortingKey( item, i );

	i->setExtraData( this, item );

	if ( !i->isMimeTypeKnown() )
		mResolver->m_lstPendingMimeIconItems.append( item );
}

void FileDetailView::slotActivate( TQListViewItem *item )
{
	if ( !item ) return;

	const KFileItem *fi = ( (FileDetailViewItem*)item )->fileInfo();
	if ( fi ) sig->activate( fi );
}

void FileDetailView::selected( TQListViewItem *item )
{
	if ( !item ) return;

	if ( TDEGlobalSettings::singleClick() ) {
		const KFileItem *fi = ( (FileDetailViewItem*)item )->fileInfo();
		if ( fi && (fi->isDir() || !onlyDoubleClickSelectsFiles()) )
			sig->activate( fi );
	}
}

void FileDetailView::highlighted( TQListViewItem *item )
{
	if ( !item ) return;

	const KFileItem *fi = ( (FileDetailViewItem*)item )->fileInfo();
	if ( fi ) sig->highlightFile( fi );
}


bool FileDetailView::isSelected(const KFileItem* fileItem) const
{
	if (!fileItem) return false;

	FileDetailViewItem *item = viewItem(fileItem);
	return item && item->isSelected();
}


void FileDetailView::updateView( bool b )
{
	if ( !b ) return;

	TQListViewItemIterator it( (TQListView*)this );
	for ( ; it.current(); ++it ) {
		FileDetailViewItem *item=static_cast<FileDetailViewItem *>(it.current());
		item->setPixmap( 0, item->fileInfo()->pixmap(TDEIcon::SizeSmall) );
	}
}

void FileDetailView::updateView( const KFileItem *i, bool /*metaDataOnly*/ )
{
	if ( !i ) return;

	FileDetailViewItem *item = viewItem(i);
	if ( !item ) return;

	item->init();
	setSortingKey( item, i );
}


void FileDetailView::setSortingKey( FileDetailViewItem *dvItem, const KFileItem *item)
{
	TQDir::SortSpec spec = KFileView::sorting();
	bool isDirOrArchive=item->isDir() || Archive::fileItemIsArchive(item);

	TQString key;
	if ( spec & TQDir::Time ) {
		time_t time = TimeUtils::getTime(item);
		key=sortingKey(time, isDirOrArchive, spec);
		
	} else if ( spec & TQDir::Size ) {
		key=sortingKey( item->size(), isDirOrArchive, spec );
		
	} else {
		// Name or Unsorted
		key=sortingKey( item->text(), isDirOrArchive, spec );
	}

	dvItem->setKey(key);
}


void FileDetailView::removeItem( const KFileItem *i )
{
	if ( !i ) return;

	FileDetailViewItem *item = viewItem(i);
	mResolver->m_lstPendingMimeIconItems.remove( item );
	if(mShownFileItem==i) mShownFileItem=0L;
	delete item;

	KFileView::removeItem( i );
}

void FileDetailView::slotSortingChanged( int col )
{
	TQDir::SortSpec sort = sorting();
	int sortSpec = -1;
	bool reversed = col == mSortingCol && (sort & TQDir::Reversed) == 0;
	mSortingCol = col;

	switch( col ) {
	case COL_NAME:
		sortSpec = ((sort & ~TQDir::SortByMask) | TQDir::Name);
		break;
	case COL_SIZE:
		sortSpec = ((sort & ~TQDir::SortByMask) | TQDir::Size);
		break;
	case COL_DATE:
		sortSpec = ((sort & ~TQDir::SortByMask) | TQDir::Time);
		break;

	// the following columns have no equivalent in TQDir, so we set it
	// to TQDir::Unsorted and remember the column (mSortingCol)
	case COL_OWNER:
	case COL_GROUP:
	case COL_PERM:
		// grmbl, TQDir::Unsorted == SortByMask.
		sortSpec = (sort & ~TQDir::SortByMask);// | TQDir::Unsorted;
		break;
	default:
		break;
	}

	if ( reversed )
		sortSpec |= TQDir::Reversed;
	else
		sortSpec &= ~TQDir::Reversed;

	if ( sort & TQDir::IgnoreCase )
		sortSpec |= TQDir::IgnoreCase;
	else
		sortSpec &= ~TQDir::IgnoreCase;


	KFileView::setSorting( static_cast<TQDir::SortSpec>( sortSpec ) );

	KFileItem *item;
	KFileItemListIterator it( *items() );

	for ( ; (item = it.current() ); ++it ) {
		FileDetailViewItem* thumbItem=viewItem( item );
		if (thumbItem) setSortingKey(thumbItem,item);
	}

	TDEListView::setSorting( mSortingCol, !reversed );
	TDEListView::sort();

	if (!mBlockSortingSignal) sig->changeSorting( static_cast<TQDir::SortSpec>( sortSpec ) );
}


void FileDetailView::setSorting( TQDir::SortSpec spec )
{
	int col = 0;
	if ( spec & TQDir::Time )
		col = COL_DATE;
	else if ( spec & TQDir::Size )
		col = COL_SIZE;
	else if ( spec & TQDir::Unsorted )
		col = mSortingCol;
	else
		col = COL_NAME;

	// inversed, because slotSortingChanged will reverse it
	if ( spec & TQDir::Reversed )
		spec = (TQDir::SortSpec) (spec & ~TQDir::Reversed);
	else
		spec = (TQDir::SortSpec) (spec | TQDir::Reversed);

	mSortingCol = col;
	KFileView::setSorting( (TQDir::SortSpec) spec );


	// don't emit sortingChanged() when called via setSorting()
	mBlockSortingSignal = true; // can't use blockSignals()
	slotSortingChanged( col );
	mBlockSortingSignal = false;
}

void FileDetailView::ensureItemVisible( const KFileItem *i )
{
	if ( !i ) return;

	FileDetailViewItem *item = viewItem(i);
		
	if ( item ) TDEListView::ensureItemVisible( item );
}

// we're in multiselection mode
void FileDetailView::slotSelectionChanged()
{
	sig->highlightFile( 0L );
}

KFileItem * FileDetailView::firstFileItem() const
{
	FileDetailViewItem *item = static_cast<FileDetailViewItem*>( firstChild() );
	if ( item ) return item->fileInfo();
	return 0L;
}

KFileItem * FileDetailView::nextItem( const KFileItem *fileItem ) const
{
	if ( fileItem ) {
		FileDetailViewItem *item = viewItem( fileItem );
		if ( item && item->itemBelow() )
			return ((FileDetailViewItem*) item->itemBelow())->fileInfo();
		else
			return 0L;
	}
	else
		return firstFileItem();
}

KFileItem * FileDetailView::prevItem( const KFileItem *fileItem ) const
{
	if ( fileItem ) {
		FileDetailViewItem *item = viewItem( fileItem );
		if ( item && item->itemAbove() )
			return ((FileDetailViewItem*) item->itemAbove())->fileInfo();
		else
			return 0L;
	}
	else
		return firstFileItem();
}

void FileDetailView::keyPressEvent( TQKeyEvent *e )
{
	TDEListView::keyPressEvent( e );

	if ( e->key() == Key_Return || e->key() == Key_Enter ) {
		if ( e->state() & ControlButton )
			e->ignore();
		else
			e->accept();
	}
}

//
// mimetype determination on demand
//
void FileDetailView::mimeTypeDeterminationFinished()
{
	// anything to do?
}

void FileDetailView::determineIcon( FileDetailViewItem *item )
{
	(void) item->fileInfo()->determineMimeType();
	updateView( item->fileInfo() );
}

void FileDetailView::listingCompleted()
{
	mResolver->start();
}

void FileDetailView::startDrag()
{
	/**
	 * The item drawer for DragPixmapGenerator
	 */
	struct ItemDrawer : public DragPixmapItemDrawer<KFileItem*> {
		ItemDrawer(const TQFontMetrics& fontMetrics)
		: mFontMetrics(fontMetrics) {}

		TQSize itemSize(KFileItem* fileItem) {
			if (!fileItem) return TQSize();
			TQString name = fileItem->name();
			int width = TQMIN(mGenerator->maxWidth(), mFontMetrics.width(name));
			int height = mFontMetrics.height();
			return TQSize(width, height);
		}
				
		void drawItem(TQPainter* painter, int left, int top, KFileItem* fileItem) {
			TQString name = fileItem->name();
			painter->save();
			KWordWrap::drawFadeoutText(painter, 
				left, top + mFontMetrics.ascent(), 
				mGenerator->maxWidth(), name);
			painter->restore();
		}
		
		TQFontMetrics mFontMetrics;
	};
	ItemDrawer drawer(fontMetrics());


	KURL::List urls;
	KFileItemListIterator it(*KFileView::selectedItems());

	DragPixmapGenerator<KFileItem*> generator;
	generator.setItemDrawer(&drawer);
	
	for ( ; it.current(); ++it ) {
		urls.append(it.current()->url());
		generator.addItem(it.current());
	}

	if (urls.isEmpty()) {
		kdWarning() << "No item to drag\n";
		return;
	}

	TQDragObject* drag=new KURLDrag(urls, this, 0);
	TQPixmap dragPixmap = generator.generate();

	drag->setPixmap( dragPixmap, TQPoint(-generator.DRAG_OFFSET, -generator.DRAG_OFFSET));
	drag->dragCopy();
}


void FileDetailView::setShownFileItem(KFileItem* fileItem)
{
	if( fileItem == mShownFileItem ) return;
	FileDetailViewItem* oldShownItem=viewItem(mShownFileItem);
	FileDetailViewItem* newShownItem=viewItem(fileItem);
	
	FileViewBase::setShownFileItem(fileItem);
	if (oldShownItem) oldShownItem->repaint();
	if (newShownItem) newShownItem->repaint();
}


//----------------------------------------------------------------------
//
// Drop support
//
//----------------------------------------------------------------------
bool FileDetailView::acceptDrag(TQDropEvent* event) const {
	return KURLDrag::canDecode(event);
}

void FileDetailView::contentsDropEvent(TQDropEvent *event) {
	KFileItem* fileItem=0L;
	TQListViewItem *item=itemAt(contentsToViewport(event->pos() ) );
	
	if (item) {
		fileItem=static_cast<FileDetailViewItem*>(item)->fileInfo();
	}
	emit dropped(event,fileItem);
}

} // namespace
