diff options
Diffstat (limited to 'src/gvcore/filethumbnailview.cpp')
-rw-r--r-- | src/gvcore/filethumbnailview.cpp | 866 |
1 files changed, 866 insertions, 0 deletions
diff --git a/src/gvcore/filethumbnailview.cpp b/src/gvcore/filethumbnailview.cpp new file mode 100644 index 0000000..d9d3ca2 --- /dev/null +++ b/src/gvcore/filethumbnailview.cpp @@ -0,0 +1,866 @@ +// vim: set tabstop=4 shiftwidth=4 noexpandtab: +/* +Gwenview - A simple image viewer for KDE +Copyright 2000-2004 Aurélien Gâteau + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +// Qt +#include <qframe.h> +#include <qlayout.h> +#include <qpainter.h> +#include <qpen.h> +#include <qpixmap.h> +#include <qpushbutton.h> +#include <qtimer.h> +#include <qvaluevector.h> + +// KDE +#include <kapplication.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kglobalsettings.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kprogress.h> +#include <kstandarddirs.h> +#include <kurldrag.h> +#include <kwordwrap.h> + +// Local +#include "fileviewconfig.h" +#include "filethumbnailviewitem.h" +#include "archive.h" +#include "dragpixmapgenerator.h" +#include "thumbnailloadjob.h" +#include "busylevelmanager.h" +#include "imageloader.h" +#include "timeutils.h" +#include "thumbnailsize.h" +#include "thumbnaildetailsdialog.h" + +#undef ENABLE_LOG +#undef LOG +#define ENABLE_LOG +#ifdef ENABLE_LOG +#define LOG(x) kdDebug() << k_funcinfo << x << endl +#else +#define LOG(x) ; +#endif + +#include "filethumbnailview.moc" +namespace Gwenview { + +static const int THUMBNAIL_UPDATE_DELAY=500; + +static const int RIGHT_TEXT_WIDTH=128; +static const int BOTTOM_MIN_TEXT_WIDTH=96; + +class ProgressWidget : public QFrame { + KProgress* mProgressBar; + QPushButton* mStop; +public: + ProgressWidget(FileThumbnailView* view, int count) + : QFrame(view) + { + QHBoxLayout* layout=new QHBoxLayout(this, 3, 3); + layout->setAutoAdd(true); + setFrameStyle( QFrame::StyledPanel | QFrame::Raised ); + + mStop=new QPushButton(this); + mStop->setPixmap(SmallIcon("stop")); + mStop->setFlat(true); + + mProgressBar=new KProgress(count, this); + mProgressBar->setFormat("%v/%m"); + + view->clipper()->installEventFilter(this); + } + + void polish() { + QFrame::polish(); + setMinimumWidth(layout()->minimumSize().width()); + //setFixedHeight( mProgressBar->height() ); + setFixedHeight( mStop->height() ); + } + + void showEvent(QShowEvent*) { + updatePosition(); + } + + bool eventFilter(QObject*, QEvent* event) { + if (event->type()==QEvent::Resize) { + updatePosition(); + } + return false; + } + + void updatePosition() { + FileThumbnailView* view=static_cast<FileThumbnailView*>(parent()); + QSize tmp=view->clipper()->size() - size(); + move(tmp.width() - 2, tmp.height() - 2); + } + + KProgress* progressBar() const { return mProgressBar; } + QPushButton* stopButton() const { return mStop; } +}; + + +struct FileThumbnailView::Private { + int mThumbnailSize; + int mMarginSize; + bool mUpdateThumbnailsOnNextShow; + QPixmap mWaitPixmap; // The wait pixmap (32 x 32) + QPixmap mWaitThumbnail; // The wait thumbnail (mThumbnailSize x mThumbnailSize) + ProgressWidget* mProgressWidget; + + QGuardedPtr<ThumbnailLoadJob> mThumbnailLoadJob; + + QTimer* mThumbnailUpdateTimer; + + int mItemDetails; + + ImageLoader* mPrefetch; + ThumbnailDetailsDialog* mThumbnailsDetailDialog; + + void updateWaitThumbnail(const FileThumbnailView* view) { + mWaitThumbnail=QPixmap(mThumbnailSize, mThumbnailSize); + mWaitThumbnail.fill(view->paletteBackgroundColor()); + QPainter painter(&mWaitThumbnail); + + painter.setPen(view->colorGroup().button()); + painter.drawRect(0,0,mThumbnailSize,mThumbnailSize); + painter.drawPixmap( + (mThumbnailSize-mWaitPixmap.width())/2, + (mThumbnailSize-mWaitPixmap.height())/2, + mWaitPixmap); + painter.end(); + } +}; + + +static FileThumbnailViewItem* viewItem(const FileThumbnailView* view, const KFileItem* fileItem) { + if (!fileItem) return 0L; + return static_cast<FileThumbnailViewItem*>( const_cast<void*>(fileItem->extraData(view) ) ); +} + + +FileThumbnailView::FileThumbnailView(QWidget* parent) +: KIconView(parent), FileViewBase() +{ + d=new Private; + d->mUpdateThumbnailsOnNextShow=false; + d->mThumbnailLoadJob=0L; + d->mWaitPixmap=QPixmap(::locate("appdata", "thumbnail/wait.png")); + d->mProgressWidget=0L; + d->mThumbnailUpdateTimer=new QTimer(this); + d->mMarginSize=FileViewConfig::thumbnailMarginSize(); + d->mItemDetails=FileViewConfig::thumbnailDetails(); + d->mPrefetch = NULL; + d->mThumbnailSize = 0; + d->mThumbnailsDetailDialog = 0; + + setItemTextPos( QIconView::ItemTextPos(FileViewConfig::thumbnailTextPos()) ); + setAutoArrange(true); + QIconView::setSorting(true); + setItemsMovable(false); + setResizeMode(Adjust); + setShowToolTips(true); + setSpacing(0); + setAcceptDrops(true); + + // We can't use KIconView::Execute mode because in this mode the current + // item is unselected after being clicked, so we use KIconView::Select mode + // and emit the execute() signal with slotClicked() ourself. + setMode(KIconView::Select); + connect(this, SIGNAL(clicked(QIconViewItem*)), + this, SLOT(slotClicked(QIconViewItem*)) ); + connect(this, SIGNAL(doubleClicked(QIconViewItem*)), + this, SLOT(slotDoubleClicked(QIconViewItem*)) ); + + connect(this, SIGNAL(dropped(QDropEvent*,const QValueList<QIconDragItem>&)), + this, SLOT(slotDropped(QDropEvent*)) ); + connect(this, SIGNAL( contentsMoving( int, int )), + this, SLOT( slotContentsMoving( int, int ))); + connect(this, SIGNAL(currentChanged(QIconViewItem*)), + this, SLOT(slotCurrentChanged(QIconViewItem*)) ); + + QIconView::setSelectionMode(Extended); + + connect(BusyLevelManager::instance(), SIGNAL(busyLevelChanged(BusyLevel)), + this, SLOT( slotBusyLevelChanged(BusyLevel))); + + connect(d->mThumbnailUpdateTimer, SIGNAL(timeout()), + this, SLOT( startThumbnailUpdate()) ); +} + + +FileThumbnailView::~FileThumbnailView() { + stopThumbnailUpdate(); + FileViewConfig::setThumbnailDetails(d->mItemDetails); + FileViewConfig::setThumbnailTextPos( int(itemTextPos()) ); + FileViewConfig::writeConfig(); + delete d; +} + + +void FileThumbnailView::setThumbnailSize(int value) { + if (value==d->mThumbnailSize) return; + d->mThumbnailSize=value; + updateGrid(); + + KFileItemListIterator it( *items() ); + for ( ; it.current(); ++it ) { + KFileItem *item=it.current(); + QPixmap pixmap=createItemPixmap(item); + QIconViewItem* iconItem=viewItem(this, item); + if (iconItem) iconItem->setPixmap(pixmap); + } + arrangeItemsInGrid(); + d->mThumbnailUpdateTimer->start(THUMBNAIL_UPDATE_DELAY, true); +} + + +int FileThumbnailView::thumbnailSize() const { + return d->mThumbnailSize; +} + + +/** + * Overriden to call updateGrid + */ +void FileThumbnailView::setItemTextPos(ItemTextPos pos) { + QIconView::setItemTextPos(pos); + updateGrid(); +} + + +void FileThumbnailView::setMarginSize(int value) { + if (value==d->mMarginSize) return; + d->mMarginSize=value; + updateGrid(); +} + + +int FileThumbnailView::marginSize() const { + return d->mMarginSize; +} + + +void FileThumbnailView::setItemDetails(int details) { + d->mItemDetails=details; + for (QIconViewItem* item=firstItem(); item; item=item->nextItem()) { + static_cast<FileThumbnailViewItem*>(item)->updateLines(); + } + arrangeItemsInGrid(); +} + + +int FileThumbnailView::itemDetails() const { + return d->mItemDetails; +} + + +void FileThumbnailView::setThumbnailPixmap(const KFileItem* fileItem, const QPixmap& thumbnail, const QSize& size) { + FileThumbnailViewItem* iconItem=viewItem(this, fileItem); + if (!iconItem) return; + + iconItem->setPixmap(thumbnail); + + // Update item info + if (size.isValid()) { + iconItem->setImageSize(size); + } + iconItem->repaint(); + + // Notify progress + if (d->mProgressWidget) { + // mProgressWidget might be null if we get called after the thumbnail + // job finished. This can happen when the thumbnail job use KPreviewJob + // to generate a thumbnail. + d->mProgressWidget->progressBar()->advance(1); + } +} + + + + +void FileThumbnailView::setShownFileItem(KFileItem* fileItem) { + if( fileItem == mShownFileItem ) return; + FileThumbnailViewItem* oldShownItem=viewItem(this, mShownFileItem); + FileThumbnailViewItem* newShownItem=viewItem(this, fileItem); + + FileViewBase::setShownFileItem(fileItem); + if (oldShownItem) repaintItem(oldShownItem); + if (newShownItem) repaintItem(newShownItem); +} + + +//----------------------------------------------------------------------------- +// +// Thumbnail code +// +//----------------------------------------------------------------------------- +QPixmap FileThumbnailView::createItemPixmap(const KFileItem* item) const { + bool isDirOrArchive=item->isDir() || Archive::fileItemIsArchive(item); + if (!isDirOrArchive) { + if (d->mWaitThumbnail.width()!=d->mThumbnailSize) { + d->updateWaitThumbnail(this); + } + return d->mWaitThumbnail; + } + + QPixmap thumbnail(d->mThumbnailSize, d->mThumbnailSize); + thumbnail.fill(paletteBackgroundColor()); + QPainter painter(&thumbnail); + + // Load the icon + QPixmap itemPix=item->pixmap(QMIN(d->mThumbnailSize, ThumbnailSize::NORMAL)); + painter.drawPixmap( + (d->mThumbnailSize-itemPix.width())/2, + (d->mThumbnailSize-itemPix.height())/2, + itemPix); + + return thumbnail; +} + + +void FileThumbnailView::startThumbnailUpdate() { + // Delay thumbnail update if the widget is not visible + if (!isVisible()) { + d->mUpdateThumbnailsOnNextShow=true; + return; + } + d->mUpdateThumbnailsOnNextShow=false; + stopThumbnailUpdate(); // just in case + doStartThumbnailUpdate(items()); +} + + +void FileThumbnailView::doStartThumbnailUpdate(const KFileItemList* list) { + QValueVector<const KFileItem*> imageList; + imageList.reserve( list->count()); + QPtrListIterator<KFileItem> it(*list); + for (;it.current(); ++it) { + KFileItem* item=it.current(); + if (!item->isDir() && !Archive::fileItemIsArchive(item)) { + imageList.append( item ); + } + } + if (imageList.empty()) return; + + BusyLevelManager::instance()->setBusyLevel( this, BUSY_THUMBNAILS ); + + Q_ASSERT(!d->mProgressWidget); + d->mProgressWidget=new ProgressWidget(this, imageList.count() ); + + connect(d->mProgressWidget->stopButton(), SIGNAL(clicked()), + this, SLOT(stopThumbnailUpdate()) ); + d->mProgressWidget->show(); + + d->mThumbnailLoadJob = new ThumbnailLoadJob(&imageList, d->mThumbnailSize); + + connect(d->mThumbnailLoadJob, SIGNAL(thumbnailLoaded(const KFileItem*, const QPixmap&, const QSize&)), + this, SLOT(setThumbnailPixmap(const KFileItem*,const QPixmap&, const QSize&)) ); + connect(d->mThumbnailLoadJob, SIGNAL(result(KIO::Job*)), + this, SLOT(slotUpdateEnded()) ); + + slotBusyLevelChanged( BusyLevelManager::instance()->busyLevel()); + // start updating at visible position + slotContentsMoving( contentsX(), contentsY()); + d->mThumbnailLoadJob->start(); +} + + +void FileThumbnailView::stopThumbnailUpdate() { + if (!d->mThumbnailLoadJob.isNull()) { + d->mThumbnailLoadJob->kill(false); + } +} + + +void FileThumbnailView::slotUpdateEnded() { + Q_ASSERT(d->mProgressWidget); + delete d->mProgressWidget; + d->mProgressWidget=0L; + + BusyLevelManager::instance()->setBusyLevel( this, BUSY_NONE ); +} + + +void FileThumbnailView::updateThumbnail(const KFileItem* fileItem) { + if (fileItem->isDir() || Archive::fileItemIsArchive(fileItem)) { + return; + } + + ThumbnailLoadJob::deleteImageThumbnail(fileItem->url()); + if (d->mThumbnailLoadJob.isNull()) { + KFileItemList list; + list.append(fileItem); + doStartThumbnailUpdate(&list); + } else { + d->mThumbnailLoadJob->appendItem(fileItem); + } +} + +// temporarily stop loading thumbnails when busy loading the selected image, +// otherwise thumbnail loading slows it down +void FileThumbnailView::slotBusyLevelChanged(BusyLevel level) { + if( !d->mThumbnailLoadJob.isNull()) { + if( level > BUSY_THUMBNAILS ) { + d->mThumbnailLoadJob->suspend(); + } else { + d->mThumbnailLoadJob->resume(); + } + } +} + +//----------------------------------------------------------------------------- +// +// KFileView methods +// +//----------------------------------------------------------------------------- +void FileThumbnailView::clearView() { + stopThumbnailUpdate(); + mShownFileItem=0L; + QIconView::clear(); +} + + +void FileThumbnailView::insertItem(KFileItem* item) { + if (!item) return; + bool isDirOrArchive=item->isDir() || Archive::fileItemIsArchive(item); + + QPixmap thumbnail=createItemPixmap(item); + FileThumbnailViewItem* iconItem=new FileThumbnailViewItem(this,item->text(),thumbnail,item); + iconItem->setDropEnabled(isDirOrArchive); + + setSortingKey(iconItem, item); + item->setExtraData(this,iconItem); +} + + +void FileThumbnailView::updateView(const KFileItem* fileItem) { + if (!fileItem) return; + + FileThumbnailViewItem* iconItem=viewItem(this, fileItem); + if (iconItem) { + iconItem->setText(fileItem->text()); + updateThumbnail(fileItem); + } + sort(); +} + + +void FileThumbnailView::ensureItemVisible(const KFileItem* fileItem) { + if (!fileItem) return; + + FileThumbnailViewItem* iconItem=viewItem(this, fileItem); + if (iconItem) QIconView::ensureItemVisible(iconItem); +} + + +void FileThumbnailView::setCurrentItem(const KFileItem* fileItem) { + if (!fileItem) return; + + FileThumbnailViewItem* iconItem=viewItem(this, fileItem); + if (iconItem) QIconView::setCurrentItem(iconItem); +} + + +void FileThumbnailView::setSelected(const KFileItem* fileItem,bool enable) { + if (!fileItem) return; + + FileThumbnailViewItem* iconItem=viewItem(this, fileItem); + if (iconItem) QIconView::setSelected(iconItem, enable, true /* do not unselect others */); +} + + +bool FileThumbnailView::isSelected(const KFileItem* fileItem) const { + if (!fileItem) return false; + + FileThumbnailViewItem* iconItem=viewItem(this, fileItem); + if (!iconItem) return false; + + return iconItem->isSelected(); +} + + +void FileThumbnailView::removeItem(const KFileItem* fileItem) { + if (!fileItem) return; + + // Remove it from the image preview job + if (!d->mThumbnailLoadJob.isNull()) + d->mThumbnailLoadJob->itemRemoved(fileItem); + + if (fileItem==mShownFileItem) mShownFileItem=0L; + + // Remove it from our view + FileThumbnailViewItem* iconItem=viewItem(this, fileItem); + if (iconItem) delete iconItem; + KFileView::removeItem(fileItem); + arrangeItemsInGrid(); +} + + +KFileItem* FileThumbnailView::firstFileItem() const { + FileThumbnailViewItem* iconItem=static_cast<FileThumbnailViewItem*>(firstItem()); + if (!iconItem) return 0L; + return iconItem->fileItem(); +} + + +KFileItem* FileThumbnailView::prevItem(const KFileItem* fileItem) const { + const FileThumbnailViewItem* iconItem=viewItem(this, fileItem); + if (!iconItem) return 0L; + + iconItem=static_cast<const FileThumbnailViewItem*>(iconItem->prevItem()); + if (!iconItem) return 0L; + + return iconItem->fileItem(); +} + + +KFileItem* FileThumbnailView::currentFileItem() const { + const QIconViewItem* iconItem=currentItem(); + if (!iconItem) return 0L; + + return static_cast<const FileThumbnailViewItem*>(iconItem)->fileItem(); +} + + +KFileItem* FileThumbnailView::nextItem(const KFileItem* fileItem) const { + const FileThumbnailViewItem* iconItem=viewItem(this, fileItem); + if (!iconItem) return 0L; + + iconItem=static_cast<const FileThumbnailViewItem*>(iconItem->nextItem()); + if (!iconItem) return 0L; + + return iconItem->fileItem(); +} + + +void FileThumbnailView::setSorting(QDir::SortSpec spec) { + KFileView::setSorting(spec); + + KFileItem *item; + KFileItemListIterator it( *items() ); + + for ( ; (item = it.current() ); ++it ) { + QIconViewItem* iconItem=viewItem(this, item); + if (iconItem) setSortingKey(iconItem, item); + } + + KIconView::sort(! (spec & QDir::Reversed) ); +} + +//-------------------------------------------------------------------------- +// +// Drop support +// +//-------------------------------------------------------------------------- +void FileThumbnailView::contentsDragEnterEvent(QDragEnterEvent* event) { + return event->accept( KURLDrag::canDecode(event) ); +} + + +void FileThumbnailView::slotDropped(QDropEvent* event) { + emit dropped(event,0L); +} + + +void FileThumbnailView::showEvent(QShowEvent* event) { + KIconView::showEvent(event); + if (!d->mUpdateThumbnailsOnNextShow) return; + + d->mUpdateThumbnailsOnNextShow=false; + QTimer::singleShot(0, this, SLOT(startThumbnailUpdate())); +} + + +//-------------------------------------------------------------------------- +// +// Private +// +//-------------------------------------------------------------------------- +void FileThumbnailView::updateGrid() { + if (itemTextPos()==Right) { + setGridX( + d->mThumbnailSize + + FileThumbnailViewItem::PADDING*3 + + RIGHT_TEXT_WIDTH); + } else { + setGridX( + QMAX(d->mThumbnailSize, BOTTOM_MIN_TEXT_WIDTH) + + FileThumbnailViewItem::PADDING*2); + } + setSpacing(d->mMarginSize); +} + + +void FileThumbnailView::setSortingKey(QIconViewItem *iconItem, const KFileItem *item) +{ + // see also setSorting() + QDir::SortSpec spec = KFileView::sorting(); + bool isDirOrArchive=item->isDir() || Archive::fileItemIsArchive(item); + + QString key; + if ( spec & QDir::Time ) { + time_t time = TimeUtils::getTime(item); + key=sortingKey(time, isDirOrArchive, spec); + + } else if ( spec & QDir::Size ) { + key=sortingKey( item->size(), isDirOrArchive, spec ); + + } else { + // Name or Unsorted + key=sortingKey( item->text(), isDirOrArchive, spec ); + } + + iconItem->setKey(key); +} + + +//-------------------------------------------------------------------------- +// +// Private slots +// +//-------------------------------------------------------------------------- +void FileThumbnailView::slotDoubleClicked(QIconViewItem* iconItem) { + if (!iconItem) return; + if (KGlobalSettings::singleClick()) return; + FileThumbnailViewItem* thumbItem=static_cast<FileThumbnailViewItem*>(iconItem); + + KFileItem* fileItem=thumbItem->fileItem(); + + if (fileItem->isDir() || Archive::fileItemIsArchive(fileItem)) { + emit executed(iconItem); + } +} + + +void FileThumbnailView::slotClicked(QIconViewItem* iconItem) { + if (!iconItem) return; + if (!KGlobalSettings::singleClick()) return; + FileThumbnailViewItem* thumbItem=static_cast<FileThumbnailViewItem*>(iconItem); + + KFileItem* fileItem=thumbItem->fileItem(); + + if (fileItem->isDir() || Archive::fileItemIsArchive(fileItem)) { + emit executed(iconItem); + } +} + +void FileThumbnailView::slotContentsMoving( int x, int y ) { + updateVisibilityInfo( x, y ); // use x,y, the signal is emitted before moving +} + +void FileThumbnailView::slotCurrentChanged(QIconViewItem* item ) { + // trigger generating thumbnails from the current one + updateVisibilityInfo( contentsX(), contentsY()); + prefetchDone(); + // if the first image is selected, no matter how, preload the next one + for( QIconViewItem* pos = item; + pos != NULL; + pos = pos->nextItem()) { + FileThumbnailViewItem* cur = static_cast< FileThumbnailViewItem* >( pos ); + if( cur->fileItem()->isDir() || Archive::fileItemIsArchive(cur->fileItem())) continue; + if( pos == item && pos->nextItem() != NULL ) { + d->mPrefetch = ImageLoader::loader( + static_cast<const FileThumbnailViewItem*>( cur->nextItem() )->fileItem()->url(), + this, BUSY_PRELOADING ); + connect( d->mPrefetch, SIGNAL( imageLoaded( bool )), SLOT( prefetchDone())); + } + } +} + +/** + * when generating thumbnails, make the current thumbnail + * to be the next one processed by the thumbnail job, if visible, + * otherwise use the first visible thumbnail + */ +void FileThumbnailView::updateVisibilityInfo( int x, int y ) { + if (d->mThumbnailLoadJob.isNull()) return; + + QRect rect( x, y, visibleWidth(), visibleHeight()); + FileThumbnailViewItem* first = static_cast< FileThumbnailViewItem* >( findFirstVisibleItem( rect )); + if (!first) { + d->mThumbnailLoadJob->setPriorityItems(NULL,NULL,NULL); + return; + } + + FileThumbnailViewItem* last = static_cast< FileThumbnailViewItem* >( findLastVisibleItem( rect )); + Q_ASSERT(last); // If we get a first item, then there must be a last + + if (currentItem() && currentItem()->intersects(rect)) { + KFileItem* fileItem = currentFileItem(); + d->mThumbnailLoadJob->setPriorityItems(fileItem, + first->fileItem(), last->fileItem()); + return; + } + + d->mThumbnailLoadJob->setPriorityItems( + first->fileItem(), + first->fileItem(), + last->fileItem()); +} + +void FileThumbnailView::keyPressEvent( QKeyEvent* e ) { +// When the user presses e.g. the Down key, try to preload the next image in that direction. + if( e->key() != Key_Left + && e->key() != Key_Right + && e->key() != Key_Up + && e->key() != Key_Down ) return KIconView::keyPressEvent( e ); + + QIconViewItem* current = currentItem(); + KIconView::keyPressEvent( e ); + QIconViewItem* next = NULL; + if( current != currentItem() && currentItem() != NULL ) { // it actually moved + switch( e->key()) { + case Key_Left: + next = currentItem()->prevItem(); + break; + case Key_Right: + next = currentItem()->nextItem(); + break; + case Key_Up: + // This relies on the thumbnails being in a grid ( x() == x() ) + for( next = currentItem()->prevItem(); + next != NULL && next->x() != currentItem()->x(); + next = next->prevItem()) + ; + break; + case Key_Down: + for( next = currentItem()->nextItem(); + next != NULL && next->x() != currentItem()->x(); + next = next->nextItem()) + ; + break; + } + + } + prefetchDone(); + if( next != NULL ) { + d->mPrefetch = ImageLoader::loader( + static_cast<const FileThumbnailViewItem*>( next )->fileItem()->url(), + this, BUSY_PRELOADING ); + connect( d->mPrefetch, SIGNAL( imageLoaded( bool )), SLOT( prefetchDone())); + } +} + +void FileThumbnailView::prefetchDone() { + if( d->mPrefetch != NULL ) { + d->mPrefetch->release( this ); + d->mPrefetch = NULL; + } +} + +//-------------------------------------------------------------------------- +// +// Protected +// +//-------------------------------------------------------------------------- +void FileThumbnailView::startDrag() { + /** + * The item drawer for DragPixmapGenerator + */ + struct ItemDrawer : public DragPixmapItemDrawer<KFileItem*> { + ItemDrawer(FileThumbnailView* view) + : mView(view) {} + + QSize itemSize(KFileItem* fileItem) { + QPixmap* pix = pixmapFromFileItem(fileItem); + if (!pix) return QSize(); + + QSize size = pix->size(); + int maxWidth = mGenerator->maxWidth(); + if (size.width() > maxWidth) { + size.rheight() = size.height() * maxWidth / size.width(); + size.rwidth() = maxWidth; + } + return size; + } + + int spacing() const { + return 2; + } + + void drawItem(QPainter* painter, int left, int top, KFileItem* fileItem) { + QPixmap* pix = pixmapFromFileItem(fileItem); + if (!pix) return; + + QSize size = itemSize(fileItem); + left += (mGenerator->pixmapWidth() - size.width()) / 2; + if (size == pix->size()) { + painter->drawPixmap(left, top, *pix); + return; + } + + QImage img = pix->convertToImage(); + img = img.smoothScale(size, QImage::ScaleMin); + painter->drawImage(left, top, img); + } + + QPixmap* pixmapFromFileItem(KFileItem* fileItem) { + FileThumbnailViewItem* iconItem = viewItem(mView, fileItem); + Q_ASSERT(iconItem); + if (!iconItem) return 0; + + QPixmap* pix = iconItem->pixmap(); + Q_ASSERT(pix); + if (!pix) return 0; + return pix; + } + + FileThumbnailView* mView; + }; + ItemDrawer drawer(this); + + + 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; + } + + QDragObject* drag=new KURLDrag(urls, this, 0); + QPixmap dragPixmap = generator.generate(); + + drag->setPixmap( dragPixmap, QPoint(generator.DRAG_OFFSET, -generator.DRAG_OFFSET)); + drag->dragCopy(); +} + + +void FileThumbnailView::showThumbnailDetailsDialog() { + if (!d->mThumbnailsDetailDialog) { + d->mThumbnailsDetailDialog = new ThumbnailDetailsDialog(this); + } + d->mThumbnailsDetailDialog->show(); +} + + +} // namespace |