//============================================== // copyright : (C) 2003-2005 by Will Stokes //============================================== // 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. //============================================== //Systemwide includes #include #include #include #include #include #include #include #include #include #include #include #include //Projectwide includes #include "addPhotosDialog.h" #include "../../config.h" #include "../../backend/tools/imageTools.h" #define MIN_WIDTH 240 #define MIN_HEIGHT 180 #define UPDATE_PREVIEW_DETAILS QEvent::User //================================ //Qt requires us to pass information for GUI posting from the worker thread to the //GUI thread via events as opposed to directly posting the events ourselves. in order //to update the file preview we'll construct custom UpdatePreviewEvents that contain //the updated preview image and file details. class UpdatePreviewEvent : public QCustomEvent { public: UpdatePreviewEvent( QImage image, QString details ) : QCustomEvent( UPDATE_PREVIEW_DETAILS ) { this->image = image; this->details = details; } //returns the preview image QImage getImage() const { return image; } //returns the file details string QString getDetails() const { return details; } private: QImage image; QString details; }; //================================ GeneratePreviewThread::GeneratePreviewThread( FilePreview* previewWidget ) { //we'll need to store a previewWidget handle to //posting update events when updates are //ready to be shown this->previewWidget = previewWidget; //by default worker thread isn't busy yet updating = false; queue = QString::null; } //================================ void GeneratePreviewThread::start( QString filename ) { //get lock lockingMutex.lock(); //if currently animating then append job to queue if(updating) { queue = filename; lockingMutex.unlock(); return; } //else set animating to true, actually initiate job else { updating = true; this->filename = filename; lockingMutex.unlock(); QThread::start(); } } //================================ void GeneratePreviewThread::run() { //since it is possible for another job //to be added to the queue while processing this one, it is necessary //to loop until the queue is empty while(true) { //------------------------------------------ //Get image type extension and convert to caps QString extension = QFileInfo(filename).extension(false).upper(); bool validExtension = ( (extension.compare("GIF") == 0) || (extension.compare("JPG") == 0) || (extension.compare("JPEG") == 0) || (extension.compare("PNG") == 0) || (extension.compare("XPM") == 0) ); //------------------------------------------ //Scale the image to fit nicely on the screen, aka < 300x225 QImage scaledImage; if( validExtension ) { scaleImage(filename, scaledImage, MIN_WIDTH, MIN_HEIGHT ); } //------------------------------------------ //Get image resolution QString imageRes = ""; if(validExtension) { QSize res; getImageSize( filename, res ); imageRes = QString("%1 x %2").arg(res.width()).arg(res.height()); } //------------------------------------------ //Determine file size and construct a nicely formatted size string QString fileSize = "?"; QFileInfo info; info.setFile( filename ); int sizeOnDisk = info.size(); if(sizeOnDisk < 1024) fileSize = QString("%1 Byte%2").arg(sizeOnDisk).arg( sizeOnDisk == 0 || sizeOnDisk > 1 ? "s" : ""); else if( sizeOnDisk/1024 < 1024) // fileSize = QString("%1 Kb").arg( ((float)*sizeOnDisk)/1024 ); fileSize = QString("%1 Kb").arg( ((float)((100*sizeOnDisk)/1024))/100 ); else if( sizeOnDisk/(1024*1024) < 1024) fileSize = QString("%1 Mb").arg( ((float)((100*sizeOnDisk)/(1024*1024)))/100 ); else fileSize = QString("%1 Gigs").arg( ((float)((100*sizeOnDisk)/(1024*1024*1024)))/100 ); //------------------------------------------ //Setup image details string QString fileDetails = QString("%1 %2, %3") .arg(imageRes) .arg(extension) .arg(fileSize); //------------------------------------------ //Post UPDATE_PREVIEW_DETAILS event UpdatePreviewEvent* upe = new UpdatePreviewEvent( scaledImage, fileDetails ); QApplication::postEvent( previewWidget, upe ); //------------------------------------------ //get lock lockingMutex.lock(); //if the queue is empty we're done! if( queue.isNull() ) { updating = false; lockingMutex.unlock(); return; } //clear queue and process pending job else { filename = queue; queue = QString::null; lockingMutex.unlock(); } } //end while(true) } //================================ FilePreview::FilePreview(QWidget* parent) : QWidget(parent) { //create widgets for display preview image and details filePreview = new QLabel( this ); fileDetails = new QLabel( this ); QGridLayout* grid = new QGridLayout( this, 4, 3 ); grid->setRowStretch( 0, 1 ); grid->addWidget( filePreview, 1, 1, Qt::AlignHCenter ); grid->addWidget( fileDetails, 2, 1, Qt::AlignHCenter ); grid->setRowStretch( 3, 1 ); grid->setColStretch( 0, 1 ); grid->setColStretch( 2, 1 ); //create a generator thread that will be used for actually generating //preview images and constructing details strings generatorThread = new GeneratePreviewThread(this); } //============================================== FilePreview::~FilePreview() { //make sure generator thread is done! generatorThread->wait(); delete generatorThread; generatorThread = NULL; } //============================================== QSize FilePreview::minimumSizeHint () const { QFontMetrics fm( font() ); return QSize(MIN_WIDTH, MIN_HEIGHT + 2*fm.height() ); } //============================================== void FilePreview::customEvent( QCustomEvent * e ) { //handle UpdatePrevewEvents that are sent from the worker thread //by update the preview image and details that are shown if ( e->type() == UPDATE_PREVIEW_DETAILS ) { UpdatePreviewEvent* upe = (UpdatePreviewEvent*)e; if( !upe->getImage().isNull() ) { QPixmap scaledPixmap; scaledPixmap.convertFromImage( upe->getImage() ); filePreview->setPixmap( scaledPixmap ); } fileDetails->setText( upe->getDetails() ); } } //============================================== void FilePreview::updatePreview( const QString& filename ) { //handle requests to update the preview information by asking //the generator thread to handle them. by using //an auxiallary thread we can process requests very quickly while //any current work being done to generate an image preview continues if( generatorThread != NULL) { generatorThread->start( filename ); } } //============================================== AddPhotosDialog::AddPhotosDialog(QString path, QWidget *parent, const char* name ) : QFileDialog(path, tr("Images") + " (*.gif *.jpg *.jpeg *.png *.xpm *.GIF *.JPG *.JPEG *.PNG *.XPM)", parent,name) { //setup filter filter and modes setMode( QFileDialog::ExistingFiles ); setViewMode( QFileDialog::List ); filePreview = new FilePreview(); setContentsPreviewEnabled( true ); setContentsPreview( filePreview, filePreview ); setPreviewMode( QFileDialog::Contents ); //create label and checkbox asking user if they want to //set image descriptions from filenames setDescriptions = new QCheckBox( tr("Use filenames for descriptions."), this ); setDescriptions->setChecked( false ); addWidgets( NULL, setDescriptions, NULL ); //set window description setCaption( tr("Add Photos") ); connect( this, SIGNAL( fileHighlighted(const QString&)), this, SLOT( updatePreview(const QString&)) ); } //============================================== QStringList AddPhotosDialog::getFilenames(bool& setDescriptionsBool) { if( exec() == QDialog::Accepted ) { setDescriptionsBool = setDescriptions->isChecked(); return selectedFiles(); } else { return QStringList(); } } //============================================== void AddPhotosDialog::updatePreview(const QString& filename) { filePreview->updatePreview( filename ); } //==============================================