diff options
Diffstat (limited to 'kooka/thumbview.cpp')
-rw-r--r-- | kooka/thumbview.cpp | 494 |
1 files changed, 494 insertions, 0 deletions
diff --git a/kooka/thumbview.cpp b/kooka/thumbview.cpp new file mode 100644 index 00000000..5dfa93b6 --- /dev/null +++ b/kooka/thumbview.cpp @@ -0,0 +1,494 @@ +/*************************************************************************** + 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 Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#include <qpixmap.h> +#include <qpainter.h> + +#include <kio/previewjob.h> +#include <kdebug.h> +#include <kfileitem.h> +#include <kfileiconview.h> +#include <kfiletreeviewitem.h> +#include <kimageeffect.h> +#include <kglobal.h> +#include <kstandarddirs.h> +#include <kprogress.h> + +#include "thumbview.h" +#include "thumbview.moc" + +#include "thumbviewitem.h" + + + +ThumbView::ThumbView( QWidget *parent, const char *name ) + : QVBox( parent ), + m_iconView(0), + m_job(0) +{ + setMargin(3); + m_pixWidth = 0; + m_pixHeight = 0; + m_thumbMargin = 5; + m_iconView = new KIconView( this, name ); + m_progress = new KProgress( this ); + m_progress->hide(); + + m_pixWidth = 100; + m_pixHeight = 100; + + readSettings(); + + m_basePix.resize( QSize( m_pixWidth, m_pixHeight ) ); + m_basePix.fill(); // fills white per default TODO + + + m_iconView->setItemsMovable( false ); + + slSetBackGround(); + + connect( m_iconView, SIGNAL( executed( QIconViewItem* )), + this, SLOT( slDoubleClicked( QIconViewItem* ))); + + m_pendingJobs.setAutoDelete(false); +} + +ThumbView::~ThumbView() +{ + saveConfig(); +} + +bool ThumbView::readSettings() +{ + KConfig *cfg = KGlobal::config(); + cfg->setGroup( THUMB_GROUP ); + bool dirty = false; + + QColor 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; + } + + KStandardDirs stdDir; + QString 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( QIconViewItem *qIt ) +{ + ThumbViewItem *it = static_cast<ThumbViewItem*>( qIt ); + + if( it ) + { + const KURL url = it->itemUrl(); + + emit( selectFromThumbnail( url )); + } +} + +void ThumbView::slSetBackGround( ) +{ + QPixmap bgPix; + if( m_bgImg.isEmpty()) + { + bgPix.resize( QSize(16, 16)); + bgPix.fill( QPixmap::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( QString()); + 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 ( QIconViewItem *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 ( QIconViewItem *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 ( QIconViewItem *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 ) + { + QString filename = item->url().prettyURL(); + if( item->isDir() ) + { + /* create a dir pixmap */ + } + else + { + QPixmap p(m_basePix) ; + QPixmap mime( item->pixmap(0) ); + + if( p.width() > mime.width() && p.height() > mime.height() ) + { + QPainter 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 = KIO::filePreview(m_pendingJobs, m_pixWidth, m_pixHeight ); + + if( m_job ) + { + connect( m_job, SIGNAL( result( KIO::Job * )), + this, SLOT( slPreviewResult( KIO::Job * ))); + connect( m_job, SIGNAL( gotPreview( const KFileItem*, const QPixmap& )), + SLOT( slGotPreview( const KFileItem*, const QPixmap& ) )); + + m_pendingJobs.clear(); + + /* KIO::Jo result is called in any way: Success, Failed, Error, + * thus connecting the failed is not really necessary. + */ + // connect( job, SIGNAL( failed( const KFileItem* )), + // this, SLOT( slotFailed( const KFileItem* ) )); + + } + } +} + + + +void ThumbView::slGotPreview( const KFileItem* newFileItem, const QPixmap& 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( KIO::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 */ + } +} + + +QPixmap ThumbView::createPixmap( const QPixmap& preview ) const +{ + QImage ires = KImageEffect::unbalancedGradient( QSize( 2*m_thumbMargin+ preview.width(), + 2*m_thumbMargin+ preview.height()), + m_marginColor1, m_marginColor2, + KImageEffect::DiagonalGradient ); + + + QPixmap pixRet; + pixRet.convertFromImage( ires ); + QPainter 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() +{ + KConfig *cfg = KGlobal::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 ); + + +} |