diff --git a/imageclient.cpp b/imageclient.cpp new file mode 100644 index 00000000..5bb73b12 --- /dev/null +++ b/imageclient.cpp @@ -0,0 +1,7 @@ +#include "imageclient.h" +#include "imagemanager.h" + +ImageClient::~ImageClient() +{ + ImageManager::instance()->stop( this ); +} diff --git a/imageclient.h b/imageclient.h new file mode 100644 index 00000000..4b38cf64 --- /dev/null +++ b/imageclient.h @@ -0,0 +1,13 @@ +#ifndef IMAGECLIENT_H +#define IMAGECLIENT_H +class QPixmap; +class QString; + +class ImageClient { +public: + virtual ~ImageClient(); + virtual void pixmapLoaded( const QString& fileName, int width, int height, const QPixmap& ) = 0; +}; + +#endif /* IMAGECLIENT_H */ + diff --git a/imageconfig.cpp b/imageconfig.cpp new file mode 100644 index 00000000..e6a33ee1 --- /dev/null +++ b/imageconfig.cpp @@ -0,0 +1,103 @@ +#include "metainfo.h" +#include "imageconfig.h" +#include "listselect.h" +#include +#include +#include +#include +#include +#include +#include "imagemanager.h" + +ImageConfig::ImageConfig( QWidget* parent, const char* name ) + : ImageConfigUI( parent, name ) +{ + persons->setLabel( "Persons" ); + keywords->setLabel("Keywords"); + + MetaInfo* minfo = MetaInfo::instance(); + persons->insertStringList( minfo->items( "persons" ) ); + keywords->insertStringList( minfo->items( "keywords" ) ); +} + + +void ImageConfig::slotRevert() +{ + load(); +} + +void ImageConfig::slotPrev() +{ + save(); + _current--; + load(); +} + +void ImageConfig::slotNext() +{ + if ( _current != -1 ) { + save(); + } + _current++; + load(); +} + +void ImageConfig::slotDone() +{ + save(); +} + +void ImageConfig::load() +{ + ImageInfo* info = _list.at( _current ); + year->setValue( info->year() ); + month->setCurrentItem( info->month() ); + day->setValue( info->day() ); + hour->setValue( info->hour() ); + minute->setValue( info->minute() ); + quality->setCurrentItem( info->quality() ); + label->setText( info->label() ); + description->setText( info->description() ); + persons->setSelection( info->optionValue( persons->label() ) ); + keywords->setSelection( info->optionValue( keywords->label() ) ); + + nextBut->setEnabled( _current != (int)_list.count()-1 ); + prevBut->setEnabled( _current != 0 ); + if ( _preloadImageMap.contains( info->fileName() ) ) + preview->setPixmap( _preloadImageMap[ info->fileName() ] ); + else + preview->setText( "Loading
preview
" ); +} + +void ImageConfig::save() +{ + ImageInfo* info = _list.at( _current ); + info->setDate( year->value(), month->currentItem(), day->value() ); + info->setTime( hour->value(), minute->value() ); + info->setQuality( quality->currentItem() ); + info->setLabel( label->text() ); + info->setDescription( description->text() ); + QStringList list = persons->selection(); + info->setOption( persons->label(), persons->selection() ); + info->setOption( keywords->label(), keywords->selection() ); +} + +void ImageConfig::setImageInfo( ImageInfoList list ) +{ + _list = list; + _current = -1; + slotNext(); + _preloadImageMap.clear(); + for( QPtrListIterator it( list ); *it; ++it ) { + ImageManager::instance()->load( (*it)->fileName(), this, 256, 256, false ); + } +} + +void ImageConfig::pixmapLoaded( const QString& fileName, int, int, const QPixmap& pixmap ) +{ + if ( fileName == _list.at( _current )->fileName() ) + preview->setPixmap( pixmap ); + _preloadImageMap[ fileName ] = pixmap; +} + + diff --git a/imageconfig.h b/imageconfig.h new file mode 100644 index 00000000..5dc0d62e --- /dev/null +++ b/imageconfig.h @@ -0,0 +1,28 @@ +#ifndef IMAGECONFIG_H +#define IMAGECONFIG_H +#include "imageinfo.h" +#include "imageconfigui.h" +#include "imageclient.h" + +class ImageConfig :public ImageConfigUI, public ImageClient { +public: + ImageConfig( QWidget* parent, const char* name = 0 ); + void setImageInfo( ImageInfoList list ); + virtual void pixmapLoaded( const QString&, int, int, const QPixmap& ); + +protected: + void slotRevert(); + void slotPrev(); + void slotNext(); + void slotDone(); + void load(); + void save(); + +private: + ImageInfoList _list; + int _current; + QMap _preloadImageMap; +}; + +#endif /* IMAGECONFIG_H */ + diff --git a/imageconfigui.ui b/imageconfigui.ui new file mode 100644 index 00000000..3d415a79 --- /dev/null +++ b/imageconfigui.ui @@ -0,0 +1,697 @@ + +ImageConfigUI + + + ImageConfigUI + + + + 0 + 0 + 578 + 423 + + + + Image Description + + + + unnamed + + + + layout38 + + + + unnamed + + + + options + + + + tab + + + Metrics + + + + unnamed + + + + layout4 + + + + unnamed + + + + textLabel4 + + + NoFrame + + + Plain + + + Time: + + + + + hour + + + ??? + + + 23 + + + -1 + + + -1 + + + + + textLabel1 + + + : + + + + + minute + + + ??? + + + 59 + + + -1 + + + -1 + + + + + spacer2 + + + Horizontal + + + Expanding + + + + 31 + 20 + + + + + + + + layout5 + + + + unnamed + + + + textLabel2 + + + Date: + + + + + day + + + ??? + + + 31 + + + 0 + + + + + textLabel3 + + + - + + + + + + ??? + + + + + Jan + + + + + Feb + + + + + Mar + + + + + Apr + + + + + May + + + + + Jun + + + + + Jul + + + + + Aug + + + + + Sep + + + + + Oct + + + + + Nov + + + + + Dec + + + + month + + + + + textLabel3_2 + + + - + + + + + year + + + ??? + + + 2010 + + + 1899 + + + + + spacer3 + + + Horizontal + + + Expanding + + + + 21 + 20 + + + + + + + + layout7 + + + + unnamed + + + + textLabel2_2 + + + Quality: + + + + + + High + + + + + Medium + + + + + Low + + + + quality + + + + + spacer4 + + + Horizontal + + + Expanding + + + + 41 + 20 + + + + + + + + layout9 + + + + unnamed + + + + persons + + + + + keywords + + + + + + + + + tab + + + Description + + + + unnamed + + + + layout8 + + + + unnamed + + + + textLabel3_3 + + + Label: + + + + + label + + + + + + + layout9 + + + + unnamed + + + + spacer6 + + + Horizontal + + + Expanding + + + + 41 + 20 + + + + + + textLabel4_2 + + + Description: + + + + + spacer5_2 + + + Horizontal + + + Expanding + + + + 40 + 20 + + + + + + + + description + + + + + + + + layout37 + + + + unnamed + + + + preview + + + + 256 + 256 + + + + + 256 + 256 + + + + Image +Preview + + + AlignCenter + + + + + layout29 + + + + unnamed + + + + spacer28 + + + Horizontal + + + Minimum + + + + 21 + 20 + + + + + + prevBut + + + + 30 + 32767 + + + + < + + + false + + + + + nextBut + + + + 30 + 32767 + + + + > + + + false + + + + + spacer29 + + + Horizontal + + + Minimum + + + + 21 + 20 + + + + + + + + spacer30 + + + Vertical + + + Expanding + + + + 21 + 71 + + + + + + + + + + layout33 + + + + unnamed + + + + pushButton7 + + + Revert Edit for This Image + + + false + + + + + spacer27 + + + Horizontal + + + Expanding + + + + 156 + 20 + + + + + + pushButton19 + + + Close + + + false + + + + + + + + + ListSelect +
listselect.h
+ + -1 + -1 + + 0 + + 5 + 5 + 0 + 0 + + image0 +
+
+ + + 789c9d97c76e24490e86effd1442f3d65870d2451a0ce6206f5adeb4cc620f8c34f2553225b5a4c1befb46927fe6a1d4c0ccac4287fa8a0c26834193f5dbb785b3fd9d856fbf7d799ec9ecba5ea8afe469e15bf3727ffffeeffffcf1e797af49b2d0ffc7d142f2f55f5fbe1ecc16ea85dde9a4ed81290045faa77ca49cf4ab67ba1e3953969173651ab9d4fdf1c8a27c3872ad7c3c72d3b32c2a67c3f3444636fbef23eb7e590267e68fcc4656395d8dacf6d938ef97eaef289781cddebdb2846fccff44b989ba58e3411f3dc75158e6df1d3889539517ca49bf54fe43398d1dec4f46567f685fd9c539f43fc025f80c1c3ce8d93f28e77185f8bf0c6cfae4c0b5f9c3c6e5c0542a4b5876fe13708df39d2bd7716372aa8c93c4e4723bb2f977aadc26ceec4bdb7310e6b0bfab9c2445ecd49f1370694cebca65d260ffa6711a41aef14d24e94cee6be3348e0ae5e9c8765f07ca3e8db17f0f9c825794eb3489351fe9bb7217e4969f29388bd51ee9fda5715a19730696b852fea95c6471647ca95cf64bcf43e0cef4657b647bfe6acf213d6bb32f9a9f5992b5a64f9a8f990b6cf9ecc15d6cf9acf6b2da55a8a75cb973dee4b2d1b38b5c05de02434e87e01aacfb351df5bebddea74b5c67f9c795711ea15e357e2ecde358fb87efc0a867df8cac728a074e62e527706afb59f3cf6583be5c28bb3c35f693812d1fbdc6dbe57966fec932d899ffa4f7e38adca17e34beaeca4b9c271a18f1d5fc739257b0d70d9c68be7bed7fcee7827ab91cd8e4740cf6b19d4ffb87ab07b9bf516e72d4a3ac821b9cef6e60d84f47367ded2fae1d9f7704f6163f79000ffde27660f387ed3c5dbf54dfeebb0bf6acbe6b706bfa5ef32f8f8b18f5bf0f463f20edb77956a4e68fbc80b3c4facd163847be6bfde72ec86dbeac82715fbc0286be683cf3bc081d44f7df80d344fb0b6b7de64581fb916765297263d6fccd9b7ee97e566e8b06cfdb1f59cf2bda2f8bbc2c11df0c5c41aefdb328ca22b1feb0012eedbce24736f91b18fb796d64eb87cb60817dcdafb07d906bfd14d22f659d8785ef97b2f6d7b2df6ef1d6fb2babaac6f92e8c25b27ecc1f239b7f3a5fca5a8678ea7c2d9bc0e6ef1238b3fca2c7814d5f4cbf9334b5f833384bed3c7a5f5514f4adbe2ec07962e7590457f06f6f649b977afe2a16f42f5e070bfc591e18f7a5f1a812a9e0df39d827da4fbd8c6ccfd7785569bf945f953371a9d5a3de67950bfa2d75c63eb2fb15bdcfaaf011e6c7263846bf8f4636f91618f3c7d3c0f047fb69550efab2074e12bd6fd27957553ec33c5b043bcc7f9d0795f818fdb501a3df8ae673e507ff69021eea37063bc45ffb73550736ff36c0a84f3e0317a86f8b5f139e6ffe1f821de6d9127898ff3be00a7c3ab2cd03e3d697560ff40016e47b3bb2e96bbd559d8f32bbff4b7065f193042c98873acf240af6adbf3c83c5f2954bb0c7bcd77c107d81d2fdebc63e33ff640aaecc9e1c8151df5c803dfaadc64b52dfa03ed64636ffb4ff4b2867f453ede7227586fcd3f9264ded91fffafe236d5da37fe83c91ceb7b9e5b7f6731ff92ed7f747d6fbf7e185cf58347e3e6932e47b3430fa893edf87d795c2fc3f003b67f5f902cec17abfde0dfa3c053b3c5ffb832fc2eb8fddcf233887fc195c801f46b6f3cdc02558fba72f7d2d1a7f7e321ee58fe0cad86bbff24dd3e2fe74fef836b0c6eb60d62fa6bf5e07b3419f853dd7dc70fb8bd5f1255fd90ed30f9f3c5ff30ddff21ddff384a7fcc08ffcc4cf61cdf8855ff9e79c7e1db4dff89d3f7891977899577895d7789d377893b7f83b6fcfe937bc13b477798ff7f9800ff928ac633ee11f7cca6761d7f99c7e1b3cb908da11c7413be1943376e153cc39175c72f549ff9e17c31744429e6a6aa8a58e2ee98aaee9866e7f617fc24b7417a4f734a1293dd0233dd133cd8285177aa5f9f3b63ca5377aa78f607b919668995682e62aadd17ab0b1419b9ff41f682b48bed336edd02eed05ed7d5ea3033a0cdf1ed1f127fd273ae123fa41a774a6b685cee982228a837e42e927fd47caf8985c38651eb40b2ac38e4a5842658a97fab33fd248cb87d2c9a55cc9b5dcc82d1fc99ddccb44a6f230af2f8ff224cf417f262ff22a3fe54ddee543166549966545567f617f4dd66523dc6b2c9bb225df655b7682f692ecca9eeccfe97772c09b722847722c27c1f3eb70f66bf9116c9fca999ccbc59cfe25bf4a143a5ef8992561324a78bd92522acf9ebc78efe7cf7b153276db37bef59dbff457fedadff85b7f27abfede4ffcd4cf9ff76faeff4fffefeff8c7f5fedfdfbffc0fa355c495 + + + + + pushButton7 + clicked() + ImageConfigUI + slotRevert() + + + prevBut + clicked() + ImageConfigUI + slotPrev() + + + nextBut + clicked() + ImageConfigUI + slotNext() + + + pushButton19 + clicked() + ImageConfigUI + slotDone() + + + pushButton19 + clicked() + ImageConfigUI + close() + + + + slotRevert() + slotPrev() + slotNext() + slotDone() + + + + + listselect.h + listselect.h + +
diff --git a/imageinfo.cpp b/imageinfo.cpp new file mode 100644 index 00000000..ca0ded2d --- /dev/null +++ b/imageinfo.cpp @@ -0,0 +1,183 @@ +#include "imageinfo.h" +#include +#include +#include +#include "options.h" + + +ImageInfo::ImageInfo() +{ +} + +ImageInfo::ImageInfo( const QString& fileName, QDomElement elm ) + : _fileName( fileName ) +{ + QFileInfo fi( fileName ); + _label = elm.attribute( "label", fi.baseName() ); + _description = elm.attribute( "description" ); + + int year = -1, month = -1, day = -1, hour = -1, minute = -1; + if ( Options::instance()->trustFileTimeStamps() ) { + QDate date = fi.created().date(); + QTime time = fi.created().time(); + year = date.year(); + month = date.month(); + day = date.day(); + hour = time.hour(); + minute = time.minute(); + } + _year = elm.attribute( "year", QString::number(year) ).toInt(); + _month = elm.attribute( "month", QString::number(month) ).toInt(); + _day = elm.attribute( "day", QString::number(day) ).toInt(); + _hour = elm.attribute( "hour", QString::number(hour) ).toInt(); + _minute = elm.attribute( "minute", QString::number(minute) ).toInt(); + _quality = elm.attribute( "quality" ).toInt(); + for ( QDomNode nodeOption = elm.firstChild(); !nodeOption.isNull(); nodeOption = nodeOption.nextSibling() ) { + if ( nodeOption.isElement() ) { + QDomElement elmOption = nodeOption.toElement(); + Q_ASSERT( elmOption.tagName() == "option" ); + QString name = elmOption.attribute( "name" ); + if ( !name.isNull() ) { + for ( QDomNode nodeValue = elmOption.firstChild(); !nodeValue.isNull(); nodeValue = nodeValue.nextSibling() ) { + if ( nodeValue.isElement() ) { + QDomElement elmValue = nodeValue.toElement(); + Q_ASSERT( elmValue.tagName() == "value" ); + QString value = elmValue.attribute( "value" ); + if ( !value.isNull() ) { + _options[name].append( value ); + } + } + } + } + } + } +} + +void ImageInfo::setLabel( const QString& desc ) +{ + _label = desc; +} + +QString ImageInfo::label() const +{ + return _label; +} + +void ImageInfo::setDescription( const QString& desc ) +{ + _description = desc; +} + +QString ImageInfo::description() const +{ + return _description; +} + +void ImageInfo::setDate( int year, int month, int day ) +{ + _year = year; + _month = month; + _day = day; +} + +int ImageInfo::year() const +{ + return _year; +} + +int ImageInfo::month() const +{ + return _month; +} + +int ImageInfo::day() const +{ + return _day; +} + +void ImageInfo::setTime( int hour, int minute ) +{ + _hour = hour; + _minute = minute; +} + +int ImageInfo::hour() const +{ + return _hour; +} + +int ImageInfo::minute() const +{ + return _minute; +} + +void ImageInfo::setOption( const QString& key, const QStringList& value ) +{ + _options[key] = value; +} + +QStringList ImageInfo::optionValue( const QString& key ) const +{ + return _options[key]; +} + +void ImageInfo::setQuality( int quality ) +{ + _quality = quality; +} + +int ImageInfo::quality() const +{ + return _quality; +} + +QString ImageInfo::fileName() +{ + return _fileName; +} + +QPixmap ImageInfo::pixmap( int width, int height ) +{ + QImage img( _fileName ); + QPixmap pix; + img = img.smoothScale( width, height, QImage::ScaleMin ); + pix.convertFromImage( img ); + return pix; +} + +QDomElement ImageInfo::save( QDomDocument& doc ) +{ + QDomElement elm = doc.createElement( "Image" ); + elm.setAttribute( "file", QFileInfo( _fileName ).fileName() ); + elm.setAttribute( "label", _label ); + elm.setAttribute( "description", _description ); + elm.setAttribute( "year", _year ); + elm.setAttribute( "month", _month ); + elm.setAttribute( "day", _day ); + elm.setAttribute( "hour", _hour ); + elm.setAttribute( "minute", _minute ); + elm.setAttribute( "quality", _quality ); + for( QMapIterator it= _options.begin(); it != _options.end(); ++it ) { + QDomElement opt = doc.createElement( "option" ); + opt.setAttribute( "name", it.key() ); + elm.appendChild( opt ); + QStringList list = it.data(); + for( QStringList::Iterator it2 = list.begin(); it2 != list.end(); ++it2 ) { + QDomElement val = doc.createElement( "value" ); + val.setAttribute( "value", *it2 ); + opt.appendChild( val ); + } + } + return elm; +} + + + + + + + + + + + diff --git a/imageinfo.h b/imageinfo.h new file mode 100644 index 00000000..84a04e48 --- /dev/null +++ b/imageinfo.h @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef IMAGEINFO_H +#define IMAGEINFO_H + +class ImageInfo :public QObject { + +public: + ImageInfo(); + ImageInfo( const QString& fileName, QDomElement elm ); + + QString fileName(); + + void setLabel( const QString& ); + QString label() const; + + void setDescription( const QString& ); + QString description() const; + + void setDate( int year, int month, int day ); + int year() const; + int month() const; + int day() const; + + void setTime( int hour, int minute ); + int hour() const; + int minute() const; + + void setQuality( int ); + int quality() const; + + void setOption( const QString& key, const QStringList& value ); + QStringList optionValue( const QString& key ) const; + + QPixmap pixmap( int width, int height ); + QDomElement save( QDomDocument& doc ); + +private: + // This string is accessed on several threads, so we need to make it a deep copy! + QDeepCopy _fileName; + QString _label; + QString _description; + int _year, _month, _day, _hour, _minute; + int _quality; + QMap _options; +}; + +typedef QPtrList ImageInfoList; + +#endif /* IMAGEINFO_H */ + diff --git a/imageloader.cpp b/imageloader.cpp new file mode 100644 index 00000000..dfe42615 --- /dev/null +++ b/imageloader.cpp @@ -0,0 +1,161 @@ +#include "imageloader.h" +#include +#include "imagemanager.h" +#include "thumbnail.h" +#include +#include +#include "options.h" +#include + +extern "C" { +#include +#include +} + +ImageLoader::ImageLoader( QWaitCondition* sleeper ) + : _sleeper( sleeper ) +{ +} + +void ImageLoader::run() +{ + while ( true ) { + LoadInfo li = ImageManager::instance()->next(); + + if ( !li.isNull() ) { + + QString cacheDir = QFileInfo( li.fileName() ).dirPath() + "/ThumbNails"; + QString cacheFile = cacheDir + QString("/%1x%2-%3").arg(li.width()).arg(li.height()).arg( QFileInfo( li.fileName() ).baseName() ); + if ( li.width() != -1 ) { + // Try to load thumbernail from cache + QImage img; + if ( QFileInfo( cacheFile ).exists() ) { + if ( img.load( cacheFile ) ) { + li.setImage( img ); + ImageEvent* iew = new ImageEvent( li ); + QApplication::postEvent( ImageManager::instance(), iew ); + continue; // Done. + } + } + } + + QImage orig; + if (!isJPEG(li) || !loadJPEG(&orig, li)) { + // Fetch the original unscaled image using normal Qt image loading + if ( !li.image().isNull() ) { + orig = li.image(); + } + else { + if ( orig.load( li.fileName() ) ) { + // hmmmm sending this information will fill the cache very fast + // LoadInfo liOrigImage( li.fileName(), -1, -1, orig ); + // ImageEvent* iew = new ImageEvent( liOrigImage ); + // QApplication::postEvent( ImageManager::instance(), iew ); + } + } + } + + + // If we are looking for a scaled version, then scale + if ( li.width() != -1 ) { + QImage scaled = orig.scale( li.width(), li.height(), QImage::ScaleMin ); + + // Save thumbnail to disk + if ( Options::instance()->cacheThumbNails() ) { + if ( ! QDir( cacheDir ).exists() ) { + QDir().mkdir( cacheDir, true ); + } + scaled.save( cacheFile, "JPEG" ); + } + + li.setImage( scaled ); + ImageEvent* iew = new ImageEvent( li ); + QApplication::postEvent( ImageManager::instance(), iew ); + } + } + else + _sleeper->wait(); + sleep( 1 ); + } +} + + +bool ImageLoader::isJPEG( const LoadInfo& li ) +{ + QString format=QImageIO::imageFormat( li.fileName() ); + return format=="JPEG"; +} + +// Fudged Fast JPEG decoding code from GWENVIEW (picked out out digikam) + +bool ImageLoader::loadJPEG(QImage* image, const LoadInfo& li ) +{ + FILE* inputFile=fopen(li.fileName().data(), "rb"); + if(!inputFile) return false; + + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, inputFile); + jpeg_read_header(&cinfo, TRUE); + + int size = li.width(); + int imgSize = QMAX(cinfo.image_width, cinfo.image_height); + + int scale=1; + while(size*scale*2<=imgSize) { + scale*=2; + } + if(scale>8) scale=8; + + cinfo.scale_num=1; + cinfo.scale_denom=scale; + + // Create QImage + jpeg_start_decompress(&cinfo); + + switch(cinfo.output_components) { + case 3: + case 4: + image->create( cinfo.output_width, cinfo.output_height, 32 ); + break; + case 1: // B&W image + image->create( cinfo.output_width, cinfo.output_height, 8, 256 ); + for (int i=0; i<256; i++) + image->setColor(i, qRgb(i,i,i)); + break; + default: + return false; + } + + uchar** lines = image->jumpTable(); + while (cinfo.output_scanline < cinfo.output_height) + jpeg_read_scanlines(&cinfo, lines + cinfo.output_scanline, + cinfo.output_height); + jpeg_finish_decompress(&cinfo); + + // Expand 24->32 bpp + if ( cinfo.output_components == 3 ) { + for (uint j=0; jscanLine(j) + cinfo.output_width*3; + QRgb *out = (QRgb*)( image->scanLine(j) ); + + for (uint i=cinfo.output_width; i--; ) { + in-=3; + out[i] = qRgb(in[0], in[1], in[2]); + } + } + } + + int newMax = QMAX(cinfo.output_width, cinfo.output_height); + int newx = size*cinfo.output_width / newMax; + int newy = size*cinfo.output_height / newMax; + + //image=image.smoothScale( newx, newy); + + jpeg_destroy_decompress(&cinfo); + fclose(inputFile); + + return true; +} diff --git a/imageloader.h b/imageloader.h new file mode 100644 index 00000000..70d9a5b1 --- /dev/null +++ b/imageloader.h @@ -0,0 +1,21 @@ +#ifndef IMAGELOADER_H +#define IMAGELOADER_H +#include +class ImageManager; +class QWaitCondition; +class LoadInfo; + +class ImageLoader :public QThread { +public: + ImageLoader( QWaitCondition* sleeper ); +protected: + virtual void run(); + bool isJPEG( const LoadInfo& li ); + bool loadJPEG(QImage* image, const LoadInfo& li ); + +private: + QWaitCondition* _sleeper; +}; + +#endif /* IMAGELOADER_H */ + diff --git a/imagemanager.cpp b/imagemanager.cpp new file mode 100644 index 00000000..7345d5a9 --- /dev/null +++ b/imagemanager.cpp @@ -0,0 +1,207 @@ +#include "imagemanager.h" +#include "imageloader.h" +#include "options.h" +#include +#include "imageclient.h" +#include + +ImageManager* ImageManager::_instance = 0; + +ImageManager::ImageManager() +{ +} + +// We need this as a separate method as the _instance variable will otherwise not be initialized corrected before the thread starts. +void ImageManager::init() +{ + _sleepers = new QWaitCondition(); // Is it necessary to load this using new? + _lock = new QMutex(); // necessary with new? + for ( int i = 0; i < Options::instance()->numThreads(); ++i ) { + ImageLoader* imageLoader = new ImageLoader( _sleepers ); + _imageLoaders.append( imageLoader ); + imageLoader->start(); + } +} + +void ImageManager::load( const QString& fileName, ImageClient* client, int width, int height, bool cache ) +{ + QString key = QString("%1-%2x%3").arg( fileName ).arg( width ).arg( height ); + QPixmap* pixmap = QPixmapCache::find( key ); + if ( pixmap ) { + if ( client ) + client->pixmapLoaded( fileName, width, height, *pixmap ); + } + else { + _lock->lock(); + QPixmap pix; + if ( width == -1 && height == -1 ) { + key = QString("%1-%2x%3").arg( fileName ).arg( -1 ).arg( -1 ); + QPixmap* pp = QPixmapCache::find( key ); + if ( pp ) + pix = *pp; + } + LoadInfo li( fileName, width, height, pix, client ); + li.setCache( cache ); + _loadList.append( li ); + _lock->unlock(); + if ( client ) + _clientMap.insert( li, client ); + _sleepers->wakeOne(); + } +} + +LoadInfo ImageManager::next() +{ + _lock->lock(); + LoadInfo info; + if ( _loadList.count() != 0 ) { + info = _loadList.first(); + _loadList.pop_front(); + } + _lock->unlock(); + return info; +} + +LoadInfo::LoadInfo() : _null( true ), _cache( true ), _client( 0 ) +{ +} + +LoadInfo::LoadInfo( const QString& fileName, int width, int height, QPixmap image, ImageClient* client ) + : _null( false ), _fileName( fileName ), _width( width ), _height( height ), _image( image ), _cache( true ), _client( client ) +{ +} + +void ImageManager::customEvent( QCustomEvent* ev ) +{ + if ( ev->type() == 1001 ) { + ImageEvent* iev = dynamic_cast( ev ); + if ( !iev ) { + Q_ASSERT( iev ); + return; + } + + LoadInfo li = iev->loadInfo(); + QString key = QString("%1-%2x%3").arg( li.fileName() ).arg( li.width() ).arg( li.height() ); + QPixmap pixmap( li.image() ); + if ( li.cache() ) { + qDebug("Cacheing %s %dx%d", li.fileName().latin1(), li.width(), li.height()); + QPixmapCache::insert( key, pixmap ); + } + if ( _clientMap.contains( li ) ) { + // If it is not in the map, then it has been deleted since the request. + ImageClient* client = _clientMap[li]; + client->pixmapLoaded( li.fileName(), li.width(), li.height(), pixmap ); + } + } +} + +ImageEvent::ImageEvent( LoadInfo info ) + : QCustomEvent( 1001 ), _info( info ) +{ +} + +LoadInfo ImageEvent::loadInfo() +{ + return _info; +} + +bool LoadInfo::isNull() const +{ + return _null; +} + +QString LoadInfo::fileName() const +{ + return const_cast(this)->_fileName; +} + +int LoadInfo::width() const +{ + return _width; +} + +int LoadInfo::height() const +{ + return _height; +} + +QPixmap LoadInfo::image() +{ + return _image; +} + +void LoadInfo::setImage( const QPixmap& image ) +{ + _image = image; +} + +ImageManager* ImageManager::instance() +{ + if ( !_instance ) { + _instance = new ImageManager; + _instance->init(); + } + + return _instance; +} + +bool LoadInfo::operator<( const LoadInfo& other ) const +{ + LoadInfo& o = const_cast( other ); + LoadInfo& t = const_cast( *this ); + + if ( t._fileName != o._fileName ) + return t._fileName < o._fileName; + else if ( t._width != o._width ) + return t._width < o._width; + else + return t._height < o._height; +} + +bool LoadInfo::operator==( const LoadInfo& other ) const +{ + // Compare all atributes but the pixmap. + LoadInfo& t = const_cast( *this ); + LoadInfo& o = const_cast( other ); + return ( t._null == o._null && t._fileName == o._fileName && t._width == o._width && t._height == o._height ); +} + + +void LoadInfo::setCache( bool b ) +{ + _cache = b; +} + +bool LoadInfo::cache() const +{ + return _cache; +} + +void ImageManager::stop( ImageClient* client ) +{ + // remove from active map + for( QMapIterator it= _clientMap.begin(); it != _clientMap.end(); ) { + LoadInfo key = it.key(); + ImageClient* data = it.data(); + ++it; // We need to increase it before removing the element. + if ( data == client ) + _clientMap.remove( key ); + } + + // remove from pending map. + _lock->lock(); + for( QValueList::Iterator it = _loadList.begin(); it != _loadList.end(); ) { + LoadInfo li = *it; + ++it; + if ( li.client() == client ) + _loadList.remove( li ); + } + _lock->unlock(); +} + +ImageClient* LoadInfo::client() +{ + return _client; +} + + diff --git a/imagemanager.h b/imagemanager.h new file mode 100644 index 00000000..2fd46d2a --- /dev/null +++ b/imagemanager.h @@ -0,0 +1,77 @@ +#ifndef IMAGEMANAGER_H +#define IMAGEMANAGER_H +#include "thumbnail.h" +#include "imageloader.h" +#include +#include +#include +#include +#include +#include +#include +class ImageClient; + +class LoadInfo { +public: + LoadInfo(); + LoadInfo( const QString& fileName, int width, int heigth, QPixmap image, ImageClient* client ); + + bool isNull() const; + QString fileName() const; + int width() const; + int height() const; + QPixmap image(); + void setImage( const QPixmap& image ); + void setCache( bool ); + bool cache() const; + ImageClient* client(); + + bool operator<( const LoadInfo& other ) const; + bool operator==( const LoadInfo& other ) const; + +private: + bool _null; + QDeepCopy _fileName; + int _width; + int _height; + QDeepCopy _image; + bool _cache; + ImageClient* _client; +}; + +class ImageEvent :public QCustomEvent { +public: + ImageEvent( LoadInfo info ); + LoadInfo loadInfo(); + +private: + LoadInfo _info; +}; + +// This class needs to inherit QObject to be capable of receiving events. +class ImageManager :public QObject { + Q_OBJECT + +public: + void load( const QString& fileName, ImageClient* client, int width = -1, int height = -1, bool cache = true ); + LoadInfo next(); + static ImageManager* instance(); + void stop( ImageClient* ); + +protected: + virtual void customEvent( QCustomEvent* ev ); + +private: + ImageManager(); + void init(); + static ImageManager* _instance; + + QPtrList _imageLoaders; + QValueList _loadList; + QWaitCondition* _sleepers; + QMutex* _lock; + QMap _clientMap; +}; + +#endif /* IMAGEMANAGER_H */ + diff --git a/kpalbum.pro b/kpalbum.pro new file mode 100644 index 00000000..451a143a --- /dev/null +++ b/kpalbum.pro @@ -0,0 +1,46 @@ +SOURCES += main.cpp \ + thumbnailview.cpp \ + thumbnail.cpp \ + imagemanager.cpp \ + imageloader.cpp \ + options.cpp \ + imageinfo.cpp \ + metainfo.cpp \ + viewer.cpp \ + listselect.cpp \ + mainview.cpp \ + imageconfig.cpp \ + imageclient.cpp + +HEADERS += thumbnailview.h \ + thumbnail.h \ + imagemanager.h \ + imageloader.h \ + options.h \ + imageinfo.h \ + metainfo.h \ + viewer.h \ + listselect.h \ + mainview.h \ + imageconfig.h \ + imageclient.h + +FORMS = mainviewui.ui \ + optionsdialog.ui \ + imageconfigui.ui + +unix { + MOC_DIR = .moc + OBJECTS_DIR = .obj + UI_DIR = .ui +} +!unix { + MOC_DIR = _moc + OBJECTS_DIR = _obj + UI_DIR = _ui +} + +TEMPLATE =app +INCLUDEPATH += . +LANGUAGE = C++ +LIBS += -ljpeg diff --git a/listselect.cpp b/listselect.cpp new file mode 100644 index 00000000..ecc66345 --- /dev/null +++ b/listselect.cpp @@ -0,0 +1,82 @@ +#include "listselect.h" +#include +#include +#include +#include +#include "metainfo.h" + +ListSelect::ListSelect( QWidget* parent, const char* name ) + : QWidget( parent, name ) +{ + QVBoxLayout* layout = new QVBoxLayout( this, 6 ); + + _label = new QLabel( this ); + _label->setAlignment( AlignCenter ); + layout->addWidget( _label ); + + _lineEdit = new QLineEdit( this ); + layout->addWidget( _lineEdit ); + + _listBox = new QListBox( this ); + _listBox->setSelectionMode( QListBox::Multi ); + layout->addWidget( _listBox ); + + connect( _lineEdit, SIGNAL( returnPressed() ), this, SLOT( slotReturn() ) ); +} + +void ListSelect::setLabel( const QString& label ) +{ + _textLabel = label; + _label->setText( label ); +} + +void ListSelect::slotReturn() +{ + QString txt = _lineEdit->text(); + if ( txt == "" ) + return; + + QListBoxItem* item = _listBox->findItem( txt, ExactMatch ); + if ( !item ) { + item = new QListBoxText( _listBox, txt ); + } + MetaInfo* minfo = MetaInfo::instance(); + minfo->addItem( _textLabel, txt); + + _listBox->setSelected( item, true ); + _lineEdit->clear(); +} + +void ListSelect::insertStringList( const QStringList& list ) +{ + _listBox->insertStringList( list ); +} + +QString ListSelect::label() const +{ + return _textLabel; +} + +void ListSelect::setSelection( const QStringList& list ) +{ + _listBox->clearSelection(); + + for( QStringList::ConstIterator it = list.begin(); it != list.end(); ++it ) { + QListBoxItem* item = _listBox->findItem( *it, ExactMatch ); + if ( !item ) { + _listBox->insertItem( *it ); + item = _listBox->findItem( *it, ExactMatch ); + } + _listBox->setSelected( item, true ); + } +} + +QStringList ListSelect::selection() +{ + QStringList list; + for ( QListBoxItem* item = _listBox->firstItem(); item; item = item->next() ) { + if ( item->isSelected() ) + list.append( item->text() ); + } + return list; +} diff --git a/listselect.h b/listselect.h new file mode 100644 index 00000000..9c940419 --- /dev/null +++ b/listselect.h @@ -0,0 +1,31 @@ +#ifndef LISTSELECT_H +#define LISTSELECT_H + +#include +class QLineEdit; +class QListBox; +class QLabel; + +class ListSelect :public QWidget { + Q_OBJECT + +public: + ListSelect( QWidget* parent, const char* name = 0 ); + void setLabel( const QString& label ); + QString label() const; + void insertStringList( const QStringList& list ); + void setSelection( const QStringList& list ); + QStringList selection(); + +protected slots: + void slotReturn(); + +private: + QLabel* _label; + QString _textLabel; + QLineEdit* _lineEdit; + QListBox* _listBox; +}; + +#endif /* LISTSELECT_H */ + diff --git a/main.cpp b/main.cpp new file mode 100644 index 00000000..9082ecdb --- /dev/null +++ b/main.cpp @@ -0,0 +1,16 @@ +#include +#include "thumbnailview.h" +#include "options.h" +#include +#include "mainview.h" + +int main( int argc, char** argv ) { + QApplication app( argc, argv ); + + MainView* view = new MainView( 0, "view" ); + view->resize(800, 600); + view->show(); + + QObject::connect( qApp, SIGNAL( lastWindowClosed() ), qApp, SLOT( quit() ) ); + return app.exec(); +} diff --git a/mainview.cpp b/mainview.cpp new file mode 100644 index 00000000..c4ddf68f --- /dev/null +++ b/mainview.cpp @@ -0,0 +1,143 @@ +#include "mainview.h" +#include +#include +#include +#include "thumbnailview.h" +#include "thumbnail.h" +#include "imageconfig.h" +#include +#include +#include +#include + +MainView::MainView( QWidget* parent, const char* name ) + :MainViewUI( parent, name ) +{ + _optionsDialog = 0; + _imageConfigure = 0; + + QString directory = "/home/blackie/Images"; + + // Load the information from the XML file. + QString xmlFile = directory + "/index.xml"; + QMap map; + if ( QFileInfo( xmlFile ).exists() ) { + QFile file( xmlFile ); + if ( ! file.open( IO_ReadOnly ) ) { + qWarning( "Couldn't read file %s", xmlFile.latin1() ); + } + else { + QDomDocument doc; + doc.setContent( &file ); + for ( QDomNode node = doc.documentElement().firstChild(); !node.isNull(); node = node.nextSibling() ) { + QDomElement elm; + if ( node.isElement() ) + elm = node.toElement(); + else + continue; + + QString fileName = elm.attribute( "file" ); + if ( fileName.isNull() ) { + qWarning( "Element did not contain a file attirbute" ); + } + else { + map[fileName] = elm; + } + } + } + } + + QDir dir( directory ); + QStringList dirList = dir.entryList(); + for( QStringList::Iterator it = dirList.begin(); it != dirList.end(); ++it ) { + if ( (*it) != "." && (*it) != ".." && + ( (*it).endsWith( ".jpg" ) || (*it).endsWith( ".jpegf" ) || (*it).endsWith( ".png" ) || (*it).endsWith( ".tiff" ) || (*it).endsWith( ".gif" ) ) ) { + ImageInfo* info = new ImageInfo( directory + "/" + *it, map[*it] ); + _images.append(info); + connect( info, SIGNAL( destroyed( QObject* ) ), this, SLOT( imageDeleted( QObject* ) ) ); + } + } + thumbNailView->load( &_images ); +} + +void MainView::slotExit() +{ + qApp->quit(); +} + +void MainView::slotOptions() +{ + if ( ! _optionsDialog ) { + _optionsDialog = new OptionsDialog( this ); + connect( _optionsDialog, SIGNAL( changed() ), thumbNailView, SLOT( reload() ) ); + } + _optionsDialog->show(); +} + + +void MainView::slotConfigureAllImages() +{ +} + + +void MainView::slotConfigureImagesOneAtATime() +{ + if ( ! _imageConfigure ) { + _imageConfigure = new ImageConfig( this, "_imageConfigure" ); + } + + ImageInfoList list; + for ( QIconViewItem *item = thumbNailView->firstItem(); item; item = item->nextItem() ) { + if ( item->isSelected() ) { + ThumbNail* tn = dynamic_cast( item ); + Q_ASSERT( tn ); + list.append( tn->imageInfo() ); + } + } + if ( list.count() == 0 ) { + QMessageBox::warning( this, tr("No Selection"), tr("No item selected.") ); + } + else { + _imageConfigure->setImageInfo( list ); + _imageConfigure->exec(); + save(); + } +} + +void MainView::save() +{ + QMap docs; + for( QPtrListIterator it( _images ); *it; ++it ) { + QString fileName = (*it)->fileName(); + QString outputFile = QFileInfo( fileName ).dirPath() + "/index.xml"; + if ( !docs.contains( outputFile ) ) { + QDomDocument tmp; + tmp.setContent( QString("") ); + docs[outputFile] = tmp; + } + QDomDocument doc = docs[outputFile]; + QDomElement elm = doc.documentElement(); + elm.appendChild( (*it)->save( doc ) ); + } + + for( QMapIterator it= docs.begin(); it != docs.end(); ++it ) { + QFile out( it.key() ); + + if ( !out.open( IO_WriteOnly ) ) { + qWarning( "Could not open file '%s'", it.key().latin1() ); + } + else { + QTextStream stream( &out ); + stream << it.data().toString(); + out.close(); + } + } +} + +void MainView::imageDeleted( QObject* obj ) +{ + // It needs to be a reinterpret_cast below as the destruction of 'obj' + // already have started, so calling dynamic_cast crashes the program. + ImageInfo* image = reinterpret_cast( obj ); + _images.removeRef( image ); +} diff --git a/mainview.h b/mainview.h new file mode 100644 index 00000000..f7bc6b2e --- /dev/null +++ b/mainview.h @@ -0,0 +1,33 @@ +#ifndef MAINVIEW_H +#define MAINVIEW_H +class OptionsDialog; +class ImageConfig; +#include "imageinfo.h" +#include "mainviewui.h" + +class MainView :public MainViewUI +{ + Q_OBJECT + +public: + MainView( QWidget* parent, const char* name = 0 ); + +protected: + void slotExit(); + void slotOptions(); + void slotConfigureAllImages(); + void slotConfigureImagesOneAtATime(); + void save(); + +protected slots: + void imageDeleted( QObject* ); + +private: + OptionsDialog* _optionsDialog; + ImageConfig* _imageConfigure; + ImageInfoList _images; +}; + + +#endif /* MAINVIEW_H */ + diff --git a/mainviewui.ui b/mainviewui.ui new file mode 100644 index 00000000..3663437a --- /dev/null +++ b/mainviewui.ui @@ -0,0 +1,185 @@ + +MainViewUI + + + MainViewUI + + + + 0 + 0 + 297 + 438 + + + + K Photo Album + + + + unnamed + + + + thumbNailView + + + + + + + menubar + + + + + + + + + + + + + + + + ThumbNailView +
thumbnailview.h
+ + -1 + -1 + + 0 + + 5 + 5 + 0 + 0 + + image0 +
+
+ + + + Exit + + + &Exit + + + + + Options + + + &Options + + + + + Search + + + &Search + + + + + Export + + + Export to &HTML + + + + + ViewSelected + + + &View Images + + + + + ImageProperties + + + Image Properties + + + true + + + + DescribeAll + + + false + + + All Simultaniously + + + Configure all selected images simultaniously + + + + + DescribeOneAtATime + + + false + + + One at a time + + + Configure all selected images one at a time + + + + + + + 789c9d97c76e24490e86effd1442f3d65870d2451a0ce6206f5adeb4cc620f8c34f2553225b5a4c1befb46927fe6a1d4c0ccac4287fa8a0c26834193f5dbb785b3fd9d856fbf7d799ec9ecba5ea8afe469e15bf3727ffffeeffffcf1e797af49b2d0ffc7d142f2f55f5fbe1ecc16ea85dde9a4ed81290045faa77ca49cf4ab67ba1e3953969173651ab9d4fdf1c8a27c3872ad7c3c72d3b32c2a67c3f3444636fbef23eb7e590267e68fcc4656395d8dacf6d938ef97eaef289781cddebdb2846fccff44b989ba58e3411f3dc75158e6df1d3889539517ca49bf54fe43398d1dec4f46567f685fd9c539f43fc025f80c1c3ce8d93f28e77185f8bf0c6cfae4c0b5f9c3c6e5c0542a4b5876fe13708df39d2bd7716372aa8c93c4e4723bb2f977aadc26ceec4bdb7310e6b0bfab9c2445ecd49f1370694cebca65d260ffa6711a41aef14d24e94cee6be3348e0ae5e9c8765f07ca3e8db17f0f9c825794eb3489351fe9bb7217e4969f29388bd51ee9fda5715a19730696b852fea95c6471647ca95cf64bcf43e0cef4657b647bfe6acf213d6bb32f9a9f5992b5a64f9a8f990b6cf9ecc15d6cf9acf6b2da55a8a75cb973dee4b2d1b38b5c05de02434e87e01aacfb351df5bebddea74b5c67f9c795711ea15e357e2ecde358fb87efc0a867df8cac728a074e62e527706afb59f3cf6583be5c28bb3c35f693812d1fbdc6dbe57966fec932d899ffa4f7e38adca17e34beaeca4b9c271a18f1d5fc739257b0d70d9c68be7bed7fcee7827ab91cd8e4740cf6b19d4ffb87ab07b9bf516e72d4a3ac821b9cef6e60d84f47367ded2fae1d9f7704f6163f79000ffde27660f387ed3c5dbf54dfeebb0bf6acbe6b706bfa5ef32f8f8b18f5bf0f463f20edb77956a4e68fbc80b3c4facd163847be6bfde72ec86dbeac82715fbc0286be683cf3bc081d44f7df80d344fb0b6b7de64581fb916765297263d6fccd9b7ee97e566e8b06cfdb1f59cf2bda2f8bbc2c11df0c5c41aefdb328ca22b1feb0012eedbce24736f91b18fb796d64eb87cb60817dcdafb07d906bfd14d22f659d8785ef97b2f6d7b2df6ef1d6fb2babaac6f92e8c25b27ecc1f239b7f3a5fca5a8678ea7c2d9bc0e6ef1238b3fca2c7814d5f4cbf9334b5f833384bed3c7a5f5514f4adbe2ec07962e7590457f06f6f649b977afe2a16f42f5e070bfc591e18f7a5f1a812a9e0df39d827da4fbd8c6ccfd7785569bf945f953371a9d5a3de67950bfa2d75c63eb2fb15bdcfaaf011e6c7263846bf8f4636f91618f3c7d3c0f047fb69550efab2074e12bd6fd27957553ec33c5b043bcc7f9d0795f818fdb501a3df8ae673e507ff69021eea37063bc45ffb73550736ff36c0a84f3e0317a86f8b5f139e6ffe1f821de6d9127898ff3be00a7c3ab2cd03e3d697560ff40016e47b3bb2e96bbd559d8f32bbff4b7065f193042c98873acf240af6adbf3c83c5f2954bb0c7bcd77c107d81d2fdebc63e33ff640aaecc9e1c8151df5c803dfaadc64b52dfa03ed64636ffb4ff4b2867f453ede7227586fcd3f9264ded91fffafe236d5da37fe83c91ceb7b9e5b7f6731ff92ed7f747d6fbf7e185cf58347e3e6932e47b3430fa893edf87d795c2fc3f003b67f5f902cec17abfde0dfa3c053b3c5ffb832fc2eb8fddcf233887fc195c801f46b6f3cdc02558fba72f7d2d1a7f7e321ee58fe0cad86bbff24dd3e2fe74fef836b0c6eb60d62fa6bf5e07b3419f853dd7dc70fb8bd5f1255fd90ed30f9f3c5ff30ddff21ddff384a7fcc08ffcc4cf61cdf8855ff9e79c7e1db4dff89d3f7891977899577895d7789d377893b7f83b6fcfe937bc13b477798ff7f9800ff928ac633ee11f7cca6761d7f99c7e1b3cb908da11c7413be1943376e153cc39175c72f549ff9e17c31744429e6a6aa8a58e2ee98aaee9866e7f617fc24b7417a4f734a1293dd0233dd133cd8285177aa5f9f3b63ca5377aa78f607b919668995682e62aadd17ab0b1419b9ff41f682b48bed336edd02eed05ed7d5ea3033a0cdf1ed1f127fd273ae123fa41a774a6b685cee982228a837e42e927fd47caf8985c38651eb40b2ac38e4a5842658a97fab33fd248cb87d2c9a55cc9b5dcc82d1fc99ddccb44a6f230af2f8ff224cf417f262ff22a3fe54ddee543166549966545567f617f4dd66523dc6b2c9bb225df655b7682f692ecca9eeccfe97772c09b722847722c27c1f3eb70f66bf9116c9fca999ccbc59cfe25bf4a143a5ef8992561324a78bd92522acf9ebc78efe7cf7b153276db37bef59dbff457fedadff85b7f27abfede4ffcd4cf9ff76faeff4fffefeff8c7f5fedfdfbffc0fa355c495 + + + + + Exit + activated() + MainViewUI + slotExit() + + + Options + activated() + MainViewUI + slotOptions() + + + DescribeAll + activated() + MainViewUI + slotConfigureAllImages() + + + DescribeOneAtATime + activated() + MainViewUI + slotConfigureImagesOneAtATime() + + + + slotExit() + slotOptions() + slotConfigureAllImages() + slotConfigureImagesOneAtATime() + + + + thumbnailview.h + +
diff --git a/options.cpp b/options.cpp new file mode 100644 index 00000000..909d7fcd --- /dev/null +++ b/options.cpp @@ -0,0 +1,141 @@ +#include "options.h" +#include +#include +#include +#include + +Options* Options::_instance = 0; + +Options* Options::instance() +{ + if ( ! _instance ) + _instance = new Options(); + return _instance; +} + + +Options::Options() + : _thumbWidth( 32 ), _thumbHeight( 32 ), _numThreads( 1 ), _cacheThumbNails( true ), _use4To3Ratio( true ) +{ + QFile file( QDir::home().path() + "/.kpalbum" ); + if ( !file.open( IO_ReadOnly ) ) { + qWarning("No ~/.kpalbum found"); + return; + } + + QDomDocument doc; + doc.setContent( &file ); + QDomElement top = doc.documentElement(); + + _thumbWidth = top.attribute( "thumbWidth", QString::number(_thumbWidth) ).toInt(); + _thumbHeight = top.attribute( "thumbHeight", QString::number( _thumbHeight ) ).toInt(); + _numThreads = top.attribute( "numThreads", QString::number( _numThreads ) ).toInt(); + _cacheThumbNails = top.attribute( "cacheThumbNails", QString::number( _cacheThumbNails ) ).toInt(); + _use4To3Ratio = top.attribute( "use4To3Ratio", QString::number( _use4To3Ratio ) ).toInt(); + _trustFileTimeStanmps = top.attribute( "trustTimeFileStamps", "1" ).toInt(); +} + +void Options::setThumbWidth( int w ) +{ + _thumbWidth = w; +} + +int Options::thumbWidth() const +{ + return _thumbWidth; +} + +void Options::setThumbHeight( int h ) +{ + _thumbHeight = h; +} + +int Options::thumbHeight() const +{ + if ( _use4To3Ratio ) + return 3*_thumbWidth/4; + + return _thumbHeight; +} + +void Options::setNumThreads( int count ) +{ + _numThreads = count; +} + +int Options::numThreads() const +{ + return _numThreads; +} + +void Options::setCacheThumbNails( bool b ) +{ + _cacheThumbNails = b; +} + +bool Options::cacheThumbNails() const +{ + return _cacheThumbNails; +} + +void Options::setUse4To3Ratio( bool b ) +{ + _use4To3Ratio = b; +} + +bool Options::use4To3Ratio() const +{ + return _use4To3Ratio; +} + +QStringList Options::dataDirs() const +{ + return QStringList() << "/home/blackie/Images/take1"; +} + +void Options::save() +{ + QFile file( QDir::home().path() + "/.kpalbum" ); + if ( !file.open( IO_WriteOnly ) ) { + qWarning( "Could't open file ~/.kpalbum" ); + return; + } + + QDomDocument doc; + doc.setContent( QString("") ); + QDomElement top = doc.documentElement(); + + top.setAttribute( "thumbWidth", _thumbWidth ); + top.setAttribute( "thumbHeight", _thumbHeight ); + top.setAttribute( "numThreads", _numThreads ); + top.setAttribute( "cacheThumbNails", _cacheThumbNails ); + top.setAttribute( "use4To3Ratio", _use4To3Ratio ); + top.setAttribute( "trustTimeFileStamps", _trustFileTimeStanmps ); + + QTextStream stream( &file ); + stream << doc.toString(); + file.close(); +} + +void Options::setOption( const QString& key, const QStringList& value ) +{ + _options[key] = value; +} + +QStringList Options::optionValue( const QString& key ) const +{ + return _options[key]; +} + +void Options::setTrustFileTimeStamps( bool b) +{ + _trustFileTimeStanmps = b; +} + +bool Options::trustFileTimeStamps() const +{ + return _trustFileTimeStanmps; +} + + + diff --git a/options.h b/options.h new file mode 100644 index 00000000..2abf29a3 --- /dev/null +++ b/options.h @@ -0,0 +1,45 @@ +#ifndef OPTIONS_H +#define OPTIONS_H +#include +#include + +class Options { +public: + static Options* instance(); + + void setUse4To3Ratio( bool ); + bool use4To3Ratio() const; + + void setThumbWidth( int ); + int thumbWidth() const; + + void setThumbHeight( int ); + int thumbHeight() const; + + void setNumThreads( int ); + int numThreads() const; + + void setCacheThumbNails( bool ); + bool cacheThumbNails() const; + + void setOption( const QString& key, const QStringList& value ); + QStringList optionValue( const QString& key ) const; + + void setTrustFileTimeStamps( bool ); + bool trustFileTimeStamps() const; + + QStringList dataDirs() const; + void save(); + +private: + Options(); + ~Options() {}; + static Options* _instance; + + int _thumbWidth, _thumbHeight, _imageCacheSize, _numThreads; + bool _cacheThumbNails, _use4To3Ratio, _trustFileTimeStanmps; + QMap _options; +}; + +#endif /* OPTIONS_H */ + diff --git a/optionsdialog.ui b/optionsdialog.ui new file mode 100644 index 00000000..0594e33e --- /dev/null +++ b/optionsdialog.ui @@ -0,0 +1,221 @@ + +OptionsDialog + + + OptionsDialog + + + + 0 + 0 + 301 + 243 + + + + Options + + + + unnamed + + + + tabWidget3 + + + + tab + + + Thumbnails + + + + unnamed + + + + thumbNailWidth + + + + 1 + 0 + 0 + 0 + + + + 256 + + + + + cacheThumbNails + + + Cache On Disk + + + + + use4to3ratio + + + Use 4-to-3 ratio + + + + + thumbNailHeight + + + 256 + + + + + spacer2_2 + + + Horizontal + + + Expanding + + + + 139 + 20 + + + + + + heightLabel + + + height: + + + + + textLabel1 + + + width: + + + + + trustFileTimeStamps + + + Trust File Time Stamps + + + + + + + + layout1 + + + + unnamed + + + + spacer1 + + + Horizontal + + + Expanding + + + + 0 + 20 + + + + + + pushButton1 + + + OK + + + + + pushButton2 + + + Apply + + + + + pushButton3 + + + Cancel + + + + + + + + + pushButton3 + clicked() + OptionsDialog + close() + + + pushButton2 + clicked() + OptionsDialog + slotApply() + + + pushButton1 + clicked() + OptionsDialog + close() + + + pushButton1 + clicked() + OptionsDialog + slotApply() + + + use4to3ratio + clicked() + OptionsDialog + slotUpdate4To3View() + + + + options.h + optionsdialog.ui.h + + + changed(); + + + slotApply() + slotUpdate4To3View() + + + show() + + + diff --git a/optionsdialog.ui.h b/optionsdialog.ui.h new file mode 100644 index 00000000..34e6273b --- /dev/null +++ b/optionsdialog.ui.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you wish to add, delete or rename functions or slots use +** Qt Designer which will update this file, preserving your code. Create an +** init() function in place of a constructor, and a destroy() function in +** place of a destructor. +*****************************************************************************/ + +void OptionsDialog::show() +{ + Options* opt = Options::instance(); + + use4to3ratio->setChecked( opt->use4To3Ratio() ); + slotUpdate4To3View(); + + thumbNailWidth->setValue( opt->thumbWidth() ); + thumbNailHeight->setValue( opt->thumbHeight() ); + cacheThumbNails->setChecked( opt->cacheThumbNails() ); + trustFileTimeStamps->setChecked( opt->trustFileTimeStamps() ); + + QDialog::show(); +} + + + +void OptionsDialog::slotApply() +{ + Options* opt = Options::instance(); + opt->setThumbWidth( thumbNailWidth->value() ); + opt->setThumbHeight( thumbNailHeight->value() ); + opt->setCacheThumbNails( cacheThumbNails->isChecked() ); + opt->setTrustFileTimeStamps( trustFileTimeStamps->isChecked() ); + opt->save(); + emit changed(); +} + + +void OptionsDialog::slotUpdate4To3View() +{ + thumbNailHeight->setEnabled( !use4to3ratio->isChecked() ); + heightLabel->setEnabled( !use4to3ratio->isChecked() ); + +} diff --git a/thumbnail.cpp b/thumbnail.cpp new file mode 100644 index 00000000..5d07910b --- /dev/null +++ b/thumbnail.cpp @@ -0,0 +1,44 @@ +#include "thumbnail.h" +#include +#include +#include +#include "thumbnailview.h" +#include +#include "options.h" +#include "imageinfo.h" +#include "imagemanager.h" +#include + +ThumbNail::ThumbNail( ImageInfo* imageInfo, ThumbNailView* parent, const char* name ) + :QIconViewItem( parent, name ), _imageInfo( imageInfo ) +{ + int w = Options::instance()->thumbWidth(); + int h = Options::instance()->thumbHeight(); + QPixmap img( w, h ); + QPainter painter( &img ); + painter.fillRect( 0, 0, w, h, white ); + painter.drawRect( 0, 0, w-1, h-1 ); + setPixmap( img ); + setText( imageInfo->label() ); + ImageManager::instance()->load( _imageInfo->fileName(), this, w, h ); +} + +QString ThumbNail::fileName() const +{ + return _imageInfo->fileName(); +} + +ImageInfo* ThumbNail::imageInfo() +{ + return _imageInfo; +} + +void ThumbNail::pixmapLoaded( const QString&, int, int, const QPixmap& pixmap ) +{ + setPixmap( pixmap ); +} + + + + + diff --git a/thumbnail.h b/thumbnail.h new file mode 100644 index 00000000..af3108b6 --- /dev/null +++ b/thumbnail.h @@ -0,0 +1,23 @@ +#ifndef THUMBNAIL_H +#define THUMBNAIL_H + +#include +#include +#include "imageclient.h" +class ThumbNailView; +class ImageInfo; + +class ThumbNail :public QIconViewItem, public ImageClient { +public: + ThumbNail( ImageInfo* image, ThumbNailView* parent, const char* name ); + + virtual void pixmapLoaded( const QString&, int, int, const QPixmap& pixmap ); + QString fileName() const; + ImageInfo* imageInfo(); + +private: + ImageInfo* _imageInfo; +}; + +#endif /* THUMBNAIL_H */ + diff --git a/thumbnailview.cpp b/thumbnailview.cpp new file mode 100644 index 00000000..90877ca9 --- /dev/null +++ b/thumbnailview.cpp @@ -0,0 +1,48 @@ +#include "thumbnailview.h" +#include +#include "thumbnail.h" +#include +#include "viewer.h" + +ThumbNailView::ThumbNailView( QWidget* parent, const char* name ) + :QIconView( parent, name ) +{ + setResizeMode( QIconView::Adjust ); + + connect( this, SIGNAL( returnPressed( QIconViewItem* ) ), this, SLOT( showImage( QIconViewItem* ) ) ); + setSelectionMode( Multi ); + setItemsMovable( false ); +} + + +void ThumbNailView::showImage( QIconViewItem* item ) +{ + if ( item ) { + ThumbNail* tn = dynamic_cast( item ); + Q_ASSERT( tn ); + Viewer* viewer = new Viewer( this ); + viewer->resize(800, 600); + viewer->show(); + viewer->load( tn->imageInfo() ); + } +} + +void ThumbNailView::startDrag() +{ + // No dnd please. + return; +} + +void ThumbNailView::load( ImageInfoList* list ) +{ + for( QPtrListIterator it( *list ); *it; ++it ) { + new ThumbNail( *it, this, "thumbnail" ); + } + _imageList = list; +} + +void ThumbNailView::reload() +{ + clear(); + load( _imageList ); +} diff --git a/thumbnailview.h b/thumbnailview.h new file mode 100644 index 00000000..57885528 --- /dev/null +++ b/thumbnailview.h @@ -0,0 +1,25 @@ +#ifndef THUMBNAILVIEW_H +#define THUMBNAILVIEW_H +#include +#include "imageinfo.h" +class ImageManager; + +class ThumbNailView :public QIconView { + Q_OBJECT + +public: + ThumbNailView( QWidget* parent, const char* name = 0 ); + void load( ImageInfoList* list ); + +public slots: + void reload(); + +protected slots: + void showImage( QIconViewItem* ); + virtual void startDrag(); +private: + ImageInfoList* _imageList; +}; + +#endif /* THUMBNAILVIEW_H */ + diff --git a/viewer.cpp b/viewer.cpp new file mode 100644 index 00000000..a62e515d --- /dev/null +++ b/viewer.cpp @@ -0,0 +1,25 @@ +#include "viewer.h" +#include +#include +#include "imageinfo.h" + +Viewer::Viewer( QWidget* parent, const char* name ) + :QDialog( parent, name ), _info(0) +{ + QVBoxLayout* layout = new QVBoxLayout( this ); + _label = new QLabel( this ); + layout->addWidget( _label ); +} + +void Viewer::load( ImageInfo* info ) +{ + _info = info; + _label->setPixmap( info->pixmap( _label->width(), _label->height() ) ); +} + +void Viewer::resizeEvent( QResizeEvent* e ) +{ + QDialog::resizeEvent( e ); + if ( _info ) + load( _info ); +} diff --git a/viewer.h b/viewer.h new file mode 100644 index 00000000..a0d51be6 --- /dev/null +++ b/viewer.h @@ -0,0 +1,23 @@ +#ifndef VIEWER_H +#define VIEWER_H + +#include +class ImageInfo; +class QLabel; + +class Viewer :public QDialog +{ +public: + Viewer( QWidget* parent, const char* name = 0 ); + void load( ImageInfo* ); + +protected: + virtual void resizeEvent( QResizeEvent* e ); + +private: + QLabel* _label; + ImageInfo* _info; +}; + +#endif /* VIEWER_H */ +