|
|
|
/***************************************************************************
|
|
|
|
thumbview.cpp - Class to display thumbnailed images
|
|
|
|
-------------------
|
|
|
|
begin : Tue Apr 18 2002
|
|
|
|
copyright : (C) 2002 by Klaas Freitag
|
|
|
|
email : freitag@suse.de
|
|
|
|
|
|
|
|
$Id$
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* *
|
|
|
|
* This file may be distributed and/or modified under the terms of the *
|
|
|
|
* GNU General Public License version 2 as published by the Free Software *
|
|
|
|
* Foundation and appearing in the file COPYING included in the *
|
|
|
|
* packaging of this file. *
|
|
|
|
*
|
|
|
|
* As a special exception, permission is given to link this program *
|
|
|
|
* with any version of the KADMOS ocr/icr engine of reRecognition GmbH, *
|
|
|
|
* Kreuzlingen and distribute the resulting executable without *
|
|
|
|
* including the source code for KADMOS in the source distribution. *
|
|
|
|
*
|
|
|
|
* As a special exception, permission is given to link this program *
|
|
|
|
* with any edition of TQt, and distribute the resulting executable, *
|
|
|
|
* without including the source code for TQt in the source distribution. *
|
|
|
|
* *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#include <tqpixmap.h>
|
|
|
|
#include <tqpainter.h>
|
|
|
|
|
|
|
|
#include <tdeio/previewjob.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <tdefileitem.h>
|
|
|
|
#include <tdefileiconview.h>
|
|
|
|
#include <tdefiletreeviewitem.h>
|
|
|
|
#include <kimageeffect.h>
|
|
|
|
#include <tdeglobal.h>
|
|
|
|
#include <kstandarddirs.h>
|
|
|
|
#include <kprogress.h>
|
|
|
|
|
|
|
|
#include "thumbview.h"
|
|
|
|
#include "thumbview.moc"
|
|
|
|
|
|
|
|
#include "thumbviewitem.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ThumbView::ThumbView( TQWidget *parent, const char *name )
|
|
|
|
: TQVBox( parent ),
|
|
|
|
m_iconView(0),
|
|
|
|
m_job(0)
|
|
|
|
{
|
|
|
|
setMargin(3);
|
|
|
|
m_pixWidth = 0;
|
|
|
|
m_pixHeight = 0;
|
|
|
|
m_thumbMargin = 5;
|
|
|
|
m_iconView = new TDEIconView( this, name );
|
|
|
|
m_progress = new KProgress( this );
|
|
|
|
m_progress->hide();
|
|
|
|
|
|
|
|
m_pixWidth = 100;
|
|
|
|
m_pixHeight = 100;
|
|
|
|
|
|
|
|
readSettings();
|
|
|
|
|
|
|
|
m_basePix.resize( TQSize( m_pixWidth, m_pixHeight ) );
|
|
|
|
m_basePix.fill(); // fills white per default TODO
|
|
|
|
|
|
|
|
|
|
|
|
m_iconView->setItemsMovable( false );
|
|
|
|
|
|
|
|
slSetBackGround();
|
|
|
|
|
|
|
|
connect( m_iconView, TQ_SIGNAL( executed( TQIconViewItem* )),
|
|
|
|
this, TQ_SLOT( slDoubleClicked( TQIconViewItem* )));
|
|
|
|
|
|
|
|
m_pendingJobs.setAutoDelete(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
ThumbView::~ThumbView()
|
|
|
|
{
|
|
|
|
saveConfig();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ThumbView::readSettings()
|
|
|
|
{
|
|
|
|
TDEConfig *cfg = TDEGlobal::config();
|
|
|
|
cfg->setGroup( THUMB_GROUP );
|
|
|
|
bool dirty = false;
|
|
|
|
|
|
|
|
TQColor color;
|
|
|
|
color = cfg->readColorEntry( MARGIN_COLOR1, &(colorGroup().base()));
|
|
|
|
if( color != m_marginColor1 )
|
|
|
|
{
|
|
|
|
dirty = true;
|
|
|
|
m_marginColor1 = color;
|
|
|
|
}
|
|
|
|
|
|
|
|
color = cfg->readColorEntry( MARGIN_COLOR2, &(colorGroup().foreground()));
|
|
|
|
if( color != m_marginColor2 )
|
|
|
|
{
|
|
|
|
dirty = true;
|
|
|
|
m_marginColor2 = color;
|
|
|
|
}
|
|
|
|
|
|
|
|
int value;
|
|
|
|
bool sizeDirty = false;
|
|
|
|
value = cfg->readNumEntry( THUMB_MARGIN, 5 );
|
|
|
|
if( value != m_thumbMargin )
|
|
|
|
{
|
|
|
|
sizeDirty = true;
|
|
|
|
m_thumbMargin = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = cfg->readNumEntry( PIXMAP_WIDTH, 100 );
|
|
|
|
if( value != m_pixWidth || m_pixWidth == 0 )
|
|
|
|
{
|
|
|
|
sizeDirty = true;
|
|
|
|
m_pixWidth = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = cfg->readNumEntry( PIXMAP_HEIGHT, 120 );
|
|
|
|
if( value != m_pixHeight || m_pixHeight == 0 )
|
|
|
|
{
|
|
|
|
sizeDirty = true;
|
|
|
|
m_pixHeight = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( sizeDirty )
|
|
|
|
{
|
|
|
|
int gX = 2*m_thumbMargin+m_pixWidth+10;
|
|
|
|
int gY = 2*m_thumbMargin+m_pixHeight+10;
|
|
|
|
m_iconView->setGridX(gX);
|
|
|
|
m_iconView->setGridY(gY);
|
|
|
|
kdDebug(28000) << "Setting Grid " << gX << " - " << gY << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
TDEStandardDirs stdDir;
|
|
|
|
TQString newBgImg = cfg->readEntry( BG_WALLPAPER, stdDir.findResource( "data", STD_TILE_IMG ) );
|
|
|
|
|
|
|
|
if( m_bgImg != newBgImg )
|
|
|
|
{
|
|
|
|
m_bgImg = newBgImg;
|
|
|
|
slSetBackGround();
|
|
|
|
}
|
|
|
|
|
|
|
|
return (sizeDirty || dirty);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ThumbView::slDoubleClicked( TQIconViewItem *qIt )
|
|
|
|
{
|
|
|
|
ThumbViewItem *it = static_cast<ThumbViewItem*>( qIt );
|
|
|
|
|
|
|
|
if( it )
|
|
|
|
{
|
|
|
|
const KURL url = it->itemUrl();
|
|
|
|
|
|
|
|
emit( selectFromThumbnail( url ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ThumbView::slSetBackGround( )
|
|
|
|
{
|
|
|
|
TQPixmap bgPix;
|
|
|
|
if( m_bgImg.isEmpty())
|
|
|
|
{
|
|
|
|
bgPix.resize( TQSize(16, 16));
|
|
|
|
bgPix.fill( TQPixmap::blue );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bgPix.load( m_bgImg );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_iconView->setPaletteBackgroundPixmap ( bgPix );
|
|
|
|
setPaletteBackgroundPixmap ( bgPix );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void ThumbView::slImageChanged( KFileItem *kfit )
|
|
|
|
{
|
|
|
|
if( ! kfit ) return;
|
|
|
|
// kdDebug(28000) << "changes to one thumbnail!" << endl;
|
|
|
|
|
|
|
|
KURL thumbDir = currentDir();
|
|
|
|
KURL itemUrl = kfit->url();
|
|
|
|
|
|
|
|
/* delete filename */
|
|
|
|
itemUrl.setFileName( TQString());
|
|
|
|
if( !itemUrl.equals( thumbDir, true ))
|
|
|
|
{
|
|
|
|
// kdDebug(28000) << "returning, because directory does not match: " << itemUrl.prettyURL() << endl;
|
|
|
|
// kdDebug(28000) << "and my URL: " << thumbDir.prettyURL() << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( deleteImage( kfit ))
|
|
|
|
{
|
|
|
|
kdDebug(28000) << "was changed, deleted first!" << endl;
|
|
|
|
}
|
|
|
|
/* Trigger a new reading */
|
|
|
|
KFileItemList li;
|
|
|
|
li.append( kfit );
|
|
|
|
slNewFileItems( li );
|
|
|
|
}
|
|
|
|
|
|
|
|
void ThumbView::slImageRenamed( KFileItem *kfit, const KURL& newUrl )
|
|
|
|
{
|
|
|
|
const KURL url = kfit->url();
|
|
|
|
|
|
|
|
if( kfit->isDir() ) {
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( TQIconViewItem *item = m_iconView->firstItem(); item; item = item->nextItem() )
|
|
|
|
{
|
|
|
|
ThumbViewItem *it=static_cast<ThumbViewItem*>( item );
|
|
|
|
|
|
|
|
if( url == it->itemUrl() )
|
|
|
|
{
|
|
|
|
it->setItemUrl( newUrl );
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ThumbView::slCheckForUpdate( KFileItem *kfit )
|
|
|
|
{
|
|
|
|
if( ! kfit ) return;
|
|
|
|
|
|
|
|
kdDebug(28000) << "Checking for update of thumbview!" << endl;
|
|
|
|
|
|
|
|
KURL searchUrl = kfit->url();
|
|
|
|
bool haveItem = false;
|
|
|
|
|
|
|
|
/* iterate over all icon items and compare urls.
|
|
|
|
* TODO: Check the parent url to avoid iteration over all */
|
|
|
|
for ( TQIconViewItem *item = m_iconView->firstItem(); item && !haveItem;
|
|
|
|
item = item->nextItem() )
|
|
|
|
{
|
|
|
|
if( searchUrl == static_cast<ThumbViewItem*>(item)->itemUrl() )
|
|
|
|
{
|
|
|
|
haveItem = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if we still do not have the item, it is not in the thumbview. */
|
|
|
|
if( ! haveItem )
|
|
|
|
{
|
|
|
|
KFileItemList kfiList;
|
|
|
|
|
|
|
|
kfiList.append( kfit );
|
|
|
|
slNewFileItems( kfiList );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ThumbView::deleteImage( KFileItem *kfit )
|
|
|
|
{
|
|
|
|
if( ! kfit ) return false;
|
|
|
|
|
|
|
|
|
|
|
|
KURL searchUrl = kfit->url();
|
|
|
|
bool haveItem = false;
|
|
|
|
|
|
|
|
/* iterate over all icon items and compare urls.
|
|
|
|
* TODO: Check the parent url to avoid iteration over all */
|
|
|
|
for ( TQIconViewItem *item = m_iconView->firstItem(); item && !haveItem; item = item->nextItem() )
|
|
|
|
{
|
|
|
|
if( searchUrl == static_cast<ThumbViewItem*>(item)->itemUrl() )
|
|
|
|
{
|
|
|
|
m_iconView->takeItem( item );
|
|
|
|
haveItem = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
kdDebug(28000) << "Deleting image from thumbview, result is " << haveItem << endl;
|
|
|
|
return( haveItem );
|
|
|
|
}
|
|
|
|
|
|
|
|
void ThumbView::slImageDeleted( KFileItem *kfit )
|
|
|
|
{
|
|
|
|
deleteImage( kfit );
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
From a mail from Waldo pointing out two probs in Thumbview:
|
|
|
|
|
|
|
|
1) KDirLister is the owner of the KFileItems it emits, this means
|
|
|
|
that you must watch it's deleteItem() signal vigourously,
|
|
|
|
otherwise you may end up with KFileItems that are already
|
|
|
|
deleted. This burden is propagated to classes that use
|
|
|
|
KDirLister, such as KFileIconView.
|
|
|
|
|
|
|
|
This has a tendency to go wrong in combination with PreviewJob,
|
|
|
|
because it stores a list of KFileItems while running. This has
|
|
|
|
the potential to crash if the fileitems are being deleted
|
|
|
|
during this time. The remedy is to make sure to remove
|
|
|
|
fileitems that get deleted from the PreviewJob with
|
|
|
|
PreviewJob::removeItem.
|
|
|
|
|
|
|
|
*/
|
|
|
|
if( m_job ) /* is a job running? Remove the item from it if existing. */
|
|
|
|
{
|
|
|
|
m_job->removeItem( kfit );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if it is in the pending list */
|
|
|
|
m_pendingJobs.removeRef(kfit);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ThumbView::slNewFileItems( const KFileItemList& items )
|
|
|
|
{
|
|
|
|
kdDebug(28000) << "Creating thumbnails for fileItemList" << endl;
|
|
|
|
|
|
|
|
/* Fill the pending jobs list. */
|
|
|
|
KFileItemListIterator it( items );
|
|
|
|
KFileItem *item = 0;
|
|
|
|
for ( ; (item = it.current()); ++it )
|
|
|
|
{
|
|
|
|
TQString filename = item->url().prettyURL();
|
|
|
|
if( item->isDir() )
|
|
|
|
{
|
|
|
|
/* create a dir pixmap */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TQPixmap p(m_basePix) ;
|
|
|
|
TQPixmap mime( item->pixmap(0) );
|
|
|
|
|
|
|
|
if( p.width() > mime.width() && p.height() > mime.height() )
|
|
|
|
{
|
|
|
|
TQPainter paint( &p );
|
|
|
|
paint.drawPixmap( (p.width()-mime.width())/2,
|
|
|
|
(p.height()-mime.height())/2,
|
|
|
|
mime );
|
|
|
|
paint.flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a new empty preview pixmap and store the pointer to it */
|
|
|
|
ThumbViewItem *newIconViewIt = new ThumbViewItem( m_iconView,
|
|
|
|
item->url().filename(),
|
|
|
|
createPixmap( p ),
|
|
|
|
item );
|
|
|
|
|
|
|
|
newIconViewIt->setItemUrl( item->url() );
|
|
|
|
|
|
|
|
/* tell the file item about the iconView-representation */
|
|
|
|
item->setExtraData( this, newIconViewIt );
|
|
|
|
|
|
|
|
m_pendingJobs.append( item );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
From a mail from Waldo Bastian pointing out problems with thumbview:
|
|
|
|
|
|
|
|
2) I think you may end up creating two PreviewJob's in parallel
|
|
|
|
when the slNewFileItems() function is called two times in
|
|
|
|
quick succession. The current code doesn't seem to expect
|
|
|
|
that, given the comment in slPreviewResult(). In the light of
|
|
|
|
1) it might become fatal since you will not be able to call
|
|
|
|
PreviewJob::removeItem on the proper job. I suggest to queue
|
|
|
|
new items when a job is already running and start a new job
|
|
|
|
once the first one is finished when there are any items left
|
|
|
|
in the queue. Don't forget to delete items from the queue if
|
|
|
|
they get deleted in the mean time.
|
|
|
|
|
|
|
|
The strategy is as follows: In the global list m_pendingJobs
|
|
|
|
the jobs to start are appended. Only if m_job is zero (no job
|
|
|
|
is running) a job is started on the current m_pendingJobs list.
|
|
|
|
The m_pendingJobs list is clear afterwords.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if( ! m_job && m_pendingJobs.count() > 0 )
|
|
|
|
{
|
|
|
|
/* Progress-Bar */
|
|
|
|
m_progress->show();
|
|
|
|
m_progress->setTotalSteps(m_pendingJobs.count());
|
|
|
|
m_cntJobsStarted = 0;
|
|
|
|
|
|
|
|
/* start a preview-job */
|
|
|
|
m_job = TDEIO::filePreview(m_pendingJobs, m_pixWidth, m_pixHeight );
|
|
|
|
|
|
|
|
if( m_job )
|
|
|
|
{
|
|
|
|
connect( m_job, TQ_SIGNAL( result( TDEIO::Job * )),
|
|
|
|
this, TQ_SLOT( slPreviewResult( TDEIO::Job * )));
|
|
|
|
connect( m_job, TQ_SIGNAL( gotPreview( const KFileItem*, const TQPixmap& )),
|
|
|
|
TQ_SLOT( slGotPreview( const KFileItem*, const TQPixmap& ) ));
|
|
|
|
|
|
|
|
m_pendingJobs.clear();
|
|
|
|
|
|
|
|
/* TDEIO::Jo result is called in any way: Success, Failed, Error,
|
|
|
|
* thus connecting the failed is not really necessary.
|
|
|
|
*/
|
|
|
|
// connect( job, TQ_SIGNAL( failed( const KFileItem* )),
|
|
|
|
// this, TQ_SLOT( slotFailed( const KFileItem* ) ));
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void ThumbView::slGotPreview( const KFileItem* newFileItem, const TQPixmap& newPix )
|
|
|
|
{
|
|
|
|
if( ! newFileItem ) return;
|
|
|
|
KFileIconViewItem *item = static_cast<KFileIconViewItem*>(const_cast<void*>(newFileItem->extraData( this )));
|
|
|
|
|
|
|
|
if( ! item ) return;
|
|
|
|
|
|
|
|
item->setPixmap( createPixmap(newPix) );
|
|
|
|
m_cntJobsStarted+=1;
|
|
|
|
|
|
|
|
m_progress->setProgress(m_cntJobsStarted);
|
|
|
|
|
|
|
|
// kdDebug(28000)<< "jobs-Counter: " << m_cntJobsStarted << endl;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void ThumbView::slPreviewResult( TDEIO::Job *job )
|
|
|
|
{
|
|
|
|
if( job && job->error() > 0 )
|
|
|
|
{
|
|
|
|
kdDebug(28000) << "Thumbnail Creation ERROR: " << job->errorString() << endl;
|
|
|
|
job->showErrorDialog( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( job != m_job )
|
|
|
|
{
|
|
|
|
kdDebug(28000) << "Very obscure: Job finished is not mine!" << endl;
|
|
|
|
}
|
|
|
|
/* finished */
|
|
|
|
kdDebug(28000) << "Thumbnail job finished." << endl;
|
|
|
|
m_cntJobsStarted = 0;
|
|
|
|
m_progress->reset();
|
|
|
|
m_progress->hide();
|
|
|
|
m_job = 0L;
|
|
|
|
|
|
|
|
/* maybe there is a new job to start because of pending items? */
|
|
|
|
if( m_pendingJobs.count() > 0 )
|
|
|
|
{
|
|
|
|
slNewFileItems( KFileItemList() ); /* Call with an empty list */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQPixmap ThumbView::createPixmap( const TQPixmap& preview ) const
|
|
|
|
{
|
|
|
|
TQImage ires = KImageEffect::unbalancedGradient( TQSize( 2*m_thumbMargin+ preview.width(),
|
|
|
|
2*m_thumbMargin+ preview.height()),
|
|
|
|
m_marginColor1, m_marginColor2,
|
|
|
|
KImageEffect::DiagonalGradient );
|
|
|
|
|
|
|
|
|
|
|
|
TQPixmap pixRet;
|
|
|
|
pixRet.convertFromImage( ires );
|
|
|
|
TQPainter p( &pixRet );
|
|
|
|
|
|
|
|
p.drawPixmap( m_thumbMargin, m_thumbMargin, preview );
|
|
|
|
p.flush();
|
|
|
|
// draw on pixmap
|
|
|
|
|
|
|
|
return( pixRet );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ThumbView::clear()
|
|
|
|
{
|
|
|
|
if( m_job )
|
|
|
|
m_job->kill( false /* not silently to get result-signal */ );
|
|
|
|
m_iconView->clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ThumbView::saveConfig()
|
|
|
|
{
|
|
|
|
TDEConfig *cfg = TDEGlobal::config();
|
|
|
|
cfg->setGroup( THUMB_GROUP );
|
|
|
|
|
|
|
|
cfg->writeEntry( MARGIN_COLOR1, m_marginColor1 );
|
|
|
|
cfg->writeEntry( MARGIN_COLOR2, m_marginColor2 );
|
|
|
|
cfg->writeEntry( PIXMAP_WIDTH, m_pixWidth );
|
|
|
|
cfg->writeEntry( PIXMAP_HEIGHT, m_pixHeight );
|
|
|
|
cfg->writeEntry( THUMB_MARGIN, m_thumbMargin );
|
|
|
|
|
|
|
|
|
|
|
|
}
|