|
|
|
|
/*
|
|
|
|
|
Gwenview - A simple image viewer for TDE
|
|
|
|
|
Copyright 2000-2004 Aur<EFBFBD>lien G<EFBFBD>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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// TQt
|
|
|
|
|
#include <tqframe.h>
|
|
|
|
|
#include <tqlayout.h>
|
|
|
|
|
#include <tqpainter.h>
|
|
|
|
|
#include <tqpen.h>
|
|
|
|
|
#include <tqpixmap.h>
|
|
|
|
|
#include <tqpushbutton.h>
|
|
|
|
|
#include <tqtimer.h>
|
|
|
|
|
#include <tqvaluevector.h>
|
|
|
|
|
|
|
|
|
|
// KDE
|
|
|
|
|
#include <tdeapplication.h>
|
|
|
|
|
#include <tdeconfig.h>
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
|
#include <tdeglobalsettings.h>
|
|
|
|
|
#include <kiconloader.h>
|
|
|
|
|
#include <tdelocale.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 TQFrame {
|
|
|
|
|
KProgress* mProgressBar;
|
|
|
|
|
TQPushButton* mStop;
|
|
|
|
|
public:
|
|
|
|
|
ProgressWidget(FileThumbnailView* view, int count)
|
|
|
|
|
: TQFrame(view)
|
|
|
|
|
{
|
|
|
|
|
TQHBoxLayout* layout=new TQHBoxLayout(this, 3, 3);
|
|
|
|
|
layout->setAutoAdd(true);
|
|
|
|
|
setFrameStyle( TQFrame::StyledPanel | TQFrame::Raised );
|
|
|
|
|
|
|
|
|
|
mStop=new TQPushButton(this);
|
|
|
|
|
mStop->setPixmap(SmallIcon("process-stop"));
|
|
|
|
|
mStop->setFlat(true);
|
|
|
|
|
|
|
|
|
|
mProgressBar=new KProgress(count, this);
|
|
|
|
|
mProgressBar->setFormat("%v/%m");
|
|
|
|
|
|
|
|
|
|
view->clipper()->installEventFilter(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void polish() {
|
|
|
|
|
TQFrame::polish();
|
|
|
|
|
setMinimumWidth(layout()->minimumSize().width());
|
|
|
|
|
//setFixedHeight( mProgressBar->height() );
|
|
|
|
|
setFixedHeight( mStop->height() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void showEvent(TQShowEvent*) {
|
|
|
|
|
updatePosition();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool eventFilter(TQObject*, TQEvent* event) {
|
|
|
|
|
if (event->type()==TQEvent::Resize) {
|
|
|
|
|
updatePosition();
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void updatePosition() {
|
|
|
|
|
FileThumbnailView* view=static_cast<FileThumbnailView*>(parent());
|
|
|
|
|
TQSize tmp=view->clipper()->size() - size();
|
|
|
|
|
move(tmp.width() - 2, tmp.height() - 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
KProgress* progressBar() const { return mProgressBar; }
|
|
|
|
|
TQPushButton* stopButton() const { return mStop; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct FileThumbnailView::Private {
|
|
|
|
|
int mThumbnailSize;
|
|
|
|
|
int mMarginSize;
|
|
|
|
|
bool mUpdateThumbnailsOnNextShow;
|
|
|
|
|
TQPixmap mWaitPixmap; // The wait pixmap (32 x 32)
|
|
|
|
|
TQPixmap mWaitThumbnail; // The wait thumbnail (mThumbnailSize x mThumbnailSize)
|
|
|
|
|
ProgressWidget* mProgressWidget;
|
|
|
|
|
|
|
|
|
|
TQGuardedPtr<ThumbnailLoadJob> mThumbnailLoadJob;
|
|
|
|
|
|
|
|
|
|
TQTimer* mThumbnailUpdateTimer;
|
|
|
|
|
|
|
|
|
|
int mItemDetails;
|
|
|
|
|
|
|
|
|
|
ImageLoader* mPrefetch;
|
|
|
|
|
ThumbnailDetailsDialog* mThumbnailsDetailDialog;
|
|
|
|
|
|
|
|
|
|
void updateWaitThumbnail(const FileThumbnailView* view) {
|
|
|
|
|
mWaitThumbnail=TQPixmap(mThumbnailSize, mThumbnailSize);
|
|
|
|
|
mWaitThumbnail.fill(view->paletteBackgroundColor());
|
|
|
|
|
TQPainter 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(TQWidget* parent)
|
|
|
|
|
: TDEIconView(parent), FileViewBase()
|
|
|
|
|
{
|
|
|
|
|
d=new Private;
|
|
|
|
|
d->mUpdateThumbnailsOnNextShow=false;
|
|
|
|
|
d->mThumbnailLoadJob=0L;
|
|
|
|
|
d->mWaitPixmap=TQPixmap(::locate("appdata", "thumbnail/wait.png"));
|
|
|
|
|
d->mProgressWidget=0L;
|
|
|
|
|
d->mThumbnailUpdateTimer=new TQTimer(this);
|
|
|
|
|
d->mMarginSize=FileViewConfig::thumbnailMarginSize();
|
|
|
|
|
d->mItemDetails=FileViewConfig::thumbnailDetails();
|
|
|
|
|
d->mPrefetch = nullptr;
|
|
|
|
|
d->mThumbnailSize = 0;
|
|
|
|
|
d->mThumbnailsDetailDialog = 0;
|
|
|
|
|
|
|
|
|
|
setItemTextPos( TQIconView::ItemTextPos(FileViewConfig::thumbnailTextPos()) );
|
|
|
|
|
setAutoArrange(true);
|
|
|
|
|
TQIconView::setSorting(true);
|
|
|
|
|
setItemsMovable(false);
|
|
|
|
|
setResizeMode(Adjust);
|
|
|
|
|
setShowToolTips(true);
|
|
|
|
|
setSpacing(0);
|
|
|
|
|
setAcceptDrops(true);
|
|
|
|
|
|
|
|
|
|
// We can't use TDEIconView::Execute mode because in this mode the current
|
|
|
|
|
// item is unselected after being clicked, so we use TDEIconView::Select mode
|
|
|
|
|
// and emit the execute() signal with slotClicked() ourself.
|
|
|
|
|
setMode(TDEIconView::Select);
|
|
|
|
|
connect(this, TQ_SIGNAL(clicked(TQIconViewItem*)),
|
|
|
|
|
this, TQ_SLOT(slotClicked(TQIconViewItem*)) );
|
|
|
|
|
connect(this, TQ_SIGNAL(doubleClicked(TQIconViewItem*)),
|
|
|
|
|
this, TQ_SLOT(slotDoubleClicked(TQIconViewItem*)) );
|
|
|
|
|
|
|
|
|
|
connect(this, TQ_SIGNAL(dropped(TQDropEvent*,const TQValueList<TQIconDragItem>&)),
|
|
|
|
|
this, TQ_SLOT(slotDropped(TQDropEvent*)) );
|
|
|
|
|
connect(this, TQ_SIGNAL( contentsMoving( int, int )),
|
|
|
|
|
this, TQ_SLOT( slotContentsMoving( int, int )));
|
|
|
|
|
connect(this, TQ_SIGNAL(currentChanged(TQIconViewItem*)),
|
|
|
|
|
this, TQ_SLOT(slotCurrentChanged(TQIconViewItem*)) );
|
|
|
|
|
|
|
|
|
|
TQIconView::setSelectionMode(Extended);
|
|
|
|
|
|
|
|
|
|
connect(BusyLevelManager::instance(), TQ_SIGNAL(busyLevelChanged(BusyLevel)),
|
|
|
|
|
this, TQ_SLOT( slotBusyLevelChanged(BusyLevel)));
|
|
|
|
|
|
|
|
|
|
connect(d->mThumbnailUpdateTimer, TQ_SIGNAL(timeout()),
|
|
|
|
|
this, TQ_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();
|
|
|
|
|
TQPixmap pixmap=createItemPixmap(item);
|
|
|
|
|
TQIconViewItem* 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) {
|
|
|
|
|
TQIconView::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 (TQIconViewItem* 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 TQPixmap& thumbnail, const TQSize& 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
|
|
|
|
|
//
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
TQPixmap 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TQPixmap thumbnail(d->mThumbnailSize, d->mThumbnailSize);
|
|
|
|
|
thumbnail.fill(paletteBackgroundColor());
|
|
|
|
|
TQPainter painter(&thumbnail);
|
|
|
|
|
|
|
|
|
|
// Load the icon
|
|
|
|
|
TQPixmap itemPix=item->pixmap(TQMIN(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) {
|
|
|
|
|
TQValueVector<const KFileItem*> imageList;
|
|
|
|
|
imageList.reserve( list->count());
|
|
|
|
|
TQPtrListIterator<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(), TQ_SIGNAL(clicked()),
|
|
|
|
|
this, TQ_SLOT(stopThumbnailUpdate()) );
|
|
|
|
|
d->mProgressWidget->show();
|
|
|
|
|
|
|
|
|
|
d->mThumbnailLoadJob = new ThumbnailLoadJob(&imageList, d->mThumbnailSize);
|
|
|
|
|
|
|
|
|
|
connect(d->mThumbnailLoadJob, TQ_SIGNAL(thumbnailLoaded(const KFileItem*, const TQPixmap&, const TQSize&)),
|
|
|
|
|
this, TQ_SLOT(setThumbnailPixmap(const KFileItem*,const TQPixmap&, const TQSize&)) );
|
|
|
|
|
connect(d->mThumbnailLoadJob, TQ_SIGNAL(result(TDEIO::Job*)),
|
|
|
|
|
this, TQ_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);
|
|
|
|
|
d->mThumbnailLoadJob=nullptr;
|
|
|
|
|
// The job loads image dimensions and this may add extra line to the descriptions, which
|
|
|
|
|
// may mess up the grid, even if the job is canceled, some items may be already updated.
|
|
|
|
|
arrangeItemsInGrid();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FileThumbnailView::slotUpdateEnded() {
|
|
|
|
|
Q_ASSERT(d->mProgressWidget);
|
|
|
|
|
delete d->mProgressWidget;
|
|
|
|
|
d->mProgressWidget=0L;
|
|
|
|
|
|
|
|
|
|
BusyLevelManager::instance()->setBusyLevel( this, BUSY_NONE );
|
|
|
|
|
// Besides thumbnails the job loads image dimensions and this may mess up the grid
|
|
|
|
|
arrangeItemsInGrid();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
TQIconView::clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FileThumbnailView::insertItem(KFileItem* item) {
|
|
|
|
|
if (!item) return;
|
|
|
|
|
bool isDirOrArchive=item->isDir() || Archive::fileItemIsArchive(item);
|
|
|
|
|
|
|
|
|
|
TQPixmap 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, bool metaInfoOnly) {
|
|
|
|
|
if (!fileItem) return;
|
|
|
|
|
FileThumbnailViewItem* iconItem=viewItem(this, fileItem);
|
|
|
|
|
if (iconItem) {
|
|
|
|
|
if (metaInfoOnly) {
|
|
|
|
|
iconItem->updateLines();
|
|
|
|
|
// Note: resort will be done when metadata is completely loaded
|
|
|
|
|
} else {
|
|
|
|
|
iconItem->setText(fileItem->text());
|
|
|
|
|
updateThumbnail(fileItem);
|
|
|
|
|
sortView();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FileThumbnailView::ensureItemVisible(const KFileItem* fileItem) {
|
|
|
|
|
if (!fileItem) return;
|
|
|
|
|
|
|
|
|
|
FileThumbnailViewItem* iconItem=viewItem(this, fileItem);
|
|
|
|
|
if (iconItem) TQIconView::ensureItemVisible(iconItem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FileThumbnailView::setCurrentItem(const KFileItem* fileItem) {
|
|
|
|
|
if (!fileItem) return;
|
|
|
|
|
|
|
|
|
|
FileThumbnailViewItem* iconItem=viewItem(this, fileItem);
|
|
|
|
|
if (iconItem) TQIconView::setCurrentItem(iconItem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FileThumbnailView::setSelected(const KFileItem* fileItem,bool enable) {
|
|
|
|
|
if (!fileItem) return;
|
|
|
|
|
|
|
|
|
|
FileThumbnailViewItem* iconItem=viewItem(this, fileItem);
|
|
|
|
|
if (iconItem) TQIconView::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 TQIconViewItem* 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(TQDir::SortSpec spec) {
|
|
|
|
|
KFileView::setSorting(spec);
|
|
|
|
|
|
|
|
|
|
KFileItem *item;
|
|
|
|
|
KFileItemListIterator it( *items() );
|
|
|
|
|
|
|
|
|
|
for ( ; (item = it.current() ); ++it ) {
|
|
|
|
|
TQIconViewItem* iconItem=viewItem(this, item);
|
|
|
|
|
if (iconItem) setSortingKey(iconItem, item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sortView();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Drop support
|
|
|
|
|
//
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
void FileThumbnailView::contentsDragEnterEvent(TQDragEnterEvent* event) {
|
|
|
|
|
return event->accept( KURLDrag::canDecode(event) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FileThumbnailView::slotDropped(TQDropEvent* event) {
|
|
|
|
|
emit dropped(event,0L);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FileThumbnailView::showEvent(TQShowEvent* event) {
|
|
|
|
|
TDEIconView::showEvent(event);
|
|
|
|
|
if (!d->mUpdateThumbnailsOnNextShow) return;
|
|
|
|
|
|
|
|
|
|
d->mUpdateThumbnailsOnNextShow=false;
|
|
|
|
|
TQTimer::singleShot(0, this, TQ_SLOT(startThumbnailUpdate()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Private
|
|
|
|
|
//
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
void FileThumbnailView::updateGrid() {
|
|
|
|
|
if (itemTextPos()==Right) {
|
|
|
|
|
setGridX(
|
|
|
|
|
d->mThumbnailSize
|
|
|
|
|
+ FileThumbnailViewItem::PADDING*3
|
|
|
|
|
+ RIGHT_TEXT_WIDTH);
|
|
|
|
|
} else {
|
|
|
|
|
setGridX(
|
|
|
|
|
TQMAX(d->mThumbnailSize, BOTTOM_MIN_TEXT_WIDTH)
|
|
|
|
|
+ FileThumbnailViewItem::PADDING*2);
|
|
|
|
|
}
|
|
|
|
|
setSpacing(d->mMarginSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FileThumbnailView::setSortingKey(TQIconViewItem *iconItem, const KFileItem *item)
|
|
|
|
|
{
|
|
|
|
|
// see also setSorting()
|
|
|
|
|
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 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iconItem->setKey(key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Private slots
|
|
|
|
|
//
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
void FileThumbnailView::slotDoubleClicked(TQIconViewItem* iconItem) {
|
|
|
|
|
if (!iconItem) return;
|
|
|
|
|
if (TDEGlobalSettings::singleClick()) return;
|
|
|
|
|
FileThumbnailViewItem* thumbItem=static_cast<FileThumbnailViewItem*>(iconItem);
|
|
|
|
|
|
|
|
|
|
KFileItem* fileItem=thumbItem->fileItem();
|
|
|
|
|
|
|
|
|
|
if (fileItem->isDir() || Archive::fileItemIsArchive(fileItem)) {
|
|
|
|
|
emit executed(iconItem);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FileThumbnailView::slotClicked(TQIconViewItem* iconItem) {
|
|
|
|
|
if (!iconItem) return;
|
|
|
|
|
if (!TDEGlobalSettings::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(TQIconViewItem* 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( TQIconViewItem* pos = item;
|
|
|
|
|
pos != nullptr;
|
|
|
|
|
pos = pos->nextItem()) {
|
|
|
|
|
FileThumbnailViewItem* cur = static_cast< FileThumbnailViewItem* >( pos );
|
|
|
|
|
if( cur->fileItem()->isDir() || Archive::fileItemIsArchive(cur->fileItem())) continue;
|
|
|
|
|
if( pos == item && pos->nextItem() != nullptr ) {
|
|
|
|
|
d->mPrefetch = ImageLoader::loader(
|
|
|
|
|
static_cast<const FileThumbnailViewItem*>( cur->nextItem() )->fileItem()->url(),
|
|
|
|
|
this, BUSY_PRELOADING );
|
|
|
|
|
connect( d->mPrefetch, TQ_SIGNAL( imageLoaded( bool )), TQ_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;
|
|
|
|
|
|
|
|
|
|
TQRect rect( x, y, visibleWidth(), visibleHeight());
|
|
|
|
|
FileThumbnailViewItem* first = static_cast< FileThumbnailViewItem* >( findFirstVisibleItem( rect ));
|
|
|
|
|
if (!first) {
|
|
|
|
|
d->mThumbnailLoadJob->setPriorityItems(nullptr,nullptr,nullptr);
|
|
|
|
|
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( TQKeyEvent* 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 TDEIconView::keyPressEvent( e );
|
|
|
|
|
|
|
|
|
|
TQIconViewItem* current = currentItem();
|
|
|
|
|
TDEIconView::keyPressEvent( e );
|
|
|
|
|
TQIconViewItem* next = nullptr;
|
|
|
|
|
if( current != currentItem() && currentItem() != nullptr ) { // 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 != nullptr && next->x() != currentItem()->x();
|
|
|
|
|
next = next->prevItem())
|
|
|
|
|
;
|
|
|
|
|
break;
|
|
|
|
|
case Key_Down:
|
|
|
|
|
for( next = currentItem()->nextItem();
|
|
|
|
|
next != nullptr && next->x() != currentItem()->x();
|
|
|
|
|
next = next->nextItem())
|
|
|
|
|
;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
prefetchDone();
|
|
|
|
|
if( next != nullptr ) {
|
|
|
|
|
d->mPrefetch = ImageLoader::loader(
|
|
|
|
|
static_cast<const FileThumbnailViewItem*>( next )->fileItem()->url(),
|
|
|
|
|
this, BUSY_PRELOADING );
|
|
|
|
|
connect( d->mPrefetch, TQ_SIGNAL( imageLoaded( bool )), TQ_SLOT( prefetchDone()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FileThumbnailView::prefetchDone() {
|
|
|
|
|
if( d->mPrefetch != nullptr ) {
|
|
|
|
|
d->mPrefetch->release( this );
|
|
|
|
|
d->mPrefetch = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FileThumbnailView::sortView() {
|
|
|
|
|
TDEIconView::sort( !(KFileView::sorting() & TQDir::Reversed) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Protected
|
|
|
|
|
//
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
void FileThumbnailView::startDrag() {
|
|
|
|
|
/**
|
|
|
|
|
* The item drawer for DragPixmapGenerator
|
|
|
|
|
*/
|
|
|
|
|
struct ItemDrawer : public DragPixmapItemDrawer<KFileItem*> {
|
|
|
|
|
ItemDrawer(FileThumbnailView* view)
|
|
|
|
|
: mView(view) {}
|
|
|
|
|
|
|
|
|
|
TQSize itemSize(KFileItem* fileItem) {
|
|
|
|
|
TQPixmap* pix = pixmapFromFileItem(fileItem);
|
|
|
|
|
if (!pix) return TQSize();
|
|
|
|
|
|
|
|
|
|
TQSize 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(TQPainter* painter, int left, int top, KFileItem* fileItem) {
|
|
|
|
|
TQPixmap* pix = pixmapFromFileItem(fileItem);
|
|
|
|
|
if (!pix) return;
|
|
|
|
|
|
|
|
|
|
TQSize size = itemSize(fileItem);
|
|
|
|
|
left += (mGenerator->pixmapWidth() - size.width()) / 2;
|
|
|
|
|
if (size == pix->size()) {
|
|
|
|
|
painter->drawPixmap(left, top, *pix);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TQImage img = pix->convertToImage();
|
|
|
|
|
img = img.smoothScale(size, TQImage::ScaleMin);
|
|
|
|
|
painter->drawImage(left, top, img);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TQPixmap* pixmapFromFileItem(KFileItem* fileItem) {
|
|
|
|
|
FileThumbnailViewItem* iconItem = viewItem(mView, fileItem);
|
|
|
|
|
Q_ASSERT(iconItem);
|
|
|
|
|
if (!iconItem) return 0;
|
|
|
|
|
|
|
|
|
|
TQPixmap* 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TQDragObject* drag=new KURLDrag(urls, this, 0);
|
|
|
|
|
TQPixmap dragPixmap = generator.generate();
|
|
|
|
|
|
|
|
|
|
drag->setPixmap( dragPixmap, TQPoint(generator.DRAG_OFFSET, -generator.DRAG_OFFSET));
|
|
|
|
|
drag->dragCopy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FileThumbnailView::showThumbnailDetailsDialog() {
|
|
|
|
|
if (!d->mThumbnailsDetailDialog) {
|
|
|
|
|
d->mThumbnailsDetailDialog = new ThumbnailDetailsDialog(this);
|
|
|
|
|
}
|
|
|
|
|
d->mThumbnailsDetailDialog->show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace
|