diff options
Diffstat (limited to 'src/utilities')
161 files changed, 37851 insertions, 0 deletions
diff --git a/src/utilities/Makefile.am b/src/utilities/Makefile.am new file mode 100644 index 00000000..fe4c1532 --- /dev/null +++ b/src/utilities/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = imageeditor setup cameragui hotplug scripts batch slideshow lighttable diff --git a/src/utilities/batch/Makefile.am b/src/utilities/batch/Makefile.am new file mode 100644 index 00000000..355643da --- /dev/null +++ b/src/utilities/batch/Makefile.am @@ -0,0 +1,20 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/digikam \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/thumbbar \ + -I$(top_srcdir)/src/libs/widgets/common \ + $(LIBKDCRAW_CFLAGS) \ + $(LIBKEXIV2_CFLAGS) \ + $(all_includes) + +noinst_LTLIBRARIES = libbatch.la + +libbatch_la_SOURCES = batchthumbsgenerator.cpp batchalbumssyncmetadata.cpp \ + imageinfojob.cpp imageinfoalbumsjob.cpp batchsyncmetadata.cpp + +libbatch_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + + diff --git a/src/utilities/batch/batchalbumssyncmetadata.cpp b/src/utilities/batch/batchalbumssyncmetadata.cpp new file mode 100644 index 00000000..fba18d34 --- /dev/null +++ b/src/utilities/batch/batchalbumssyncmetadata.cpp @@ -0,0 +1,183 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-22-01 + * Description : batch sync pictures metadata from all Albums + * with digiKam database + * + * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqstring.h> +#include <tqtimer.h> +#include <tqdatetime.h> + +// KDE includes. + +#include <tdelocale.h> +#include <tdeapplication.h> +#include <kiconloader.h> + +// Local includes. + +#include "ddebug.h" +#include "album.h" +#include "albummanager.h" +#include "imageinfojob.h" +#include "metadatahub.h" +#include "batchalbumssyncmetadata.h" +#include "batchalbumssyncmetadata.moc" + +namespace Digikam +{ + +class BatchAlbumsSyncMetadataPriv +{ +public: + + BatchAlbumsSyncMetadataPriv() + { + cancel = false; + imageInfoJob = 0; + palbumList = AlbumManager::instance()->allPAlbums(); + duration.start(); + } + + bool cancel; + + TQTime duration; + + ImageInfoJob *imageInfoJob; + + AlbumList palbumList; + AlbumList::Iterator albumsIt; +}; + +BatchAlbumsSyncMetadata::BatchAlbumsSyncMetadata(TQWidget* parent) + : DProgressDlg(parent) +{ + d = new BatchAlbumsSyncMetadataPriv; + d->imageInfoJob = new ImageInfoJob(); + setValue(0); + setCaption(i18n("Sync All Images' Metadata")); + setLabel(i18n("<b>Syncing the metadata of all images with the digiKam database. Please wait...</b>")); + setButtonText(i18n("&Abort")); + resize(600, 300); + TQTimer::singleShot(500, this, TQ_SLOT(slotStart())); +} + +BatchAlbumsSyncMetadata::~BatchAlbumsSyncMetadata() +{ + delete d; +} + +void BatchAlbumsSyncMetadata::slotStart() +{ + setTitle(i18n("Parsing all albums")); + setTotalSteps(d->palbumList.count()); + + connect(d->imageInfoJob, TQ_SIGNAL(signalItemsInfo(const ImageInfoList&)), + this, TQ_SLOT(slotAlbumParsed(const ImageInfoList&))); + + connect(d->imageInfoJob, TQ_SIGNAL(signalCompleted()), + this, TQ_SLOT(slotComplete())); + + d->albumsIt = d->palbumList.begin(); + parseAlbum(); +} + +void BatchAlbumsSyncMetadata::parseAlbum() +{ + if (d->albumsIt == d->palbumList.end()) // All is done. + { + TQTime t; + t = t.addMSecs(d->duration.elapsed()); + setLabel(i18n("<b>The metadata of all images has been synchronized with the digiKam database.</b>")); + setTitle(i18n("Duration: %1").arg(t.toString())); + setButtonText(i18n("&Close")); + advance(1); + abort(); + } + else if (!(*d->albumsIt)->isRoot()) + { + d->imageInfoJob->allItemsFromAlbum(*d->albumsIt); + DDebug() << "Sync Items from Album :" << (*d->albumsIt)->kurl().directory() << endl; + } + else + { + d->albumsIt++; + parseAlbum(); + } +} + +void BatchAlbumsSyncMetadata::slotAlbumParsed(const ImageInfoList& list) +{ + TQPixmap pix = TDEApplication::kApplication()->iconLoader()->loadIcon( + "folder_image", TDEIcon::NoGroup, 32); + + ImageInfoList imageInfoList = list; + + if (!imageInfoList.isEmpty()) + { + addedAction(pix, imageInfoList.first()->kurl().directory()); + + for (ImageInfo *info = imageInfoList.first(); info; info = imageInfoList.next()) + { + MetadataHub fileHub; + // read in from database + fileHub.load(info); + // write out to file DMetadata + fileHub.write(info->filePath()); + } + } + + advance(1); + d->albumsIt++; + parseAlbum(); +} + +void BatchAlbumsSyncMetadata::slotComplete() +{ + advance(1); + d->albumsIt++; + parseAlbum(); +} + +void BatchAlbumsSyncMetadata::slotCancel() +{ + abort(); + done(Cancel); +} + +void BatchAlbumsSyncMetadata::closeEvent(TQCloseEvent *e) +{ + abort(); + e->accept(); +} + +void BatchAlbumsSyncMetadata::abort() +{ + d->cancel = true; + d->imageInfoJob->stop(); + emit signalComplete(); +} + +} // namespace Digikam + + diff --git a/src/utilities/batch/batchalbumssyncmetadata.h b/src/utilities/batch/batchalbumssyncmetadata.h new file mode 100644 index 00000000..8363ce4d --- /dev/null +++ b/src/utilities/batch/batchalbumssyncmetadata.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-22-01 + * Description : batch sync pictures metadata with + * digiKam database + * + * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef BATCHALBUMSSYNCMETADATA_H +#define BATCHALBUMSSYNCMETADATA_H + +// Local includes. + +#include "imageinfo.h" +#include "dprogressdlg.h" + +class TQWidget; + +class KURL; + +namespace Digikam +{ + +class BatchAlbumsSyncMetadataPriv; + +class BatchAlbumsSyncMetadata : public DProgressDlg +{ + TQ_OBJECT + + +public: + + BatchAlbumsSyncMetadata(TQWidget* parent); + ~BatchAlbumsSyncMetadata(); + +signals: + + void signalComplete(); + +private: + + void abort(); + void parseAlbum(); + +protected: + + void closeEvent(TQCloseEvent *e); + +protected slots: + + void slotCancel(); + +private slots: + + void slotStart(); + void slotAlbumParsed(const ImageInfoList&); + void slotComplete(); + +private: + + BatchAlbumsSyncMetadataPriv *d; +}; + +} // namespace Digikam + +#endif /* BATCHALBUMSSYNCMETADATA_H */ diff --git a/src/utilities/batch/batchsyncmetadata.cpp b/src/utilities/batch/batchsyncmetadata.cpp new file mode 100644 index 00000000..50e2ad7a --- /dev/null +++ b/src/utilities/batch/batchsyncmetadata.cpp @@ -0,0 +1,166 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-22-01 + * Description : batch sync pictures metadata from all Albums + * with digiKam database + * + * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqstring.h> + +// KDE includes. + +#include <tdelocale.h> +#include <tdeapplication.h> + +// Local includes. + +#include "ddebug.h" +#include "album.h" +#include "imageinfojob.h" +#include "metadatahub.h" +#include "statusprogressbar.h" +#include "batchsyncmetadata.h" +#include "batchsyncmetadata.moc" + +namespace Digikam +{ + +class BatchSyncMetadataPriv +{ +public: + + BatchSyncMetadataPriv() + { + cancel = false; + imageInfoJob = new ImageInfoJob(); + album = 0; + count = 0; + imageInfo = 0; + } + + bool cancel; + + int count; + + Album *album; + + ImageInfoJob *imageInfoJob; + + ImageInfoList imageInfoList; + + ImageInfo *imageInfo; +}; + +BatchSyncMetadata::BatchSyncMetadata(TQObject* parent, Album *album) + : TQObject(parent) +{ + d = new BatchSyncMetadataPriv; + d->album = album; +} + +BatchSyncMetadata::BatchSyncMetadata(TQObject* parent, const ImageInfoList& list) + : TQObject(parent) +{ + d = new BatchSyncMetadataPriv; + d->imageInfoList = list; +} + +BatchSyncMetadata::~BatchSyncMetadata() +{ + delete d; +} + +void BatchSyncMetadata::parseAlbum() +{ + d->imageInfoJob->allItemsFromAlbum(d->album); + + connect(d->imageInfoJob, TQ_SIGNAL(signalItemsInfo(const ImageInfoList&)), + this, TQ_SLOT(slotAlbumParsed(const ImageInfoList&))); + + connect(d->imageInfoJob, TQ_SIGNAL(signalCompleted()), + this, TQ_SLOT(slotComplete())); +} + +void BatchSyncMetadata::slotComplete() +{ + if (d->imageInfoList.isEmpty()) + complete(); +} + +void BatchSyncMetadata::slotAlbumParsed(const ImageInfoList& list) +{ + d->imageInfoList = list; + parseList(); +} + +void BatchSyncMetadata::parseList() +{ + emit signalProgressBarMode(StatusProgressBar::CancelProgressBarMode, + i18n("Synchonizing images' Metadata with database. Please wait...")); + + d->imageInfo = d->imageInfoList.first(); + parsePicture(); +} + +void BatchSyncMetadata::parsePicture() +{ + if (!d->imageInfo) // All is done. + { + complete(); + slotAbort(); + } + else if (d->cancel) + { + complete(); + } + else + { + MetadataHub fileHub; + // read in from database + fileHub.load(d->imageInfo); + // write out to file DMetadata + fileHub.write(d->imageInfo->filePath()); + + emit signalProgressValue((int)((d->count++/(float)d->imageInfoList.count())*100.0)); + + d->imageInfo = d->imageInfoList.next(); + + kapp->processEvents(); + parsePicture(); + } +} + +void BatchSyncMetadata::slotAbort() +{ + d->cancel = true; + d->imageInfoJob->stop(); +} + +void BatchSyncMetadata::complete() +{ + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); + emit signalComplete(); +} + +} // namespace Digikam + + diff --git a/src/utilities/batch/batchsyncmetadata.h b/src/utilities/batch/batchsyncmetadata.h new file mode 100644 index 00000000..c9b69ac7 --- /dev/null +++ b/src/utilities/batch/batchsyncmetadata.h @@ -0,0 +1,89 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-22-01 + * Description : batch sync pictures metadata with + * digiKam database + * + * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef BATCHSYNCMETADATA_H +#define BATCHSYNCMETADATA_H + +// TQt includes. + +#include <tqobject.h> + +// Local includes. + +#include "imageinfo.h" + +class KURL; + +namespace Digikam +{ + +class Album; +class BatchSyncMetadataPriv; + +class BatchSyncMetadata : public TQObject +{ + TQ_OBJECT + + +public: + + /** Constructor witch sync all metatada pictures from an Album */ + BatchSyncMetadata(TQObject* parent, Album *album); + + /** Constructor witch sync all metatada from an images list */ + BatchSyncMetadata(TQObject* parent, const ImageInfoList& list); + + ~BatchSyncMetadata(); + + void parseList(); + void parseAlbum(); + +signals: + + void signalComplete(); + void signalProgressValue(int); + void signalProgressBarMode(int, const TQString&); + +public slots: + + void slotAbort(); + +private: + + void parsePicture(); + void complete(); + +private slots: + + void slotAlbumParsed(const ImageInfoList&); + void slotComplete(); + +private: + + BatchSyncMetadataPriv *d; +}; + +} // namespace Digikam + +#endif /* BATCHSYNCMETADATA_H */ diff --git a/src/utilities/batch/batchthumbsgenerator.cpp b/src/utilities/batch/batchthumbsgenerator.cpp new file mode 100644 index 00000000..f0bd0103 --- /dev/null +++ b/src/utilities/batch/batchthumbsgenerator.cpp @@ -0,0 +1,233 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-30-08 + * Description : batch thumbnails generator + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include <unistd.h> +} + +// TQt includes. + +#include <tqstring.h> +#include <tqtimer.h> +#include <tqdir.h> +#include <tqfileinfo.h> +#include <tqdatetime.h> + +// KDE includes. + +#include <kmdcodec.h> +#include <tdelocale.h> +#include <tdeapplication.h> + +// Local includes. + +#include "ddebug.h" +#include "album.h" +#include "albumdb.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "thumbnailjob.h" +#include "batchthumbsgenerator.h" +#include "batchthumbsgenerator.moc" + +namespace Digikam +{ + +class BatchThumbsGeneratorPriv +{ +public: + + BatchThumbsGeneratorPriv() + { + cancel = false; + thumbJob = 0; + duration.start(); + } + + bool cancel; + + TQTime duration; + + TQGuardedPtr<ThumbnailJob> thumbJob; +}; + +BatchThumbsGenerator::BatchThumbsGenerator(TQWidget* parent) + : DProgressDlg(parent) +{ + d = new BatchThumbsGeneratorPriv; + setValue(0); + setCaption(i18n("Thumbnails processing")); + setLabel(i18n("<b>Updating thumbnails database. Please wait...</b>")); + setButtonText(i18n("&Abort")); + TQTimer::singleShot(500, this, TQ_SLOT(slotRebuildThumbs128())); + resize(600, 300); +} + +BatchThumbsGenerator::~BatchThumbsGenerator() +{ + if (!d->thumbJob.isNull()) + { + d->thumbJob->kill(); + d->thumbJob = 0; + } + + delete d; +} + +void BatchThumbsGenerator::slotRebuildThumbs128() +{ + setTitle(i18n("Processing small thumbs")); + rebuildAllThumbs(128); + + connect(this, TQ_SIGNAL(signalRebuildThumbsDone()), + this, TQ_SLOT(slotRebuildThumbs256())); +} + +void BatchThumbsGenerator::slotRebuildThumbs256() +{ + setTitle(i18n("Processing large thumbs")); + rebuildAllThumbs(256); + + disconnect(this, TQ_SIGNAL(signalRebuildThumbsDone()), + this, TQ_SLOT(slotRebuildThumbs256())); + + connect(this, TQ_SIGNAL(signalRebuildThumbsDone()), + this, TQ_SLOT(slotRebuildAllThumbComplete())); +} + +void BatchThumbsGenerator::slotRebuildAllThumbComplete() +{ + TQTime t; + t = t.addMSecs(d->duration.elapsed()); + setLabel(i18n("<b>The thumbnails database has been updated.</b>")); + setTitle(i18n("Duration: %1").arg(t.toString())); + setButtonText(i18n("&Close")); +} + +void BatchThumbsGenerator::rebuildAllThumbs(int size) +{ + TQStringList allPicturesPath; + TQString thumbCacheDir = TQDir::homeDirPath() + "/.thumbnails/"; + TQString filesFilter = AlbumSettings::instance()->getAllFileFilter(); + bool exifRotate = AlbumSettings::instance()->getExifRotate(); + AlbumDB *db = AlbumManager::instance()->albumDB(); + AlbumList palbumList = AlbumManager::instance()->allPAlbums(); + + // Get all digiKam albums collection pictures path. + + for (AlbumList::Iterator it = palbumList.begin(); + !d->cancel && (it != palbumList.end()); ++it ) + { + // Don't use the root album + if ((*it)->isRoot()) + continue; + + db->beginTransaction(); + TQStringList albumItemsPath = db->getItemURLsInAlbum((*it)->id()); + db->commitTransaction(); + + TQStringList pathSorted; + for (TQStringList::iterator it2 = albumItemsPath.begin(); + !d->cancel && (it2 != albumItemsPath.end()); ++it2) + { + TQFileInfo fi(*it2); + if (filesFilter.contains(fi.extension(false))) + pathSorted.append(*it2); + } + + allPicturesPath += pathSorted; + } + + setTotalSteps(allPicturesPath.count()*2); + + // Remove all current album item thumbs from disk cache. + + for (TQStringList::iterator it = allPicturesPath.begin(); + !d->cancel && (it != allPicturesPath.end()); ++it) + { + TQString uri = "file://" + TQDir::cleanDirPath(*it); + KMD5 md5(TQFile::encodeName(uri).data()); + uri = md5.hexDigest(); + + TQString smallThumbPath = thumbCacheDir + "normal/" + uri + ".png"; + TQString bigThumbPath = thumbCacheDir + "large/" + uri + ".png"; + + if (size <= 128) + ::unlink(TQFile::encodeName(smallThumbPath)); + else + ::unlink(TQFile::encodeName(bigThumbPath)); + } + + if (!d->thumbJob.isNull()) + { + d->thumbJob->kill(); + d->thumbJob = 0; + } + + d->thumbJob = new ThumbnailJob(KURL::List(allPicturesPath), size, true, exifRotate); + + connect(d->thumbJob, TQ_SIGNAL(signalThumbnail(const KURL&, const TQPixmap&)), + this, TQ_SLOT(slotRebuildThumbDone(const KURL&, const TQPixmap&))); + + connect(d->thumbJob, TQ_SIGNAL(signalFailed(const KURL&)), + this, TQ_SLOT(slotRebuildThumbDone(const KURL&))); + + connect(d->thumbJob, TQ_SIGNAL(signalCompleted()), + this, TQ_SIGNAL(signalRebuildThumbsDone())); +} + +void BatchThumbsGenerator::slotRebuildThumbDone(const KURL& url, const TQPixmap& pix) +{ + addedAction(pix, url.path()); + advance(1); +} + +void BatchThumbsGenerator::slotCancel() +{ + abort(); + done(Cancel); +} + +void BatchThumbsGenerator::closeEvent(TQCloseEvent *e) +{ + abort(); + e->accept(); +} + +void BatchThumbsGenerator::abort() +{ + d->cancel = true; + + if (!d->thumbJob.isNull()) + { + d->thumbJob->kill(); + d->thumbJob = 0; + } + + emit signalRebuildAllThumbsDone(); +} + +} // namespace Digikam diff --git a/src/utilities/batch/batchthumbsgenerator.h b/src/utilities/batch/batchthumbsgenerator.h new file mode 100644 index 00000000..091c2783 --- /dev/null +++ b/src/utilities/batch/batchthumbsgenerator.h @@ -0,0 +1,83 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-30-08 + * Description : batch thumbnails generator + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef BATCHTHUMBSGENERATOR_H +#define BATCHTHUMBSGENERATOR_H + +// Local includes. + +#include "dprogressdlg.h" + +class TQWidget; +class TQPixmap; + +class KURL; + +namespace Digikam +{ + +class BatchThumbsGeneratorPriv; + +class BatchThumbsGenerator : public DProgressDlg +{ + TQ_OBJECT + + +public: + + BatchThumbsGenerator(TQWidget* parent); + ~BatchThumbsGenerator(); + +signals: + + void signalRebuildThumbsDone(); + void signalRebuildAllThumbsDone(); + +private: + + void rebuildAllThumbs(int size); + void abort(); + +protected: + + void closeEvent(TQCloseEvent *e); + +protected slots: + + void slotCancel(); + +private slots: + + void slotRebuildThumbs128(); + void slotRebuildThumbs256(); + void slotRebuildThumbDone(const KURL& url, const TQPixmap& pix=TQPixmap()); + void slotRebuildAllThumbComplete(); + +private: + + BatchThumbsGeneratorPriv *d; +}; + +} // namespace Digikam + +#endif /* BATCHTHUMBSGENERATOR_H */ diff --git a/src/utilities/batch/imageinfoalbumsjob.cpp b/src/utilities/batch/imageinfoalbumsjob.cpp new file mode 100644 index 00000000..4d0e3c55 --- /dev/null +++ b/src/utilities/batch/imageinfoalbumsjob.cpp @@ -0,0 +1,125 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-14-02 + * Description : interface to get image info from an albums list. + * + * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqstring.h> + +// KDE includes. + +#include <kurl.h> + +// Local includes. + +#include "ddebug.h" +#include "album.h" +#include "albummanager.h" +#include "imageinfojob.h" +#include "imageinfoalbumsjob.h" +#include "imageinfoalbumsjob.moc" + +namespace Digikam +{ + +class ImageInfoAlbumsJobPriv +{ +public: + + ImageInfoAlbumsJobPriv(){} + + AlbumList albumsList; + AlbumList::Iterator albumIt; + + ImageInfoList itemsList; + + ImageInfoJob imageInfoJob; +}; + +ImageInfoAlbumsJob::ImageInfoAlbumsJob() +{ + d = new ImageInfoAlbumsJobPriv; + + connect(&d->imageInfoJob, TQ_SIGNAL(signalItemsInfo(const ImageInfoList&)), + this, TQ_SLOT(slotItemsInfo(const ImageInfoList&))); + + connect(&d->imageInfoJob, TQ_SIGNAL(signalCompleted()), + this, TQ_SLOT(slotComplete())); +} + +ImageInfoAlbumsJob::~ImageInfoAlbumsJob() +{ + delete d; +} + +void ImageInfoAlbumsJob::allItemsFromAlbums(const AlbumList& albumsList) +{ + if (albumsList.isEmpty()) + return; + + d->albumsList = albumsList; + d->albumIt = d->albumsList.begin(); + parseAlbum(); +} + +void ImageInfoAlbumsJob::parseAlbum() +{ + d->imageInfoJob.allItemsFromAlbum(*d->albumIt); +} + +void ImageInfoAlbumsJob::stop() +{ + d->imageInfoJob.stop(); + d->albumsList.clear(); +} + +void ImageInfoAlbumsJob::slotItemsInfo(const ImageInfoList& items) +{ + ImageInfo* item; + for (ImageInfoListIterator it(items); (item = it.current()); ++it) + d->itemsList.append(item); + + ++d->albumIt; + if (d->albumIt == d->albumsList.end()) + { + stop(); + emit signalCompleted(d->itemsList); + return; + } + + parseAlbum(); +} + +void ImageInfoAlbumsJob::slotComplete() +{ + ++d->albumIt; + if (d->albumIt == d->albumsList.end()) + { + stop(); + emit signalCompleted(d->itemsList); + return; + } + + parseAlbum(); +} + +} // namespace Digikam diff --git a/src/utilities/batch/imageinfoalbumsjob.h b/src/utilities/batch/imageinfoalbumsjob.h new file mode 100644 index 00000000..7b9f477f --- /dev/null +++ b/src/utilities/batch/imageinfoalbumsjob.h @@ -0,0 +1,80 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-14-02 + * Description : interface to get image info from an albums list. + * + * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef IMAGEINFOALBUMSJOB_H +#define IMAGEINFOALBUMSJOB_H + +// TQt includes. + +#include <tqobject.h> +#include <tqcstring.h> + +// Local includes. + +#include "albummanager.h" +#include "imageinfo.h" + +namespace TDEIO +{ +class Job; +} + +namespace Digikam +{ + +class ImageInfoAlbumsJobPriv; + +class ImageInfoAlbumsJob : public TQObject +{ + TQ_OBJECT + + +public: + + ImageInfoAlbumsJob(); + ~ImageInfoAlbumsJob(); + + void allItemsFromAlbums(const AlbumList& albumsList); + void stop(); + +signals: + + void signalCompleted(const ImageInfoList& items); + +private slots: + + void slotItemsInfo(const ImageInfoList&); + void slotComplete(); + +private: + + void parseAlbum(); + +private: + + ImageInfoAlbumsJobPriv *d; +}; + +} // namespace Digikam + +#endif /* IMAGEINFOALBUMSJOB_H */ diff --git a/src/utilities/batch/imageinfojob.cpp b/src/utilities/batch/imageinfojob.cpp new file mode 100644 index 00000000..58660697 --- /dev/null +++ b/src/utilities/batch/imageinfojob.cpp @@ -0,0 +1,163 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-22-01 + * Description : digikamalbum TDEIO slave interface to get image + * info from database. + * + * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqstring.h> +#include <tqdatastream.h> + +// KDE includes. + +#include <tdeio/job.h> +#include <kurl.h> + +// Local includes. + +#include "ddebug.h" +#include "album.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "imageinfojob.h" +#include "imageinfojob.moc" + +namespace Digikam +{ + +class ImageInfoJobPriv +{ +public: + + ImageInfoJobPriv() + { + job = 0; + + AlbumSettings *settings = AlbumSettings::instance(); + imagefilter = settings->getImageFileFilter().lower() + + settings->getImageFileFilter().upper() + + settings->getRawFileFilter().lower() + + settings->getRawFileFilter().upper(); + } + + TQString imagefilter; + + TDEIO::TransferJob *job; +}; + +ImageInfoJob::ImageInfoJob() +{ + d = new ImageInfoJobPriv; +} + +ImageInfoJob::~ImageInfoJob() +{ + delete d; +} + +void ImageInfoJob::allItemsFromAlbum(Album *album) +{ + if (d->job) + { + d->job->kill(); + d->job = 0; + } + + if (!album) + return; + + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + ds << AlbumManager::instance()->getLibraryPath(); + ds << album->kurl(); + ds << d->imagefilter; + ds << 0; // getting dimensions (not needed here) + ds << 0; // recursive sub-album (not needed here) + ds << 0; // recursive sub-tags (not needed here) + + // Protocol = digikamalbums -> tdeio_digikamalbums + d->job = new TDEIO::TransferJob(album->kurl(), TDEIO::CMD_SPECIAL, + ba, TQByteArray(), false); + + connect(d->job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotResult(TDEIO::Job*))); + + connect(d->job, TQ_SIGNAL(data(TDEIO::Job*, const TQByteArray&)), + this, TQ_SLOT(slotData(TDEIO::Job*, const TQByteArray&))); +} + +void ImageInfoJob::stop() +{ + if (d->job) + { + d->job->kill(); + d->job = 0; + } +} + +void ImageInfoJob::slotResult(TDEIO::Job* job) +{ + d->job = 0; + + if (job->error()) + { + DWarning() << "Failed to list url: " << job->errorString() << endl; + return; + } + + emit signalCompleted(); +} + +void ImageInfoJob::slotData(TDEIO::Job*, const TQByteArray& data) +{ + if (data.isEmpty()) + return; + + TQ_LLONG imageID; + int albumID; + TQString name; + TQString date; + size_t size; + TQSize dims; + ImageInfoList itemsList; + TQDataStream ds(data, IO_ReadOnly); + + while (!ds.atEnd()) + { + ds >> imageID; + ds >> albumID; + ds >> name; + ds >> date; + ds >> size; + ds >> dims; + + ImageInfo* info = new ImageInfo(imageID, albumID, name, + TQDateTime::fromString(date, TQt::ISODate), + size, dims); + + itemsList.append(info); + } + + emit signalItemsInfo(itemsList); +} + +} // namespace Digikam diff --git a/src/utilities/batch/imageinfojob.h b/src/utilities/batch/imageinfojob.h new file mode 100644 index 00000000..0227a628 --- /dev/null +++ b/src/utilities/batch/imageinfojob.h @@ -0,0 +1,78 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-22-01 + * Description : digikamalbum TDEIO slave interface to get image + * info from database. + * + * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef IMAGEINFOJOB_H +#define IMAGEINFOJOB_H + +// TQt includes. + +#include <tqobject.h> +#include <tqcstring.h> + +// Local includes. + +#include "imageinfo.h" + +namespace TDEIO +{ +class Job; +} + +namespace Digikam +{ + +class Album; +class ImageInfoJobPriv; + +class ImageInfoJob : public TQObject +{ + TQ_OBJECT + + +public: + + ImageInfoJob(); + ~ImageInfoJob(); + + void allItemsFromAlbum(Album *album); + void stop(); + +signals: + + void signalItemsInfo(const ImageInfoList& items); + void signalCompleted(); + +private slots: + + void slotResult(TDEIO::Job* job); + void slotData(TDEIO::Job* job, const TQByteArray& data); + +private: + + ImageInfoJobPriv *d; +}; + +} // namespace Digikam + +#endif /* IMAGEINFOJOB_H */ diff --git a/src/utilities/cameragui/Makefile.am b/src/utilities/cameragui/Makefile.am new file mode 100644 index 00000000..83f810f4 --- /dev/null +++ b/src/utilities/cameragui/Makefile.am @@ -0,0 +1,30 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libcameragui.la + +# NOTE from Gilles (06-12-06): gpcamera.cpp must be placed on the top of source file list +# to unbreak compilation with './configure -enable-final' option. I suspect a problem with +# Gphoto2 C Ansi header. +libcameragui_la_SOURCES = gpcamera.cpp cameraui.cpp cameraiconview.cpp \ + cameraiconitem.cpp cameracontroller.cpp \ + camerafolderview.cpp camerafolderitem.cpp \ + animwidget.cpp renamecustomizer.cpp \ + dkcamera.cpp umscamera.cpp gpiteminfo.cpp \ + camerainfodialog.cpp albumselectdialog.cpp \ + camerafolderdialog.cpp freespacewidget.cpp + +libcameragui_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +libcameragui_la_LIBADD = $(top_builddir)/src/libs/imageproperties/libimagepropertiescamgui.la \ + $(LIB_GPHOTO) $(LIBJPEG) + +INCLUDES = -I$(top_srcdir)/src/digikam \ + -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/libs/jpegutils \ + -I$(top_srcdir)/src/libs/themeengine \ + -I$(top_srcdir)/src/libs/imageproperties \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + $(LIBKEXIV2_CFLAGS) \ + $(GPHOTO_CFLAGS) $(LIBKDCRAW_CFLAGS) $(all_includes) diff --git a/src/utilities/cameragui/albumselectdialog.cpp b/src/utilities/cameragui/albumselectdialog.cpp new file mode 100644 index 00000000..486c4711 --- /dev/null +++ b/src/utilities/cameragui/albumselectdialog.cpp @@ -0,0 +1,417 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-16 + * Description : a dialog to select a target album to download + * pictures from camera + * + * Copyright (C) 2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlabel.h> +#include <tqframe.h> +#include <tqlayout.h> +#include <tqpopupmenu.h> +#include <tqcursor.h> +#include <tqdatetime.h> +#include <tqmap.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kiconloader.h> +#include <tdeapplication.h> +#include <tdeaction.h> +#include <kinputdialog.h> +#include <tdemessagebox.h> + +// Local includes. + +#include "ddebug.h" +#include "folderview.h" +#include "folderitem.h" +#include "album.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "searchtextbar.h" +#include "albumselectdialog.h" +#include "albumselectdialog.moc" + +namespace Digikam +{ + +class AlbumSelectDialogPrivate +{ + +public: + + AlbumSelectDialogPrivate() + { + allowRootSelection = false; + folderView = 0; + searchBar = 0; + } + + bool allowRootSelection; + + TQString newAlbumString; + + TQMap<FolderItem*, PAlbum*> albumMap; + + FolderView *folderView; + + SearchTextBar *searchBar; +}; + +AlbumSelectDialog::AlbumSelectDialog(TQWidget* parent, PAlbum* albumToSelect, + const TQString& header, + const TQString& newAlbumString, + bool allowRootSelection ) + : KDialogBase(Plain, i18n("Select Album"), + Help|User1|Ok|Cancel, Ok, + parent, 0, true, true, + i18n("&New Album")) +{ + d = new AlbumSelectDialogPrivate; + setHelp("targetalbumdialog.anchor", "digikam"); + enableButtonOK(false); + + d->allowRootSelection = allowRootSelection; + d->newAlbumString = newAlbumString; + + // ------------------------------------------------------------- + + TQGridLayout* grid = new TQGridLayout(plainPage(), 2, 1, 0, spacingHint()); + + TQLabel *logo = new TQLabel(plainPage()); + TDEIconLoader* iconLoader = TDEApplication::kApplication()->iconLoader(); + logo->setPixmap(iconLoader->loadIcon("digikam", TDEIcon::NoGroup, 128, TDEIcon::DefaultState, 0, true)); + + TQLabel *message = new TQLabel(plainPage()); + if (!header.isEmpty()) + message->setText(header); + + d->folderView = new FolderView(plainPage()); + d->folderView->addColumn(i18n("My Albums")); + d->folderView->setColumnWidthMode( 0, TQListView::Maximum ); + d->folderView->setResizeMode( TQListView::AllColumns ); + d->folderView->setRootIsDecorated(true); + + d->searchBar = new SearchTextBar(plainPage(), "AlbumSelectDialogSearchBar"); + + // ------------------------------------------------------------- + + TQPixmap icon = iconLoader->loadIcon("folder", TDEIcon::NoGroup, + AlbumSettings::instance()->getDefaultTreeIconSize(), TDEIcon::DefaultState, 0, true); + + AlbumList aList = AlbumManager::instance()->allPAlbums(); + + for (AlbumList::const_iterator it = aList.begin(); it != aList.end(); ++it) + { + PAlbum* album = (PAlbum*)(*it); + + FolderItem* viewItem = 0; + + if (album->isRoot()) + { + viewItem = new FolderItem(d->folderView, album->title()); + viewItem->setOpen(true); + } + else + { + FolderItem* parentItem = (FolderItem*)(album->parent()->extraData(d->folderView)); + + if (!parentItem) + { + DWarning() << "Failed to find parent for Album " + << album->title() << endl; + continue; + } + + viewItem = new FolderItem(parentItem, album->title()); + } + + if (viewItem) + { + viewItem->setPixmap(0, icon); + album->setExtraData(d->folderView, viewItem); + d->albumMap.insert(viewItem, album); + + if (album == albumToSelect) + { + viewItem->setOpen(true); + d->folderView->setSelected(viewItem, true); + d->folderView->ensureItemVisible(viewItem); + } + } + } + + // ------------------------------------------------------------- + + grid->addMultiCellWidget(logo, 0, 0, 0, 0); + grid->addMultiCellWidget(message, 1, 1, 0, 0); + grid->addMultiCellWidget(d->folderView, 0, 2, 1, 1); + grid->addMultiCellWidget(d->searchBar, 3, 3, 1, 1); + grid->setRowStretch(2, 10); + + // ------------------------------------------------------------- + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumAdded(Album*)), + this, TQ_SLOT(slotAlbumAdded(Album*))); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumDeleted(Album*)), + this, TQ_SLOT(slotAlbumDeleted(Album*))); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumsCleared()), + this, TQ_SLOT(slotAlbumsCleared())); + + connect(d->folderView, TQ_SIGNAL(selectionChanged()), + this, TQ_SLOT(slotSelectionChanged())); + + connect(d->folderView, TQ_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint&, int)), + this, TQ_SLOT(slotContextMenu(TQListViewItem*, const TQPoint&, int))); + + connect(d->searchBar, TQ_SIGNAL(signalTextChanged(const TQString&)), + this, TQ_SLOT(slotSearchTextChanged(const TQString&))); + + // ------------------------------------------------------------- + + resize(650, 650); + slotSelectionChanged(); +} + +AlbumSelectDialog::~AlbumSelectDialog() +{ + delete d; +} + +void AlbumSelectDialog::slotAlbumAdded(Album* album) +{ + if (!album || album->type() != Album::PHYSICAL) + return; + + FolderItem* parentItem = (FolderItem*)(album->parent()->extraData(d->folderView)); + + if (!parentItem) + { + DWarning() << "Failed to find parent for Album " + << album->title() << endl; + return; + } + + TDEIconLoader *iconLoader = TDEApplication::kApplication()->iconLoader(); + TQPixmap icon = iconLoader->loadIcon("folder", TDEIcon::NoGroup, + AlbumSettings::instance()->getDefaultTreeIconSize(), + TDEIcon::DefaultState, 0, true); + + FolderItem* viewItem = new FolderItem(parentItem, album->title()); + viewItem->setPixmap(0, icon); + album->setExtraData(d->folderView, viewItem); + d->albumMap.insert(viewItem, (PAlbum*)album); +} + +void AlbumSelectDialog::slotAlbumDeleted(Album* album) +{ + if (!album || album->type() != Album::PHYSICAL) + return; + + FolderItem* viewItem = (FolderItem*)(album->extraData(d->folderView)); + + if (viewItem) + { + delete viewItem; + album->removeExtraData(d->folderView); + d->albumMap.remove(viewItem); + } +} + +void AlbumSelectDialog::slotAlbumsCleared() +{ + d->folderView->clear(); +} + +void AlbumSelectDialog::slotSelectionChanged() +{ + TQListViewItem* selItem = 0; + TQListViewItemIterator it(d->folderView); + + while (it.current()) + { + if (it.current()->isSelected()) + { + selItem = it.current(); + break; + } + ++it; + } + + if (!selItem || (selItem == d->folderView->firstChild()) && + !d->allowRootSelection) + { + enableButtonOK(false); + return; + } + + enableButtonOK(true); +} + +void AlbumSelectDialog::slotContextMenu(TQListViewItem *, const TQPoint &, int) +{ + TQPopupMenu popmenu(d->folderView); + TDEAction *action = new TDEAction(i18n( "Create New Album" ), + "albumfolder-new", 0, this, + TQ_SLOT( slotUser1() ), + &popmenu); + action->plug(&popmenu); + popmenu.exec(TQCursor::pos()); +} + +void AlbumSelectDialog::slotUser1() +{ + TQListViewItem* item = d->folderView->currentItem(); + if (!item) + item = d->folderView->firstChild(); + + if (!item) + return; + + PAlbum* album = d->albumMap[(FolderItem*)item]; + if (!album) + return; + + bool ok; + TQString newAlbumName = KInputDialog::getText(i18n("New Album Name"), + i18n("Creating new album in '%1'\n" + "Enter album name:") + .arg(album->prettyURL()), + d->newAlbumString, &ok, this); + if (!ok) + return; + + TQString errMsg; + PAlbum* newAlbum = AlbumManager::instance()->createPAlbum(album, newAlbumName, + TQString(), TQDate::currentDate(), + TQString(), errMsg); + if (!newAlbum) + { + KMessageBox::error(this, errMsg); + return; + } + + FolderItem* newItem = (FolderItem*)newAlbum->extraData(d->folderView); + if (newItem) + { + d->folderView->ensureItemVisible(newItem); + d->folderView->setSelected(newItem, true); + } +} + +PAlbum* AlbumSelectDialog::selectAlbum(TQWidget* parent, + PAlbum* albumToSelect, + const TQString& header, + const TQString& newAlbumString, + bool allowRootSelection ) +{ + AlbumSelectDialog dlg(parent, albumToSelect, + header, newAlbumString, + allowRootSelection); + + if (dlg.exec() != KDialogBase::Accepted) + return 0; + + FolderItem* item = (FolderItem*) dlg.d->folderView->currentItem(); + if (!item || (item == dlg.d->folderView->firstChild()) && + !allowRootSelection) + { + return 0; + } + + return dlg.d->albumMap[item]; +} + +void AlbumSelectDialog::slotSearchTextChanged(const TQString& filter) +{ + TQString search = filter.lower(); + + bool atleastOneMatch = false; + + AlbumList pList = AlbumManager::instance()->allPAlbums(); + for (AlbumList::iterator it = pList.begin(); it != pList.end(); ++it) + { + PAlbum* palbum = (PAlbum*)(*it); + + // don't touch the root Album + if (palbum->isRoot()) + continue; + + bool match = palbum->title().lower().contains(search); + if (!match) + { + // check if any of the parents match the search + Album* parent = palbum->parent(); + while (parent && !parent->isRoot()) + { + if (parent->title().lower().contains(search)) + { + match = true; + break; + } + + parent = parent->parent(); + } + } + + if (!match) + { + // check if any of the children match the search + AlbumIterator it(palbum); + while (it.current()) + { + if ((*it)->title().lower().contains(search)) + { + match = true; + break; + } + ++it; + } + } + + FolderItem* viewItem = (FolderItem*) palbum->extraData(d->folderView); + + if (match) + { + atleastOneMatch = true; + + if (viewItem) + viewItem->setVisible(true); + } + else + { + if (viewItem) + { + viewItem->setVisible(false); + } + } + } + + d->searchBar->slotSearchResult(atleastOneMatch); +} + +} // namespace Digikam diff --git a/src/utilities/cameragui/albumselectdialog.h b/src/utilities/cameragui/albumselectdialog.h new file mode 100644 index 00000000..aea53319 --- /dev/null +++ b/src/utilities/cameragui/albumselectdialog.h @@ -0,0 +1,80 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-16 + * Description : a dialog to select a target album to download + * pictures from camera + * + * Copyright (C) 2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef ALBUMSELECTDIALOG_H +#define ALBUMSELECTDIALOG_H + +// TQt includes. + +#include <tqstring.h> + +// KDE includes. + +#include <kdialogbase.h> + +namespace Digikam +{ + +class PAlbum; +class AlbumSelectDialogPrivate; + +class AlbumSelectDialog : public KDialogBase +{ + TQ_OBJECT + + +public: + + AlbumSelectDialog(TQWidget* parent, PAlbum* albumToSelect, + const TQString& header=TQString(), + const TQString& newAlbumString=TQString(), + bool allowRootSelection=false); + ~AlbumSelectDialog(); + + + static PAlbum* selectAlbum(TQWidget* parent, + PAlbum* albumToSelect, + const TQString& header=TQString(), + const TQString& newAlbumString=TQString(), + bool allowRootSelection=false); + +private slots: + + void slotAlbumAdded(Album*); + void slotAlbumDeleted(Album*); + void slotAlbumsCleared(); + void slotSelectionChanged(); + void slotContextMenu(TQListViewItem *item, const TQPoint&, int); + void slotUser1(); + void slotSearchTextChanged(const TQString&); + +private: + + AlbumSelectDialogPrivate *d; +}; + +} // namespace Digikam + +#endif /* ALBUMSELECTDIALOG_H */ diff --git a/src/utilities/cameragui/animwidget.cpp b/src/utilities/cameragui/animwidget.cpp new file mode 100644 index 00000000..1d869cd9 --- /dev/null +++ b/src/utilities/cameragui/animwidget.cpp @@ -0,0 +1,131 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-21 + * Description : an animated busy widget + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqpainter.h> +#include <tqpixmap.h> +#include <tqpalette.h> +#include <tqcolor.h> +#include <tqtimer.h> + +// Local includes. + +#include "animwidget.h" +#include "animwidget.moc" + +namespace Digikam +{ + +class AnimWidgetPriv +{ +public: + + AnimWidgetPriv() + { + timer = 0; + pos = 0; + } + + int pos; + int size; + + TQTimer *timer; + + TQPixmap pix; +}; + +AnimWidget::AnimWidget(TQWidget* parent, int size) + : TQWidget(parent, 0, WResizeNoErase|WRepaintNoErase) +{ + d = new AnimWidgetPriv; + setBackgroundMode(TQt::NoBackground); + + d->size = size; + d->pix = TQPixmap(d->size, d->size); + setFixedSize(d->size, d->size); + + d->timer = new TQTimer(this); + + connect(d->timer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotTimeout())); +} + +AnimWidget::~AnimWidget() +{ + delete d; +} + +void AnimWidget::start() +{ + d->pos = 0; + d->timer->start(100); +} + +void AnimWidget::stop() +{ + d->pos = 0; + d->timer->stop(); + repaint(); +} + +void AnimWidget::paintEvent(TQPaintEvent*) +{ + d->pix.fill(colorGroup().background()); + TQPainter p(&d->pix); + + p.translate(d->size/2, d->size/2); + + if (d->timer->isActive()) + { + p.setPen(TQPen(colorGroup().text())); + p.rotate( d->pos ); + } + else + { + p.setPen(TQPen(colorGroup().dark())); + } + + for ( int i=0 ; i<12 ; i++ ) + { + p.drawLine(d->size/2-4, 0, d->size/2-2, 0); + p.rotate(30); + } + + p.end(); + bitBlt(this, 0, 0, &d->pix); +} + +void AnimWidget::slotTimeout() +{ + d->pos = (d->pos + 10) % 360; + repaint(); +} + +bool AnimWidget::running() const +{ + return d->timer->isActive(); +} + +} // namespace Digikam diff --git a/src/utilities/cameragui/animwidget.h b/src/utilities/cameragui/animwidget.h new file mode 100644 index 00000000..6a93f410 --- /dev/null +++ b/src/utilities/cameragui/animwidget.h @@ -0,0 +1,66 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-21 + * Description : an animated busy widget + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef ANIMWIDGET_H +#define ANIMWIDGET_H + +// TQt includes. + +#include <tqwidget.h> + +namespace Digikam +{ + +class AnimWidgetPriv; + +class AnimWidget : public TQWidget +{ + TQ_OBJECT + + +public: + + AnimWidget(TQWidget* parent, int size=28); + ~AnimWidget(); + + void start(); + void stop(); + bool running() const; + +protected: + + void paintEvent(TQPaintEvent*); + +private slots: + + void slotTimeout(); + +private: + + AnimWidgetPriv* d; +}; + +} // namespace Digikam + +#endif /* ANIMWIDGET_H */ diff --git a/src/utilities/cameragui/cameracontroller.cpp b/src/utilities/cameragui/cameracontroller.cpp new file mode 100644 index 00000000..34afa8ab --- /dev/null +++ b/src/utilities/cameragui/cameracontroller.cpp @@ -0,0 +1,1227 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-17 + * Description : digital camera controller + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +extern "C" +{ +#include <unistd.h> +} + +// C++ includes. + +#include <typeinfo> +#include <cstdio> + +// TQt includes. + +#include <tqthread.h> +#include <tqmutex.h> +#include <tqwaitcondition.h> +#include <tqevent.h> +#include <tqapplication.h> +#include <tqdeepcopy.h> +#include <tqvariant.h> +#include <tqimage.h> +#include <tqdatastream.h> +#include <tqfile.h> +#include <tqtimer.h> +#include <tqregexp.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kurl.h> +#include <tdemessagebox.h> +#include <tdeio/renamedlg.h> +#include <kstandarddirs.h> + +// Local includes. + +#include "ddebug.h" +#include "thumbnailsize.h" +#include "imagewindow.h" +#include "gpcamera.h" +#include "umscamera.h" +#include "dmetadata.h" +#include "jpegutils.h" +#include "mtqueue.h" +#include "cameracontroller.h" +#include "cameracontroller.moc" + +namespace Digikam +{ + +class CameraThread; + +class CameraCommand +{ +public: + + enum Action + { + gp_none = 0, + gp_connect, + gp_cancel, + gp_cameraInformations, + gp_listfolders, + gp_listfiles, + gp_download, + gp_upload, + gp_delete, + gp_lock, + gp_thumbnail, + gp_exif, + gp_open + }; + + Action action; + TQStringVariantMap map; +}; + +class CameraEvent : public TQCustomEvent +{ +public: + + enum State + { + gp_connected = 0, + gp_busy, + gp_listedfolders, + gp_listedfiles, + gp_downloadstarted, + gp_downloaded, + gp_downloadFailed, + gp_opened, + gp_uploaded, + gp_uploadFailed, + gp_deleted, + gp_deleteFailed, + gp_locked, + gp_lockFailed, + gp_thumbnailed, + gp_exif, + gp_cameraInformations, + gp_infomsg, + gp_errormsg + }; + + CameraEvent(State state) : + TQCustomEvent(TQEvent::User+state) + {} + + bool result; + TQString msg; + TQStringVariantMap map; +}; + +class CameraControllerPriv +{ +public: + + CameraControllerPriv() + { + close = false; + overwriteAll = false; + skipAll = false; + canceled = false; + downloadTotal = 0; + parent = 0; + timer = 0; + camera = 0; + thread = 0; + } + + bool close; + bool overwriteAll; + bool skipAll; + bool canceled; + + int downloadTotal; + + TQWidget *parent; + + TQTimer *timer; + + CameraThread *thread; + + DKCamera *camera; + + MTQueue<CameraCommand> cmdQueue; +}; + +class CameraThread : public TQThread +{ +public: + + CameraThread(CameraController* controller); + ~CameraThread(); + + void sendBusy(bool busy); + void sendError(const TQString& msg); + void sendInfo(const TQString& msg); + +protected: + + void run(); + +private: + + CameraControllerPriv *d; + + TQObject *parent; +}; + +CameraThread::CameraThread(CameraController* controller) + : d(controller->d), parent(controller) +{ +} + +CameraThread::~CameraThread() +{ +} + +void CameraThread::run() +{ + if (d->close) + return; + + sendBusy(true); + + CameraCommand* cmd = d->cmdQueue.dequeue(); + if (cmd) + { + switch (cmd->action) + { + case(CameraCommand::gp_connect): + { + sendInfo(i18n("Connecting to camera...")); + + bool result = d->camera->doConnect(); + + CameraEvent* event = new CameraEvent(CameraEvent::gp_connected); + event->result = result; + TQApplication::postEvent(parent, event); + + if (result) + sendInfo(i18n("Connection established")); + else + sendInfo(i18n("Connection failed")); + + break; + } + case(CameraCommand::gp_cameraInformations): + { + sendInfo(i18n("Getting camera information...")); + + TQString summary, manual, about; + + d->camera->cameraSummary(summary); + d->camera->cameraManual(manual); + d->camera->cameraAbout(about); + + CameraEvent* event = new CameraEvent(CameraEvent::gp_cameraInformations); + event->map.insert("summary", TQVariant(summary)); + event->map.insert("manual", TQVariant(manual)); + event->map.insert("about", TQVariant(about)); + TQApplication::postEvent(parent, event); + break; + } + case(CameraCommand::gp_listfolders): + { + sendInfo(i18n("Listing folders...")); + + TQStringList folderList; + folderList.append(d->camera->path()); + d->camera->getAllFolders(d->camera->path(), folderList); + + /* TODO: ugly hack since qt <= 3.1.2 does not define + TQStringList with TQDeepCopy as a friend. */ + TQValueList<TQString> flist(folderList); + + CameraEvent* event = new CameraEvent(CameraEvent::gp_listedfolders); + event->map.insert("folders", TQVariant(flist)); + TQApplication::postEvent(parent, event); + + sendInfo(i18n("The folders have been listed.")); + + break; + } + case(CameraCommand::gp_listfiles): + { + TQString folder = cmd->map["folder"].asString(); + + sendInfo(i18n("The files in %1 have been listed.").arg(folder)); + + GPItemInfoList itemsList; + // setting getImageDimensions to false is a huge speedup for UMSCamera + if (!d->camera->getItemsInfoList(folder, itemsList, false)) + { + sendError(i18n("Failed to list files in %1").arg(folder)); + } + + if (!itemsList.isEmpty()) + { + CameraEvent* event = new CameraEvent(CameraEvent::gp_listedfiles); + event->map.insert("folder", TQVariant(folder)); + + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + ds << itemsList; + + event->map.insert("files", TQVariant(ba)); + TQApplication::postEvent(parent, event); + } + + sendInfo(i18n("Listing files in %1 is complete").arg(folder)); + + break; + } + case(CameraCommand::gp_thumbnail): + { + TQString folder = cmd->map["folder"].asString(); + TQString file = cmd->map["file"].asString(); + + sendInfo(i18n("Getting thumbnails...")); + + TQImage thumbnail; + d->camera->getThumbnail(folder, file, thumbnail); + + if (!thumbnail.isNull()) + { + thumbnail = thumbnail.smoothScale(ThumbnailSize::Huge, ThumbnailSize::Huge, TQImage::ScaleMin); + + CameraEvent* event = new CameraEvent(CameraEvent::gp_thumbnailed); + event->map.insert("folder", TQVariant(folder)); + event->map.insert("file", TQVariant(file)); + event->map.insert("thumbnail", TQVariant(thumbnail)); + TQApplication::postEvent(parent, event); + } + + break; + } + case(CameraCommand::gp_exif): + { + TQString folder = cmd->map["folder"].asString(); + TQString file = cmd->map["file"].asString(); + + sendInfo(i18n("Getting EXIF information for %1/%2...").arg(folder).arg(file)); + + char* edata = 0; + int esize = 0; + d->camera->getExif(folder, file, &edata, esize); + + if (edata || esize) + { + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + ds.writeRawBytes(edata, esize); + delete [] edata; + + CameraEvent* event = new CameraEvent(CameraEvent::gp_exif); + event->map.insert("folder", TQVariant(folder)); + event->map.insert("file", TQVariant(file)); + event->map.insert("exifSize", TQVariant(esize)); + event->map.insert("exifData", TQVariant(ba)); + TQApplication::postEvent(parent, event); + } + break; + } + case(CameraCommand::gp_download): + { + TQString folder = cmd->map["folder"].asString(); + TQString file = cmd->map["file"].asString(); + TQString dest = cmd->map["dest"].asString(); + bool autoRotate = cmd->map["autoRotate"].asBool(); + bool fixDateTime = cmd->map["fixDateTime"].asBool(); + TQDateTime newDateTime = cmd->map["newDateTime"].asDateTime(); + bool setPhotographerId = cmd->map["setPhotographerId"].asBool(); + TQString author = cmd->map["author"].asString(); + TQString authorTitle = cmd->map["authorTitle"].asString(); + bool setCredits = cmd->map["setCredits"].asBool(); + TQString credit = cmd->map["credit"].asString(); + TQString source = cmd->map["source"].asString(); + TQString copyright = cmd->map["copyright"].asString(); + bool convertJpeg = cmd->map["convertJpeg"].asBool(); + TQString losslessFormat = cmd->map["losslessFormat"].asString(); + sendInfo(i18n("Downloading file %1...").arg(file)); + + // download to a temp file + + CameraEvent* event = new CameraEvent(CameraEvent::gp_downloadstarted); + event->map.insert("folder", TQVariant(folder)); + event->map.insert("file", TQVariant(file)); + event->map.insert("dest", TQVariant(dest)); + TQApplication::postEvent(parent, event); + + KURL tempURL(dest); + tempURL = tempURL.upURL(); + tempURL.addPath( TQString(".digikam-camera-tmp1-%1").arg(getpid()).append(file)); + DDebug() << "Downloading: " << file << " using (" << tempURL.path() << ")" << endl; + TQString temp = tempURL.path(); + + bool result = d->camera->downloadItem(folder, file, tempURL.path()); + + if (result && isJpegImage(tempURL.path())) + { + if (autoRotate) + { + DDebug() << "Exif autorotate: " << file << " using (" << tempURL.path() << ")" << endl; + sendInfo(i18n("EXIF rotating file %1...").arg(file)); + exifRotate(tempURL.path(), file); + } + + if (fixDateTime || setPhotographerId || setCredits) + { + DDebug() << "Set Metadata from: " << file << " using (" << tempURL.path() << ")" << endl; + sendInfo(i18n("Setting Metadata tags to file %1...").arg(file)); + DMetadata metadata(tempURL.path()); + + if (fixDateTime) + metadata.setImageDateTime(newDateTime, true); + + if (setPhotographerId) + metadata.setImagePhotographerId(author, authorTitle); + + if (setCredits) + metadata.setImageCredits(credit, source, copyright); + + metadata.applyChanges(); + } + + // Convert Jpeg file to lossless format if necessary, + // and move converted image to destination. + + if (convertJpeg) + { + DDebug() << "Convert to LossLess: " << file << " using (" << tempURL.path() << ")" << endl; + sendInfo(i18n("Converting %1 to lossless file format...").arg(file)); + + KURL tempURL2(dest); + tempURL2 = tempURL2.upURL(); + tempURL2.addPath( TQString(".digikam-camera-tmp2-%1").arg(getpid()).append(file)); + temp = tempURL2.path(); + + if (!jpegConvert(tempURL.path(), tempURL2.path(), file, losslessFormat)) + { + // convert failed. delete the temp file + unlink(TQFile::encodeName(tempURL.path())); + unlink(TQFile::encodeName(tempURL2.path())); + result = false; + } + else + { + // Else remove only the first temp file. + unlink(TQFile::encodeName(tempURL.path())); + } + } + } + + if (result) + { + CameraEvent* event = new CameraEvent(CameraEvent::gp_downloaded); + event->map.insert("folder", TQVariant(folder)); + event->map.insert("file", TQVariant(file)); + event->map.insert("dest", TQVariant(dest)); + event->map.insert("temp", TQVariant(temp)); + TQApplication::postEvent(parent, event); + } + else + { + CameraEvent* event = new CameraEvent(CameraEvent::gp_downloadFailed); + event->map.insert("folder", TQVariant(folder)); + event->map.insert("file", TQVariant(file)); + event->map.insert("dest", TQVariant(dest)); + TQApplication::postEvent(parent, event); + } + break; + } + case(CameraCommand::gp_open): + { + TQString folder = cmd->map["folder"].asString(); + TQString file = cmd->map["file"].asString(); + TQString dest = cmd->map["dest"].asString(); + + sendInfo(i18n("Retrieving file %1 from camera...").arg(file)); + + bool result = d->camera->downloadItem(folder, file, dest); + + if (result) + { + CameraEvent* event = new CameraEvent(CameraEvent::gp_opened); + event->map.insert("folder", TQVariant(folder)); + event->map.insert("file", TQVariant(file)); + event->map.insert("dest", TQVariant(dest)); + TQApplication::postEvent(parent, event); + } + else + { + sendError(i18n("Failed to retrieve file %1 from camera").arg(file)); + } + break; + } + case(CameraCommand::gp_upload): + { + TQString folder = cmd->map["destFolder"].asString(); + + // We will using the same source file name to create the dest file + // name in camera. + TQString file = cmd->map["destFile"].asString(); + + // The source file path to download in camera. + TQString src = cmd->map["srcFilePath"].asString(); + + sendInfo(i18n("Uploading file %1 to camera...").arg(file)); + + GPItemInfo itemsInfo; + + bool result = d->camera->uploadItem(folder, file, src, itemsInfo); + + if (result) + { + CameraEvent* event = new CameraEvent(CameraEvent::gp_uploaded); + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + ds << itemsInfo; + event->map.insert("info", TQVariant(ba)); + + TQApplication::postEvent(parent, event); + } + else + { + CameraEvent* event = new CameraEvent(CameraEvent::gp_uploadFailed); + event->map.insert("folder", TQVariant(folder)); + event->map.insert("file", TQVariant(file)); + event->map.insert("src", TQVariant(src)); + TQApplication::postEvent(parent, event); + } + break; + } + case(CameraCommand::gp_delete): + { + TQString folder = cmd->map["folder"].asString(); + TQString file = cmd->map["file"].asString(); + + sendInfo(i18n("Deleting file %1...").arg(file)); + + bool result = d->camera->deleteItem(folder, file); + + if (result) + { + CameraEvent* event = new CameraEvent(CameraEvent::gp_deleted); + event->map.insert("folder", TQVariant(folder)); + event->map.insert("file", TQVariant(file)); + TQApplication::postEvent(parent, event); + } + else + { + CameraEvent* event = new CameraEvent(CameraEvent::gp_deleteFailed); + event->map.insert("folder", TQVariant(folder)); + event->map.insert("file", TQVariant(file)); + TQApplication::postEvent(parent, event); + } + break; + } + case(CameraCommand::gp_lock): + { + TQString folder = cmd->map["folder"].asString(); + TQString file = cmd->map["file"].asString(); + bool lock = cmd->map["lock"].asBool(); + + sendInfo(i18n("Toggle lock file %1...").arg(file)); + + bool result = d->camera->setLockItem(folder, file, lock); + + if (result) + { + CameraEvent* event = new CameraEvent(CameraEvent::gp_locked); + event->map.insert("folder", TQVariant(folder)); + event->map.insert("file", TQVariant(file)); + TQApplication::postEvent(parent, event); + } + else + { + CameraEvent* event = new CameraEvent(CameraEvent::gp_lockFailed); + event->map.insert("folder", TQVariant(folder)); + event->map.insert("file", TQVariant(file)); + TQApplication::postEvent(parent, event); + } + break; + } + default: + DWarning() << k_funcinfo << " unknown action specified" << endl; + } + + delete cmd; + } + + sendBusy(false); +} + +void CameraThread::sendBusy(bool val) +{ + CameraEvent* event = new CameraEvent(CameraEvent::gp_busy); + event->result = val; + TQApplication::postEvent(parent, event); +} + +void CameraThread::sendError(const TQString& msg) +{ + CameraEvent* event = new CameraEvent(CameraEvent::gp_errormsg); + event->msg = msg; + TQApplication::postEvent(parent, event); +} + +void CameraThread::sendInfo(const TQString& msg) +{ + CameraEvent* event = new CameraEvent(CameraEvent::gp_infomsg); + event->msg = msg; + TQApplication::postEvent(parent, event); +} + + +//-- Camera Controller ------------------------------------------------------ + + +CameraController::CameraController(TQWidget* parent, const TQString& title, const TQString& model, + const TQString& port, const TQString& path) + : TQObject(parent) +{ + d = new CameraControllerPriv; + d->parent = parent; + d->canceled = false; + d->close = false; + d->overwriteAll = false; + d->skipAll = false; + d->downloadTotal = 0; + d->camera = 0; + + // URL parsing (c) Stephan Kulow + if (path.startsWith("camera:/")) + { + KURL url(path); + DDebug() << "path " << path << " " << url << " " << url.host() << endl; + TQString xport = url.host(); + if (xport.startsWith("usb:")) + { + DDebug() << "xport " << xport << endl; + TQRegExp x = TQRegExp("(usb:[0-9,]*)"); + + if (x.search(xport) != -1) + { + TQString usbport = x.cap(1); + DDebug() << "USB " << xport << " " << usbport << endl; + // if ((xport == usbport) || ((count == 1) && (xport == "usb:"))) { + // model = xmodel; + d->camera = new GPCamera(title, url.user(), "usb:", "/"); + // } + } + } + } + + if (!d->camera) + { + if (model.lower() == "directory browse") + d->camera = new UMSCamera(title, model, port, path); + else + d->camera = new GPCamera(title, model, port, path); + } + + d->thread = new CameraThread(this); + d->timer = new TQTimer(this); + + connect(d->timer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotProcessNext())); + + d->timer->start(50, false); +} + +CameraController::~CameraController() +{ + if (d->timer->isActive()) + { + d->timer->stop(); + delete d->timer; + } + + d->camera->cancel(); + d->canceled = true; + d->close = true; + + while (d->thread->running()) + d->thread->wait(); + + delete d->thread; + delete d->camera; + delete d; +} + +TQString CameraController::getCameraPath() +{ + if (!d->camera) return TQString(); + return d->camera->path(); +} + +TQString CameraController::getCameraTitle() +{ + if (!d->camera) return TQString(); + return d->camera->title(); +} + +void CameraController::slotConnect() +{ + d->canceled = false; + CameraCommand *cmd = new CameraCommand; + cmd->action = CameraCommand::gp_connect; + d->cmdQueue.enqueue(cmd); +} + +void CameraController::listFolders() +{ + d->canceled = false; + CameraCommand *cmd = new CameraCommand; + cmd->action = CameraCommand::gp_listfolders; + d->cmdQueue.enqueue(cmd); +} + +void CameraController::listFiles(const TQString& folder) +{ + d->canceled = false; + CameraCommand *cmd = new CameraCommand; + cmd->action = CameraCommand::gp_listfiles; + cmd->map.insert("folder", TQVariant(folder)); + d->cmdQueue.enqueue(cmd); +} + +void CameraController::getThumbnail(const TQString& folder, const TQString& file) +{ + d->canceled = false; + CameraCommand *cmd = new CameraCommand; + cmd->action = CameraCommand::gp_thumbnail; + cmd->map.insert("folder", TQVariant(folder)); + cmd->map.insert("file", TQVariant(file)); + d->cmdQueue.enqueue(cmd); +} + +void CameraController::getExif(const TQString& folder, const TQString& file) +{ + d->canceled = false; + CameraCommand *cmd = new CameraCommand; + cmd->action = CameraCommand::gp_exif; + cmd->map.insert("folder", TQVariant(folder)); + cmd->map.insert("file", TQVariant(file)); + d->cmdQueue.enqueue(cmd); +} + +void CameraController::getCameraInformations() +{ + d->canceled = false; + CameraCommand *cmd = new CameraCommand; + cmd->action = CameraCommand::gp_cameraInformations; + d->cmdQueue.enqueue(cmd); +} + +void CameraController::upload(const TQFileInfo& srcFileInfo, const TQString& destFile, const TQString& destFolder) +{ + d->canceled = false; + CameraCommand *cmd = new CameraCommand; + cmd->action = CameraCommand::gp_upload; + cmd->map.insert("srcFilePath", TQVariant(srcFileInfo.filePath())); + cmd->map.insert("destFile", TQVariant(destFile)); + cmd->map.insert("destFolder", TQVariant(destFolder)); + d->cmdQueue.enqueue(cmd); + DDebug() << "Uploading '" << srcFileInfo.filePath() << "' into camera : '" << destFolder << + "' (" << destFile << ")" << endl; +} + +void CameraController::downloadPrep() +{ + d->overwriteAll = false; + d->skipAll = false; + d->downloadTotal = 0; +} + +void CameraController::download(DownloadSettingsContainer downloadSettings) +{ + d->canceled = false; + CameraCommand *cmd = new CameraCommand; + cmd->action = CameraCommand::gp_download; + cmd->map.insert("folder", TQVariant(downloadSettings.folder)); + cmd->map.insert("file", TQVariant(downloadSettings.file)); + cmd->map.insert("dest", TQVariant(downloadSettings.dest)); + cmd->map.insert("autoRotate", TQVariant(downloadSettings.autoRotate)); + cmd->map.insert("fixDateTime", TQVariant(downloadSettings.fixDateTime)); + cmd->map.insert("newDateTime", TQVariant(downloadSettings.newDateTime)); + cmd->map.insert("setPhotographerId", TQVariant(downloadSettings.setPhotographerId)); + cmd->map.insert("author", TQVariant(downloadSettings.author)); + cmd->map.insert("authorTitle", TQVariant(downloadSettings.authorTitle)); + cmd->map.insert("setCredits", TQVariant(downloadSettings.setCredits)); + cmd->map.insert("credit", TQVariant(downloadSettings.credit)); + cmd->map.insert("source", TQVariant(downloadSettings.source)); + cmd->map.insert("copyright", TQVariant(downloadSettings.copyright)); + cmd->map.insert("convertJpeg", TQVariant(downloadSettings.convertJpeg)); + cmd->map.insert("losslessFormat", TQVariant(downloadSettings.losslessFormat)); + d->cmdQueue.enqueue(cmd); +} + +void CameraController::deleteFile(const TQString& folder, const TQString& file) +{ + d->canceled = false; + CameraCommand *cmd = new CameraCommand; + cmd->action = CameraCommand::gp_delete; + cmd->map.insert("folder", TQVariant(folder)); + cmd->map.insert("file", TQVariant(file)); + d->cmdQueue.enqueue(cmd); +} + +void CameraController::lockFile(const TQString& folder, const TQString& file, bool lock) +{ + d->canceled = false; + CameraCommand *cmd = new CameraCommand; + cmd->action = CameraCommand::gp_lock; + cmd->map.insert("folder", TQVariant(folder)); + cmd->map.insert("file", TQVariant(file)); + cmd->map.insert("lock", TQVariant(lock)); + d->cmdQueue.enqueue(cmd); +} + +void CameraController::openFile(const TQString& folder, const TQString& file) +{ + d->canceled = false; + CameraCommand *cmd = new CameraCommand; + cmd->action = CameraCommand::gp_open; + cmd->map.insert("folder", TQVariant(folder)); + cmd->map.insert("file", TQVariant(file)); + cmd->map.insert("dest", TQVariant(locateLocal("tmp", file))); + d->cmdQueue.enqueue(cmd); +} + +void CameraController::slotCancel() +{ + d->canceled = true; + d->cmdQueue.flush(); + d->camera->cancel(); +} + +void CameraController::customEvent(TQCustomEvent* e) +{ + CameraEvent* event = dynamic_cast<CameraEvent*>(e); + if (!event) + { + return; + } + + switch(event->type()-TQEvent::User) + { + case (CameraEvent::gp_connected) : + { + emit signalConnected(event->result); + break; + } + case (CameraEvent::gp_cameraInformations) : + { + TQString summary = TQDeepCopy<TQString>(event->map["summary"].asString()); + TQString manual = TQDeepCopy<TQString>(event->map["manual"].asString()); + TQString about = TQDeepCopy<TQString>(event->map["about"].asString()); + emit signalCameraInformations(summary, manual, about); + break; + } + case (CameraEvent::gp_errormsg) : + { + emit signalErrorMsg(TQDeepCopy<TQString>(event->msg)); + break; + } + case (CameraEvent::gp_busy) : + { + if (event->result) + emit signalBusy(true); + break; + } + case (CameraEvent::gp_infomsg) : + { + if (!d->canceled) + emit signalInfoMsg(TQDeepCopy<TQString>(event->msg)); + break; + } + case (CameraEvent::gp_listedfolders) : + { + /* TODO: ugly hack since qt <= 3.1.2 does not define + TQStringList with TQDeepCopy as a friend. */ + TQValueList<TQVariant> flist = TQDeepCopy< TQValueList<TQVariant> >(event->map["folders"].toList()); + + TQStringList folderList; + TQValueList<TQVariant>::Iterator it; + for (it = flist.begin(); it != flist.end(); ++it ) + { + folderList.append(TQDeepCopy<TQString>((*it).asString())); + } + + emit signalFolderList(folderList); + break; + } + case (CameraEvent::gp_listedfiles) : + { + TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString()); + TQByteArray ba = TQDeepCopy<TQByteArray>(event->map["files"].asByteArray()); + TQDataStream ds(ba, IO_ReadOnly); + GPItemInfoList items; + ds >> items; + emit signalFileList(items); + break; + } + case (CameraEvent::gp_thumbnailed) : + { + TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString()); + TQString file = TQDeepCopy<TQString>(event->map["file"].asString()); + TQImage thumb = TQDeepCopy<TQImage>(event->map["thumbnail"].asImage()); + emit signalThumbnail(folder, file, thumb); + break; + } + case (CameraEvent::gp_exif) : + { + TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString()); + TQString file = TQDeepCopy<TQString>(event->map["file"].asString()); + TQByteArray ba = TQDeepCopy<TQByteArray>(event->map["exifData"].asByteArray()); + emit signalExifData(ba); + break; + } + case (CameraEvent::gp_downloadstarted) : + { + TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString()); + TQString file = TQDeepCopy<TQString>(event->map["file"].asString()); + emit signalDownloaded(folder, file, GPItemInfo::DownloadStarted); + break; + } + case (CameraEvent::gp_downloaded) : + { + TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString()); + TQString file = TQDeepCopy<TQString>(event->map["file"].asString()); + TQString dest = TQDeepCopy<TQString>(event->map["dest"].asString()); + TQString temp = TQDeepCopy<TQString>(event->map["temp"].asString()); + + d->timer->stop(); + + bool skip = false; + bool cancel = false; + bool overwrite = false; + + // Check if dest file already exist. + + if (!d->overwriteAll) + { + TQFileInfo info(dest); + + while (info.exists()) + { + if (d->skipAll) + { + skip = true; + break; + } + + TDEIO::RenameDlg dlg(d->parent, i18n("Rename File"), + folder + TQString("/") + file, dest, + TDEIO::RenameDlg_Mode(TDEIO::M_MULTI | TDEIO::M_OVERWRITE | TDEIO::M_SKIP)); + + int result = dlg.exec(); + dest = dlg.newDestURL().path(); + info = TQFileInfo(dest); + + switch (result) + { + case TDEIO::R_CANCEL: + { + cancel = true; + break; + } + case TDEIO::R_SKIP: + { + skip = true; + break; + } + case TDEIO::R_AUTO_SKIP: + { + d->skipAll = true; + skip = true; + break; + } + case TDEIO::R_OVERWRITE: + { + overwrite = true; + break; + } + case TDEIO::R_OVERWRITE_ALL: + { + d->overwriteAll = true; + overwrite = true; + break; + } + default: + break; + } + + if (cancel || skip || overwrite) + break; + } + } + + if (cancel) + { + unlink(TQFile::encodeName(temp)); + slotCancel(); + d->timer->start(50); + emit signalSkipped(folder, file); + return; + } + else if (skip) + { + unlink(TQFile::encodeName(temp)); + d->timer->start(50); + emit signalInfoMsg(i18n("Skipped file %1").arg(file)); + emit signalSkipped(folder, file); + return; + } + + // move the file to the destination file + if (rename(TQFile::encodeName(temp), TQFile::encodeName(dest)) != 0) + { + // rename failed. delete the temp file + unlink(TQFile::encodeName(temp)); + d->timer->start(50); + emit signalDownloaded(folder, file, GPItemInfo::DownloadFailed); + } + else + { + d->timer->start(50); + emit signalDownloaded(folder, file, GPItemInfo::DownloadedYes); + } + break; + } + case (CameraEvent::gp_downloadFailed) : + { + TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString()); + TQString file = TQDeepCopy<TQString>(event->map["file"].asString()); + + d->timer->stop(); + + TQString msg = i18n("Failed to download file \"%1\".").arg(file); + + if (!d->canceled) + { + if (d->cmdQueue.isEmpty()) + { + KMessageBox::error(d->parent, msg); + } + else + { + msg += i18n(" Do you want to continue?"); + int result = KMessageBox::warningContinueCancel(d->parent, msg); + if (result != KMessageBox::Continue) + slotCancel(); + } + } + + d->timer->start(50); + emit signalDownloaded(folder, file, GPItemInfo::DownloadFailed); + break; + } + case (CameraEvent::gp_uploaded) : + { + TQByteArray ba = TQDeepCopy<TQByteArray>(event->map["info"].asByteArray()); + TQDataStream ds(ba, IO_ReadOnly); + GPItemInfo itemInfo; + ds >> itemInfo; + + emit signalUploaded(itemInfo); + break; + } + case (CameraEvent::gp_uploadFailed) : + { + TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString()); + TQString file = TQDeepCopy<TQString>(event->map["file"].asString()); + TQString src = TQDeepCopy<TQString>(event->map["src"].asString()); + + d->timer->stop(); + + TQString msg = i18n("Failed to upload file \"%1\".").arg(file); + + if (!d->canceled) + { + if (d->cmdQueue.isEmpty()) + { + KMessageBox::error(d->parent, msg); + } + else + { + msg += i18n(" Do you want to continue?"); + int result = KMessageBox::warningContinueCancel(d->parent, msg); + if (result != KMessageBox::Continue) + slotCancel(); + } + } + + d->timer->start(50); + break; + } + case (CameraEvent::gp_deleted) : + { + TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString()); + TQString file = TQDeepCopy<TQString>(event->map["file"].asString()); + emit signalDeleted(folder, file, true); + break; + } + case (CameraEvent::gp_deleteFailed) : + { + TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString()); + TQString file = TQDeepCopy<TQString>(event->map["file"].asString()); + + d->timer->stop(); + emit signalDeleted(folder, file, false); + + TQString msg = i18n("Failed to delete file \"%1\".").arg(file); + + if (!d->canceled) + { + if (d->cmdQueue.isEmpty()) + { + KMessageBox::error(d->parent, msg); + } + else + { + msg += i18n(" Do you want to continue?"); + int result = KMessageBox::warningContinueCancel(d->parent, msg); + if (result != KMessageBox::Continue) + slotCancel(); + } + } + + d->timer->start(50); + break; + } + case (CameraEvent::gp_locked) : + { + TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString()); + TQString file = TQDeepCopy<TQString>(event->map["file"].asString()); + emit signalLocked(folder, file, true); + break; + } + case (CameraEvent::gp_lockFailed) : + { + TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString()); + TQString file = TQDeepCopy<TQString>(event->map["file"].asString()); + + d->timer->stop(); + emit signalLocked(folder, file, false); + + TQString msg = i18n("Failed to toggle lock file \"%1\".").arg(file); + + if (!d->canceled) + { + if (d->cmdQueue.isEmpty()) + { + KMessageBox::error(d->parent, msg); + } + else + { + msg += i18n(" Do you want to continue?"); + int result = KMessageBox::warningContinueCancel(d->parent, msg); + if (result != KMessageBox::Continue) + slotCancel(); + } + } + + d->timer->start(50); + break; + } + case (CameraEvent::gp_opened) : + { + TQString file = TQDeepCopy<TQString>(event->map["file"].asString()); + TQString dest = TQDeepCopy<TQString>(event->map["dest"].asString()); + + KURL url(dest); + KURL::List urlList; + urlList << url; + + ImageWindow *im = ImageWindow::imagewindow(); + im->loadURL(urlList, url, i18n("Camera \"%1\"").arg(d->camera->model()), false); + + if (im->isHidden()) + im->show(); + else + im->raise(); + + im->setFocus(); + break; + } + default: + { + DWarning() << k_funcinfo << "Unknown event" << endl; + } + } +} + +void CameraController::slotProcessNext() +{ + if (d->thread->running()) + return; + + if (d->cmdQueue.isEmpty()) + { + emit signalBusy(false); + return; + } + + d->timer->stop(); + emit signalBusy(true); + + CameraCommand* cmd = d->cmdQueue.head(); + + TQString folder; + TQString file; + TQString dest; + + if ((cmd->action == CameraCommand::gp_exif) && + (typeid(*(d->camera)) == typeid(UMSCamera))) + { + folder = TQDeepCopy<TQString>(cmd->map["folder"].asString()); + file = TQDeepCopy<TQString>(cmd->map["file"].asString()); + + emit signalExifFromFile(folder, file); + + d->cmdQueue.dequeue(); + d->timer->start(50, false); + return; + } + + if (cmd->action == CameraCommand::gp_download) + { + folder = TQDeepCopy<TQString>(cmd->map["folder"].asString()); + file = TQDeepCopy<TQString>(cmd->map["file"].asString()); + dest = TQDeepCopy<TQString>(cmd->map["dest"].asString()); + cmd->map["dest"] = TQVariant(TQDeepCopy<TQString>(dest)); + } + + d->thread->start(); + d->timer->start(50, false); +} + +} // namespace Digikam diff --git a/src/utilities/cameragui/cameracontroller.h b/src/utilities/cameragui/cameracontroller.h new file mode 100644 index 00000000..45e7fede --- /dev/null +++ b/src/utilities/cameragui/cameracontroller.h @@ -0,0 +1,111 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-17 + * Description : digital camera controller + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef CAMERACONTROLLER_H +#define CAMERACONTROLLER_H + +// TQt includes. + +#include <tqobject.h> +#include <tqstring.h> +#include <tqfileinfo.h> + +// Local includes. + +#include "downloadsettingscontainer.h" +#include "gpiteminfo.h" + +namespace Digikam +{ + +class CameraControllerPriv; + +class CameraController : public TQObject +{ + TQ_OBJECT + + +public: + + CameraController(TQWidget* parent, const TQString& title, const TQString& model, + const TQString& port, const TQString& path); + ~CameraController(); + + void listFolders(); + void listFiles(const TQString& folder); + void getThumbnail(const TQString& folder, const TQString& file); + void getExif(const TQString& folder, const TQString& file); + void getCameraInformations(); + TQString getCameraPath(); + TQString getCameraTitle(); + + void downloadPrep(); + void download(DownloadSettingsContainer downloadSettings); + void upload(const TQFileInfo& srcFileInfo, const TQString& destFile, const TQString& destFolder); + void deleteFile(const TQString& folder, const TQString& file); + void lockFile(const TQString& folder, const TQString& file, bool lock); + void openFile(const TQString& folder, const TQString& file); + +signals: + + void signalBusy(bool val); + void signalInfoMsg(const TQString& msg); + void signalErrorMsg(const TQString& msg); + void signalCameraInformations(const TQString& summary, const TQString& manual, const TQString& about); + + void signalConnected(bool val); + void signalFolderList(const TQStringList& folderList); + void signalFileList(const GPItemInfoList& infoList); + void signalUploaded(const GPItemInfo& itemInfo); + void signalDownloaded(const TQString& folder, const TQString& file, int status); + void signalSkipped(const TQString& folder, const TQString& file); + void signalDeleted(const TQString& folder, const TQString& file, bool status); + void signalLocked(const TQString& folder, const TQString& file, bool status); + void signalThumbnail(const TQString& folder, const TQString& file, const TQImage& thumb); + void signalExifFromFile(const TQString& folder, const TQString& file); + void signalExifData(const TQByteArray& exifData); + +protected: + + void customEvent(TQCustomEvent* e); + +public slots: + + void slotCancel(); + void slotConnect(); + +private slots: + + void slotProcessNext(); + +private: + + CameraControllerPriv *d; + + friend class CameraThread; +}; + +} // namespace Digikam + +#endif /* CAMERACONTROLLER_H */ diff --git a/src/utilities/cameragui/camerafolderdialog.cpp b/src/utilities/cameragui/camerafolderdialog.cpp new file mode 100644 index 00000000..93feb0ab --- /dev/null +++ b/src/utilities/cameragui/camerafolderdialog.cpp @@ -0,0 +1,138 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-07-24 + * Description : a dialog to select a camera folders. + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlabel.h> +#include <tqlayout.h> +#include <tqframe.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kiconloader.h> +#include <tdeapplication.h> + +// Local includes. + +#include "ddebug.h" +#include "cameraiconview.h" +#include "camerafolderitem.h" +#include "camerafolderview.h" +#include "camerafolderdialog.h" +#include "camerafolderdialog.moc" + +namespace Digikam +{ + +CameraFolderDialog::CameraFolderDialog(TQWidget *parent, CameraIconView *cameraView, + const TQStringList& cameraFolderList, + const TQString& cameraName, const TQString& rootPath) + : KDialogBase(parent, 0, true, + i18n("%1 - Select Camera Folder").arg(cameraName), + Help|Ok|Cancel, Ok, true) +{ + setHelp("camerainterface.anchor", "digikam"); + enableButtonOK(false); + + m_rootPath = rootPath; + + TQFrame *page = makeMainWidget(); + TQGridLayout* grid = new TQGridLayout(page, 2, 1, 0, spacingHint()); + + m_folderView = new CameraFolderView(page); + TQLabel *logo = new TQLabel(page); + TQLabel *message = new TQLabel(page); + + TDEIconLoader* iconLoader = TDEApplication::kApplication()->iconLoader(); + logo->setPixmap(iconLoader->loadIcon("digikam", TDEIcon::NoGroup, 128, TDEIcon::DefaultState, 0, true)); + message->setText(i18n("<p>Please select the camera folder " + "where you want to upload the images.</p>")); + + grid->addMultiCellWidget(logo, 0, 0, 0, 0); + grid->addMultiCellWidget(message, 1, 1, 0, 0); + grid->addMultiCellWidget(m_folderView, 0, 2, 1, 1); + grid->setRowStretch(2, 10); + + m_folderView->addVirtualFolder(cameraName); + m_folderView->addRootFolder("/", cameraView->countItemsByFolder(rootPath)); + + for (TQStringList::const_iterator it = cameraFolderList.begin(); + it != cameraFolderList.end(); ++it) + { + TQString folder(*it); + if (folder.startsWith(rootPath) && rootPath != TQString("/")) + folder.remove(0, rootPath.length()); + + if (folder != TQString("/") && !folder.isEmpty()) + { + TQString root = folder.section( '/', 0, -2 ); + if (root.isEmpty()) root = TQString("/"); + + TQString sub = folder.section( '/', -1 ); + m_folderView->addFolder(root, sub, cameraView->countItemsByFolder(*it)); + DDebug() << "Camera folder: '" << folder << "' (root='" << root << "', sub='" <<sub <<"')" << endl; + } + } + + connect(m_folderView, TQ_SIGNAL(signalFolderChanged(CameraFolderItem*)), + this, TQ_SLOT(slotFolderPathSelectionChanged(CameraFolderItem*))); + + resize(500, 500); +} + +CameraFolderDialog::~CameraFolderDialog() +{ +} + +TQString CameraFolderDialog::selectedFolderPath() +{ + TQListViewItem *item = m_folderView->currentItem(); + if (!item) return TQString(); + + CameraFolderItem *folderItem = static_cast<CameraFolderItem *>(item); + if (folderItem->isVirtualFolder()) + return TQString(m_rootPath); + + // Case of Gphoto2 cameras. No need to duplicate root '/'. + if (m_rootPath == TQString("/")) + return(folderItem->folderPath()); + + return(m_rootPath + folderItem->folderPath()); +} + +void CameraFolderDialog::slotFolderPathSelectionChanged(CameraFolderItem* item) +{ + if (item) + { + enableButtonOK(true); + DDebug() << "Camera folder path: " << selectedFolderPath() << endl; + } + else + { + enableButtonOK(false); + } +} + +} // namespace Digikam + diff --git a/src/utilities/cameragui/camerafolderdialog.h b/src/utilities/cameragui/camerafolderdialog.h new file mode 100644 index 00000000..efc7586a --- /dev/null +++ b/src/utilities/cameragui/camerafolderdialog.h @@ -0,0 +1,68 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-07-24 + * Description : a dialog to select a camera folders. + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef CAMERAFOLDERDIALOG_H +#define CAMERAFOLDERDIALOG_H + +// TQt includes. + +#include <tqstring.h> + +// KDE includes. + +#include <kdialogbase.h> + +namespace Digikam +{ + +class CameraIconView; +class CameraFolderView; +class CameraFolderItem; + +class CameraFolderDialog : public KDialogBase +{ + TQ_OBJECT + + +public: + + CameraFolderDialog(TQWidget *parent, CameraIconView *cameraView, const TQStringList& cameraFolderList, + const TQString& cameraName, const TQString& rootPath); + ~CameraFolderDialog(); + + TQString selectedFolderPath(); + +private slots: + + void slotFolderPathSelectionChanged(CameraFolderItem* item); + +private: + + TQString m_rootPath; + + CameraFolderView *m_folderView; +}; + +} // namespace Digikam + +#endif /* CAMERAFOLDERDIALOG_H */ diff --git a/src/utilities/cameragui/camerafolderitem.cpp b/src/utilities/cameragui/camerafolderitem.cpp new file mode 100644 index 00000000..f53508a6 --- /dev/null +++ b/src/utilities/cameragui/camerafolderitem.cpp @@ -0,0 +1,108 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-23 + * Description : A widget to display a camera folder. + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published bythe Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// Local includes. + +#include "camerafolderitem.h" + +namespace Digikam +{ + +class CameraFolderItemPriv +{ +public: + + CameraFolderItemPriv() + { + count = 0; + } + + bool virtualFolder; + + int count; + + TQString folderName; + TQString folderPath; + TQString name; +}; + +CameraFolderItem::CameraFolderItem(TQListView* parent, const TQString& name, const TQPixmap& pixmap) + : TQListViewItem(parent, name) +{ + d = new CameraFolderItemPriv; + d->virtualFolder = true; + d->name = name; + setPixmap(0, pixmap); +} + +CameraFolderItem::CameraFolderItem(TQListViewItem* parent, const TQString& folderName, + const TQString& folderPath, const TQPixmap& pixmap) + : TQListViewItem(parent, folderName) +{ + d = new CameraFolderItemPriv; + d->folderName = folderName; + d->folderPath = folderPath; + d->virtualFolder = false; + d->name = folderName; + setPixmap(0, pixmap); +} + +CameraFolderItem::~CameraFolderItem() +{ + delete d; +} + +bool CameraFolderItem::isVirtualFolder() +{ + return d->virtualFolder; +} + +TQString CameraFolderItem::folderName() +{ + return d->folderName; +} + +TQString CameraFolderItem::folderPath() +{ + return d->folderPath; +} + +void CameraFolderItem::changeCount(int val) +{ + d->count += val; + setText(0, TQString("%1 (%2)").arg(d->name).arg(TQString::number(d->count))); +} + +void CameraFolderItem::setCount(int val) +{ + d->count = val; + setText(0, TQString("%1 (%2)").arg(d->name).arg(TQString::number(d->count))); +} + +int CameraFolderItem::count() +{ + return d->count; +} + +} // namespace Digikam diff --git a/src/utilities/cameragui/camerafolderitem.h b/src/utilities/cameragui/camerafolderitem.h new file mode 100644 index 00000000..dde2c5c6 --- /dev/null +++ b/src/utilities/cameragui/camerafolderitem.h @@ -0,0 +1,69 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-23 + * Description : A widget to display a camera folder. + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published bythe Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef CAMERAFOLDERITEM_H +#define CAMERAFOLDERITEM_H + +// TQt includes. + +#include <tqstring.h> +#include <tqlistview.h> + +// KDE includes. + +#include <kiconloader.h> + +namespace Digikam +{ + +class CameraFolderItemPriv; + +class CameraFolderItem : public TQListViewItem +{ + +public: + + CameraFolderItem(TQListView* parent, const TQString& name, + const TQPixmap& pixmap=SmallIcon("folder")); + + CameraFolderItem(TQListViewItem* parent, const TQString& folderName, const TQString& folderPath, + const TQPixmap& pixmap=SmallIcon("folder")); + + ~CameraFolderItem(); + + TQString folderName(); + TQString folderPath(); + bool isVirtualFolder(); + void changeCount(int val); + void setCount(int val); + int count(); + +private: + + CameraFolderItemPriv* d; +}; + +} // namespace Digikam + +#endif /* CAMERAFOLDERITEM_H */ diff --git a/src/utilities/cameragui/camerafolderview.cpp b/src/utilities/cameragui/camerafolderview.cpp new file mode 100644 index 00000000..877c4b51 --- /dev/null +++ b/src/utilities/cameragui/camerafolderview.cpp @@ -0,0 +1,169 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-23 + * Description : A widget to display a list of camera folders. + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published bythe Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include <tdelocale.h> +#include <kiconloader.h> + +// Local includes. + +#include "ddebug.h" +#include "camerafolderitem.h" +#include "camerafolderview.h" +#include "camerafolderview.moc" + +namespace Digikam +{ + +class CameraFolderViewPriv +{ +public: + + CameraFolderViewPriv() + { + virtualFolder = 0; + rootFolder = 0; + cameraName = TQString("Camera"); + } + + TQString cameraName; + + CameraFolderItem *virtualFolder; + CameraFolderItem *rootFolder; +}; + +CameraFolderView::CameraFolderView(TQWidget* parent) + : TQListView(parent) +{ + d = new CameraFolderViewPriv; + + addColumn(i18n("Camera Folders")); + setColumnWidthMode( 0, TQListView::Maximum ); + setResizeMode( TQListView::AllColumns ); + setSelectionMode(TQListView::Single); + + connect(this, TQ_SIGNAL(currentChanged(TQListViewItem*)), + this, TQ_SLOT(slotCurrentChanged(TQListViewItem*))); + + connect(this, TQ_SIGNAL(clicked(TQListViewItem*)), + this, TQ_SLOT(slotCurrentChanged(TQListViewItem*))); +} + +CameraFolderView::~CameraFolderView() +{ + delete d; +} + +void CameraFolderView::addVirtualFolder(const TQString& name, const TQPixmap& pixmap) +{ + d->cameraName = name; + d->virtualFolder = new CameraFolderItem(this, d->cameraName, pixmap); + d->virtualFolder->setOpen(true); + d->virtualFolder->setSelected(false); + d->virtualFolder->setSelectable(false); +} + +void CameraFolderView::addRootFolder(const TQString& folder, int nbItems, const TQPixmap& pixmap) +{ + d->rootFolder = new CameraFolderItem(d->virtualFolder, folder, folder, pixmap); + d->rootFolder->setOpen(true); + d->rootFolder->setCount(nbItems); +} + +CameraFolderItem* CameraFolderView::addFolder(const TQString& folder, const TQString& subFolder, + int nbItems, const TQPixmap& pixmap) +{ + CameraFolderItem *parentItem = findFolder(folder); + + DDebug() << "CameraFolderView: Adding Subfolder " << subFolder + << " of folder " << folder << endl; + + if (parentItem) + { + TQString path(folder); + + if (!folder.endsWith("/")) + path += '/'; + + path += subFolder; + CameraFolderItem* item = new CameraFolderItem(parentItem, subFolder, path, pixmap); + + DDebug() << "CameraFolderView: Added ViewItem with path " + << item->folderPath() << endl; + + item->setCount(nbItems); + item->setOpen(true); + return item; + } + else + { + DWarning() << "CameraFolderView: Couldn't find parent for subFolder " + << subFolder << " of folder " << folder << endl; + return 0; + } +} + +CameraFolderItem* CameraFolderView::findFolder(const TQString& folderPath) +{ + + TQListViewItemIterator it(this); + for ( ; it.current(); ++it) + { + CameraFolderItem* item = static_cast<CameraFolderItem*>(it.current()); + + if (item->folderPath() == folderPath) + return item; + } + + return 0; +} + +void CameraFolderView::slotCurrentChanged(TQListViewItem* item) +{ + if (!item) + emit signalFolderChanged(0); + else + emit signalFolderChanged(static_cast<CameraFolderItem *>(item)); +} + +CameraFolderItem* CameraFolderView::virtualFolder() +{ + return d->virtualFolder; +} + +CameraFolderItem* CameraFolderView::rootFolder() +{ + return d->rootFolder; +} + +void CameraFolderView::clear() +{ + TQListView::clear(); + d->virtualFolder = 0; + d->rootFolder = 0; + emit signalCleared(); +} + +} // namespace Digikam diff --git a/src/utilities/cameragui/camerafolderview.h b/src/utilities/cameragui/camerafolderview.h new file mode 100644 index 00000000..1b02fc9c --- /dev/null +++ b/src/utilities/cameragui/camerafolderview.h @@ -0,0 +1,83 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-23 + * Description : A widget to display a list of camera folders. + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published bythe Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef CAMERAFOLDERVIEW_H +#define CAMERAFOLDERVIEW_H + +// TQt includes. + +#include <tqstring.h> +#include <tqlistview.h> + +// KDE includes. + +#include <kiconloader.h> + +namespace Digikam +{ + +class CameraFolderItem; +class CameraFolderViewPriv; + +class CameraFolderView : public TQListView +{ + TQ_OBJECT + + +public: + + CameraFolderView(TQWidget* parent); + ~CameraFolderView(); + + void addVirtualFolder(const TQString& name, const TQPixmap& pixmap=SmallIcon("camera-photo")); + void addRootFolder(const TQString& folder, int nbItems, const TQPixmap& pixmap=SmallIcon("folder")); + + CameraFolderItem* addFolder(const TQString& folder, const TQString& subFolder, int nbItems, + const TQPixmap& pixmap=SmallIcon("folder")); + + CameraFolderItem* findFolder(const TQString& folderPath); + + CameraFolderItem* virtualFolder(); + CameraFolderItem* rootFolder(); + + virtual void clear(); + +signals: + + void signalFolderChanged(CameraFolderItem*); + void signalCleared(); + +private slots: + + void slotCurrentChanged(TQListViewItem*); + +private: + + CameraFolderViewPriv* d; + +}; + +} // namespace Digikam + +#endif /* CAMERAFOLDERVIEW_H */ diff --git a/src/utilities/cameragui/cameraiconitem.cpp b/src/utilities/cameragui/cameraiconitem.cpp new file mode 100644 index 00000000..c68f7855 --- /dev/null +++ b/src/utilities/cameragui/cameraiconitem.cpp @@ -0,0 +1,302 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-21 + * Description : camera icon view item + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqpainter.h> +#include <tqpixmap.h> + +// KDE includes. + +#include <kiconloader.h> + +// Local includes. + +#include "iconview.h" +#include "thumbnailsize.h" +#include "albumiconitem.h" +#include "gpiteminfo.h" +#include "themeengine.h" +#include "cameraiconview.h" +#include "cameraiconitem.h" + +namespace Digikam +{ + +class CameraIconViewItemPriv +{ + +public: + + CameraIconViewItemPriv() + { + itemInfo = 0; + } + + TQString downloadName; + + TQPixmap pixmap; + TQPixmap thumbnail; + + TQRect pixRect; + TQRect textRect; + TQRect extraRect; + + GPItemInfo *itemInfo; +}; + +CameraIconViewItem::CameraIconViewItem(IconGroupItem* parent, const GPItemInfo& itemInfo, + const TQImage& thumbnail, const TQString& downloadName) + : IconItem(parent) +{ + d = new CameraIconViewItemPriv; + d->itemInfo = new GPItemInfo(itemInfo); + d->downloadName = downloadName; + setThumbnail(thumbnail); +} + +CameraIconViewItem::~CameraIconViewItem() +{ + delete d->itemInfo; + delete d; +} + +void CameraIconViewItem::setThumbnail(const TQImage& thumbnail) +{ + d->thumbnail = TQPixmap(thumbnail); +} + +GPItemInfo* CameraIconViewItem::itemInfo() const +{ + return d->itemInfo; +} + +void CameraIconViewItem::paintItem() +{ + CameraIconView* view = (CameraIconView*)iconView(); + TQFont fn(view->font()); + + TQPixmap pix; + TQRect r(rect()); + + if (isSelected()) + pix = *(view->itemBaseSelPixmap()); + else + pix = *(view->itemBaseRegPixmap()); + + ThemeEngine* te = ThemeEngine::instance(); + + TQPainter p(&pix); + + TQString itemName = AlbumIconItem::squeezedText(&p, r.width()-5, d->itemInfo->name); + TQString downloadName = AlbumIconItem::squeezedText(&p, r.width()-5, d->downloadName); + calcRect(itemName, downloadName); + + p.setPen(isSelected() ? te->textSelColor() : te->textRegColor()); + + p.drawPixmap(d->pixRect.x() + (d->pixRect.width() - d->pixmap.width()) /2, + d->pixRect.y() + (d->pixRect.height() - d->pixmap.height()) /2, + d->pixmap); + + p.drawText(d->textRect, TQt::AlignHCenter|TQt::AlignTop, itemName); + + if (!d->downloadName.isEmpty()) + { + if (fn.pointSize() > 0) + fn.setPointSize(TQMAX(fn.pointSize()-2, 6)); + + p.setFont(fn); + p.setPen(isSelected() ? te->textSpecialSelColor() : te->textSpecialRegColor()); + p.drawText(d->extraRect, TQt::AlignHCenter|TQt::AlignTop, downloadName); + } + + if (this == iconView()->currentItem()) + { + p.setPen(TQPen(isSelected() ? TQt::white : TQt::black, 1, TQt::DotLine)); + p.drawRect(0, 0, r.width(), r.height()); + } + + // Draw download status icon. + TQPixmap downloaded; + + switch (d->itemInfo->downloaded) + { + case GPItemInfo::NewPicture: + { + downloaded = TQPixmap(view->newPicturePixmap()); + break; + } + case GPItemInfo::DownloadedYes: + { + downloaded = SmallIcon( "button_ok" ); + break; + } + case GPItemInfo::DownloadStarted: + { + downloaded = SmallIcon( "system-run" ); + break; + } + case GPItemInfo::DownloadFailed: + { + downloaded = SmallIcon( "button_cancel" ); + break; + } + /* TODO: see B.K.O #107316 : disable temporally the unknow download status until + a new method to identify the already downloaded pictures from camera is + implemented. + + case GPItemInfo::DownloadUnknow: + { + downloaded = view->unknowPicturePixmap(); + break; + } + */ + } + + if (!downloaded.isNull()) + p.drawPixmap(rect().width() - downloaded.width() - 5, 5, downloaded); + + // If camera item is locked (read only), draw a "Lock" icon. + if (d->itemInfo->writePermissions == 0) + p.drawPixmap(5, 5, SmallIcon( "encrypted" )); + + p.end(); + + r = TQRect(view->contentsToViewport(TQPoint(r.x(), r.y())), + TQSize(r.width(), r.height())); + + bitBlt(view->viewport(), r.x(), r.y(), &pix); +} + +void CameraIconViewItem::setDownloadName(const TQString& downloadName) +{ + d->downloadName = downloadName; + repaint(); +} + +TQString CameraIconViewItem::getDownloadName() const +{ + return d->downloadName; +} + +void CameraIconViewItem::setDownloaded(int status) +{ + d->itemInfo->downloaded = status; + repaint(); +} + +bool CameraIconViewItem::isDownloaded() const +{ + return (d->itemInfo->downloaded == GPItemInfo::DownloadedYes); +} + +void CameraIconViewItem::toggleLock() +{ + if (d->itemInfo->writePermissions == 0) + d->itemInfo->writePermissions = 1; + else + d->itemInfo->writePermissions = 0; + + repaint(); +} + +void CameraIconViewItem::calcRect(const TQString& itemName, const TQString& downloadName) +{ + CameraIconView* view = (CameraIconView*)iconView(); + int thumbSize = view->thumbnailSize().size(); + d->pixmap = TQPixmap(d->thumbnail.convertToImage().smoothScale(thumbSize, thumbSize, TQImage::ScaleMin)); + d->pixRect = TQRect(0,0,0,0); + d->textRect = TQRect(0,0,0,0); + d->extraRect = TQRect(0,0,0,0); + TQRect itemRect = rect(); + itemRect.moveTopLeft(TQPoint(0, 0)); + + d->pixRect.setWidth(thumbSize); + d->pixRect.setHeight(thumbSize); + + TQFontMetrics fm(iconView()->font()); + TQRect r = TQRect(fm.boundingRect(0, 0, thumbSize, 0xFFFFFFFF, + TQt::AlignHCenter | TQt::AlignTop, + itemName)); + d->textRect.setWidth(r.width()); + d->textRect.setHeight(r.height()); + + if (!d->downloadName.isEmpty()) + { + TQFont fn(iconView()->font()); + if (fn.pointSize() > 0) + { + fn.setPointSize(TQMAX(fn.pointSize()-2, 6)); + } + + fm = TQFontMetrics(fn); + r = TQRect(fm.boundingRect(0, 0, thumbSize, 0xFFFFFFFF, + TQt::AlignHCenter | TQt::WordBreak, + downloadName)); + d->extraRect.setWidth(r.width()); + d->extraRect.setHeight(r.height()); + + d->textRect.setWidth(TQMAX(d->textRect.width(), d->extraRect.width())); + d->textRect.setHeight(d->textRect.height() + d->extraRect.height()); + } + + int w = TQMAX(d->textRect.width(), d->pixRect.width() ); + int h = d->textRect.height() + d->pixRect.height() ; + + itemRect.setWidth(w+4); + itemRect.setHeight(h+4); + + // Center the pix and text rect + d->pixRect = TQRect(2, 2, d->pixRect.width(), d->pixRect.height()); + d->textRect = TQRect((itemRect.width() - d->textRect.width())/2, + itemRect.height() - d->textRect.height(), + d->textRect.width(), d->textRect.height()); + + if (!d->extraRect.isEmpty()) + { + d->extraRect = TQRect((itemRect.width() - d->extraRect.width())/2, + itemRect.height() - d->extraRect.height(), + d->extraRect.width(), d->extraRect.height()); + } +} + +TQRect CameraIconViewItem::clickToOpenRect() +{ + TQRect r(rect()); + + if (d->pixmap.isNull()) + { + TQRect pixRect(d->pixRect); + pixRect.moveBy(r.x(), r.y()); + return pixRect; + } + + TQRect pixRect(d->pixRect.x() + (d->pixRect.width() - d->pixmap.width())/2, + d->pixRect.y() + (d->pixRect.height() - d->pixmap.height())/2, + d->pixmap.width(), d->pixmap.height()); + pixRect.moveBy(r.x(), r.y()); + return pixRect; +} + +} // namespace Digikam diff --git a/src/utilities/cameragui/cameraiconitem.h b/src/utilities/cameragui/cameraiconitem.h new file mode 100644 index 00000000..961bc1a3 --- /dev/null +++ b/src/utilities/cameragui/cameraiconitem.h @@ -0,0 +1,81 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-21 + * Description : camera icon view item + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef CAMERAICONITEM_H +#define CAMERAICONITEM_H + +// TQt includes. + +#include <tqstring.h> +#include <tqimage.h> + +// Local includes. + +#include "iconitem.h" + +namespace Digikam +{ + +class GPItemInfo; +class CameraIconViewItemPriv; + +class CameraIconViewItem : public IconItem +{ + +public: + + CameraIconViewItem(IconGroupItem* parent, const GPItemInfo& itemInfo, + const TQImage& thumbnail, const TQString& downloadName=TQString()); + ~CameraIconViewItem(); + + void setThumbnail(const TQImage& thumbnail); + + void setDownloadName(const TQString& downloadName); + TQString getDownloadName() const; + void setDownloaded(int status); + bool isDownloaded() const; + + void toggleLock(); + + GPItemInfo* itemInfo() const; + + // reimplemented from IconItem + virtual TQRect clickToOpenRect(); + +protected: + + virtual void paintItem(); + +private: + + void calcRect(const TQString& itemName, const TQString& downloadName); + +private: + + CameraIconViewItemPriv* d; +}; + +} // namespace Digikam + +#endif /* CAMERAICONITEM_H */ diff --git a/src/utilities/cameragui/cameraiconview.cpp b/src/utilities/cameragui/cameraiconview.cpp new file mode 100644 index 00000000..ba496217 --- /dev/null +++ b/src/utilities/cameragui/cameraiconview.cpp @@ -0,0 +1,900 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-18 + * Description : camera icon view + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqfile.h> +#include <tqfileinfo.h> +#include <tqtimer.h> +#include <tqpainter.h> +#include <tqpixmap.h> +#include <tqcursor.h> +#include <tqfontmetrics.h> +#include <tqfont.h> +#include <tqdragobject.h> +#include <tqclipboard.h> + +// KDE includes. + +#include <kurldrag.h> +#include <kmimetype.h> +#include <tdelocale.h> +#include <kiconloader.h> +#include <tdeaction.h> +#include <tdeapplication.h> + +// Local includes. + +#include "ddebug.h" +#include "themeengine.h" +#include "thumbnailsize.h" +#include "gpiteminfo.h" +#include "renamecustomizer.h" +#include "icongroupitem.h" +#include "dpopupmenu.h" +#include "dragobjects.h" +#include "cameraui.h" +#include "cameraiconitem.h" +#include "cameraiconview.h" +#include "cameraiconview.moc" + +namespace Digikam +{ + +class CameraIconViewPriv +{ +public: + + CameraIconViewPriv() + { + renamer = 0; + groupItem = 0; + cameraUI = 0; + thumbSize = ThumbnailSize::Large; + pixmapNewPicture = TQPixmap(newPicture_xpm); + pixmapUnknowPicture = TQPixmap(unknowPicture_xpm); + } + + static const char *newPicture_xpm[]; + static const char *unknowPicture_xpm[]; + + TQDict<CameraIconViewItem> itemDict; + + TQRect itemRect; + + TQPixmap itemRegPixmap; + TQPixmap itemSelPixmap; + TQPixmap pixmapNewPicture; + TQPixmap pixmapUnknowPicture; + + RenameCustomizer *renamer; + + IconGroupItem *groupItem; + + ThumbnailSize thumbSize; + + CameraUI *cameraUI; +}; + +const char *CameraIconViewPriv::newPicture_xpm[] = +{ + "13 13 8 1", + " c None", + ". c #232300", + "+ c #F6F611", + "@ c #000000", + "# c #DBDA4D", + "$ c #FFFF00", + "% c #AAA538", + "& c #E8E540", + " . ", + " . .+. . ", + " @#@ .$. .#. ", + " @$@#$#@$. ", + " @$%&%$@ ", + " ..#%&&&%#.. ", + ".+$$&&&&&$$+@", + " ..#%&&&%#@@ ", + " @$%&%$@ ", + " .$@#$#@$. ", + " @#. @$@ @#. ", + " . @+@ . ", + " @ " +}; + +const char *CameraIconViewPriv::unknowPicture_xpm[] = +{ + "16 16 78 1", + " g None", + ". g #777777", + "+ g #7A7A7A", + "@ g #8C8C8C", + "# g #787878", + "$ g #707070", + "% g #878787", + "& g #C3C3C3", + "* g #EAEAEA", + "= g #E4E4E4", + "- g #E2E2E2", + "; g #E6E6E6", + "> g #CECECE", + ", g #888888", + "' g #6B6B6B", + ") g #969696", + "! g #DEDEDE", + "~ g #D8D8D8", + "{ g #FFFFFF", + "] g #F2F2F2", + "^ g #DFDFDF", + "/ g #9D9D9D", + "( g #686868", + "_ g #848484", + ": g #D0D0D0", + "< g #F1F1F1", + "[ g #F0F0F0", + "} g #EBEBEB", + "| g #FDFDFD", + "1 g #DDDDDD", + "2 g #D4D4D4", + "3 g #838383", + "4 g #ABABAB", + "5 g #C8C8C8", + "6 g #CCCCCC", + "7 g #F4F4F4", + "8 g #D6D6D6", + "9 g #E8E8E8", + "0 g #C4C4C4", + "a g #A4A4A4", + "b g #656565", + "c g #B4B4B4", + "d g #B9B9B9", + "e g #BDBDBD", + "f g #B7B7B7", + "g g #898989", + "h g #6D6D6D", + "i g #808080", + "j g #AAAAAA", + "k g #A9A9A9", + "l g #737373", + "m g #7F7F7F", + "n g #9A9A9A", + "o g #D3D3D3", + "p g #909090", + "q g #727272", + "r g #8F8F8F", + "s g #8E8E8E", + "t g #8D8D8D", + "u g #EEEEEE", + "v g #FAFAFA", + "w g #929292", + "x g #C5C5C5", + "y g #5F5F5F", + "z g #989898", + "A g #CFCFCF", + "B g #9C9C9C", + "C g #A0A0A0", + "D g #FEFEFE", + "E g #ACACAC", + "F g #5E5E5E", + "G g #868686", + "H g #AFAFAF", + "I g #C1C1C1", + "J g #818181", + "K g #7E7E7E", + "L g #7B7B7B", + "M g #636363", + " ", + " .+@@#$ ", + " .%&*=-;>,' ", + " .)!~={{]^-/( ", + " _::<{[}|{123 ", + " .456{7558{90ab ", + " +cde96df={&g,h ", + " ijjjjjk;{=@,,l ", + " mnnnnno{-pgggq ", + " #rprstuvwtttt' ", + " $tpppp6xpppp@y ", + " mnnnzA~Bnnn. ", + " 'taaCD{Eaa,F ", + " (GjHI0HjJF ", + " (K,,LM ", + " " +}; + +CameraIconView::CameraIconView(CameraUI* ui, TQWidget* parent) + : IconView(parent) +{ + d = new CameraIconViewPriv; + d->cameraUI = ui; + d->groupItem = new IconGroupItem(this); + + setHScrollBarMode(TQScrollView::AlwaysOff); + setMinimumSize(400, 300); + + setAcceptDrops(true); + viewport()->setAcceptDrops(true); + + // ---------------------------------------------------------------- + + connect(this, TQ_SIGNAL(signalSelectionChanged()), + this, TQ_SLOT(slotSelectionChanged())); + + connect(this, TQ_SIGNAL(signalNewSelection(bool)), + this, TQ_SLOT(slotUpdateDownloadNames(bool))); + + connect(this, TQ_SIGNAL(signalRightButtonClicked(IconItem*, const TQPoint&)), + this, TQ_SLOT(slotContextMenu(IconItem*, const TQPoint&))); + + connect(this, TQ_SIGNAL(signalRightButtonClicked(const TQPoint &)), + this, TQ_SLOT(slotRightButtonClicked(const TQPoint &))); + + connect(this, TQ_SIGNAL(signalDoubleClicked(IconItem*)), + this, TQ_SLOT(slotDoubleClicked(IconItem*))); + + connect(ThemeEngine::instance(), TQ_SIGNAL(signalThemeChanged()), + this, TQ_SLOT(slotThemeChanged())); + + // ---------------------------------------------------------------- + + updateItemRectsPixmap(); + slotThemeChanged(); +} + +CameraIconView::~CameraIconView() +{ + clear(); + delete d; +} + +TQPixmap* CameraIconView::itemBaseRegPixmap() const +{ + return &d->itemRegPixmap; +} + +TQPixmap* CameraIconView::itemBaseSelPixmap() const +{ + return &d->itemSelPixmap; +} + +TQPixmap CameraIconView::newPicturePixmap() const +{ + return d->pixmapNewPicture; +} + +TQPixmap CameraIconView::unknowPicturePixmap() const +{ + return d->pixmapUnknowPicture; +} + +void CameraIconView::setRenameCustomizer(RenameCustomizer* renamer) +{ + d->renamer = renamer; + + connect(d->renamer, TQ_SIGNAL(signalChanged()), + this, TQ_SLOT(slotDownloadNameChanged())); +} + +void CameraIconView::addItem(const GPItemInfo& info) +{ + TQImage thumb; + // Just to have a generic image thumb from desktop with KDE < 3.5.0 + KMimeType::Ptr mime = KMimeType::mimeType(info.mime == TQString("image/x-raw") ? + TQString("image/tiff") : info.mime); + + if (mime) + { + thumb = TQImage(mime->pixmap(TDEIcon::Desktop, ThumbnailSize::Huge, TDEIcon::DefaultState) + .convertToImage()); + } + else + { + TDEIconLoader *iconLoader = TDEApplication::kApplication()->iconLoader(); + thumb = iconLoader->loadIcon("application-x-zerosize", TDEIcon::Desktop, + ThumbnailSize::Huge, TDEIcon::DefaultState, 0, true) + .convertToImage(); + } + + TQString downloadName; + + if (d->renamer) + { + if (!d->renamer->useDefault()) + { + downloadName = getTemplatedName( &info, d->itemDict.count() ); + } + else + { + downloadName = getCasedName( d->renamer->changeCase(), &info); + } + } + + CameraIconViewItem* item = new CameraIconViewItem(d->groupItem, info, thumb, downloadName); + d->itemDict.insert(info.folder+info.name, item); +} + +void CameraIconView::removeItem(const TQString& folder, const TQString& file) +{ + CameraIconViewItem* item = d->itemDict.find(folder+file); + if (!item) + return; + d->itemDict.remove(folder+file); + + setDelayedRearrangement(true); + delete item; + setDelayedRearrangement(false); +} + +CameraIconViewItem* CameraIconView::findItem(const TQString& folder, const TQString& file) +{ + return d->itemDict.find(folder+file); +} + +int CameraIconView::countItemsByFolder(TQString folder) +{ + int count = 0; + if (folder.endsWith("/")) folder.truncate(folder.length()-1); + + for (IconItem* item = firstItem(); item; item = item->nextItem()) + { + CameraIconViewItem* iconItem = static_cast<CameraIconViewItem*>(item); + TQString itemFolder = iconItem->itemInfo()->folder; + if (itemFolder.endsWith("/")) itemFolder.truncate(itemFolder.length()-1); + + if (folder == itemFolder) + count++; + } + + return count; +} + +void CameraIconView::setThumbnail(const TQString& folder, const TQString& filename, const TQImage& image) +{ + CameraIconViewItem* item = d->itemDict.find(folder+filename); + if (!item) + return; + + item->setThumbnail(image); + item->repaint(); +} + +void CameraIconView::ensureItemVisible(CameraIconViewItem *item) +{ + IconView::ensureItemVisible(item); +} + +void CameraIconView::ensureItemVisible(const GPItemInfo& itemInfo) +{ + ensureItemVisible(itemInfo.folder, itemInfo.name); +} + +void CameraIconView::ensureItemVisible(const TQString& folder, const TQString& file) +{ + CameraIconViewItem* item = d->itemDict.find(folder+file); + if (!item) + return; + + ensureItemVisible(item); +} + +void CameraIconView::slotDownloadNameChanged() +{ + bool hasSelection = false; + for (IconItem* item = firstItem(); item; item = item->nextItem()) + { + if (item->isSelected()) + { + hasSelection = true; + break; + } + } + + // connected to slotUpdateDownloadNames, and used externally + emit signalNewSelection(hasSelection); +} + +void CameraIconView::slotUpdateDownloadNames(bool hasSelection) +{ + bool useDefault = true; + int startIndex = 0; + + if (d->renamer) + { + useDefault = d->renamer->useDefault(); + startIndex = d->renamer->startIndex() -1; + } + + bool convertLossLessJpeg = d->cameraUI->convertLosslessJpegFiles(); + TQString losslessFormat = d->cameraUI->losslessFormat(); + + viewport()->setUpdatesEnabled(false); + + // NOTE: see B.K.O #182352: ordering of item count must be adapted sort of icon view, + // since items are ordered from the most rescent to the older one. + + if (hasSelection) + { + // Camera items selection. + + for (IconItem* item = lastItem(); item; item = item->prevItem()) + { + TQString downloadName; + CameraIconViewItem* viewItem = static_cast<CameraIconViewItem*>(item); + + if (item->isSelected()) + { + if (!useDefault) + downloadName = getTemplatedName( viewItem->itemInfo(), startIndex ); + else + downloadName = getCasedName( d->renamer->changeCase(), viewItem->itemInfo() ); + + startIndex++; + } + + if (convertLossLessJpeg && !downloadName.isEmpty()) + { + TQFileInfo fi(downloadName); + TQString ext = fi.extension(false).upper(); + if (ext == TQString("JPEG") || ext == TQString("JPG") || ext == TQString("JPE")) + { + downloadName.truncate(downloadName.length() - ext.length()); + downloadName.append(losslessFormat.lower()); + } + } + + viewItem->setDownloadName( downloadName ); + } + } + else + { + // No camera item selection. + + for (IconItem* item = lastItem(); item; item = item->prevItem()) + { + TQString downloadName; + CameraIconViewItem* viewItem = static_cast<CameraIconViewItem*>(item); + + if (!useDefault) + downloadName = getTemplatedName( viewItem->itemInfo(), startIndex ); + else + downloadName = getCasedName( d->renamer->changeCase(), viewItem->itemInfo() ); + + if (convertLossLessJpeg) + { + TQFileInfo fi(downloadName); + TQString ext = fi.extension(false).upper(); + if (ext == TQString("JPEG") || ext == TQString("JPG") || ext == TQString("JPE")) + { + downloadName.truncate(downloadName.length() - ext.length()); + downloadName.append(losslessFormat.lower()); + } + } + + viewItem->setDownloadName( downloadName ); + startIndex++; + } + } + + viewport()->setUpdatesEnabled(true); + viewport()->update(); +} + +TQString CameraIconView::defaultDownloadName(CameraIconViewItem *viewItem) +{ + RenameCustomizer::Case renamecase = RenameCustomizer::NONE; + if (d->renamer) + renamecase = d->renamer->changeCase(); + + return getCasedName( renamecase, viewItem->itemInfo() ); +} + +TQString CameraIconView::getTemplatedName(const GPItemInfo* itemInfo, int position) +{ + TQString ext = itemInfo->name; + int pos = ext.findRev('.'); + if (pos < 0) + ext = ""; + else + ext = ext.right( ext.length() - pos ); + + TQDateTime mtime; + mtime.setTime_t(itemInfo->mtime); + + return d->renamer->newName(mtime, position+1, ext); +} + +TQString CameraIconView::getCasedName(const RenameCustomizer::Case ccase, + const GPItemInfo* itemInfo) +{ + TQString dname; + + switch (ccase) + { + case(RenameCustomizer::UPPER): + { + dname = itemInfo->name.upper(); + break; + } + case(RenameCustomizer::LOWER): + { + dname = itemInfo->name.lower(); + break; + } + default: + { + dname = itemInfo->name; + break; + } + }; + + return dname; +} + +void CameraIconView::slotSelectionChanged() +{ + bool selected = false; + CameraIconViewItem* camItem = 0; + + for (IconItem* item = firstItem(); item; item = item->nextItem()) + { + if (item->isSelected()) + { + camItem = static_cast<CameraIconViewItem*>(item); + selected = true; + break; + } + } + + emit signalNewSelection(selected); + emit signalSelected(camItem, selected); + + viewport()->update(); +} + +void CameraIconView::slotContextMenu(IconItem * item, const TQPoint&) +{ + if (!item) + return; + + // don't popup context menu if the camera is busy + if (d->cameraUI->isBusy()) + return; + + CameraIconViewItem* camItem = static_cast<CameraIconViewItem*>(item); + + DPopupMenu menu(this); + menu.insertItem(SmallIcon("editimage"), i18n("&View"), 0); + menu.insertSeparator(-1); + menu.insertItem(SmallIcon("go-down"),i18n("Download"), 1); + menu.insertItem(SmallIcon("go-down"),i18n("Download && Delete"), 4); + menu.insertItem(SmallIcon("encrypted"), i18n("Toggle lock"), 3); + menu.insertSeparator(-1); + menu.insertItem(SmallIcon("edit-delete"), i18n("Delete"), 2); + + int result = menu.exec(TQCursor::pos()); + + switch (result) + { + case(0): + { + emit signalFileView(camItem); + break; + } + case(1): + { + emit signalDownload(); + break; + } + case(2): + { + emit signalDelete(); + break; + } + case(3): + { + emit signalToggleLock(); + break; + } + case(4): + { + emit signalDownloadAndDelete(); + break; + } + default: + break; + } +} + +void CameraIconView::slotDoubleClicked(IconItem* item) +{ + if (!item) + return; + + if (d->cameraUI->isBusy()) + return; + + emit signalFileView(static_cast<CameraIconViewItem*>(item)); +} + +void CameraIconView::slotSelectAll() +{ + selectAll(); +} + +void CameraIconView::slotSelectNone() +{ + clearSelection(); +} + +void CameraIconView::slotSelectInvert() +{ + invertSelection(); +} + +void CameraIconView::slotSelectNew() +{ + blockSignals(true); + clearSelection(); + + for (IconItem* item = firstItem(); item; + item = item->nextItem()) + { + CameraIconViewItem* viewItem = static_cast<CameraIconViewItem*>(item); + if (viewItem->itemInfo()->downloaded == GPItemInfo::NewPicture) + { + viewItem->setSelected(true, false); + } + } + + blockSignals(false); + emit signalSelectionChanged(); +} + +void CameraIconView::startDrag() +{ + TQStringList lst; + + for (IconItem* item = firstItem(); item; item = item->nextItem()) + { + if (!item->isSelected()) + continue; + + CameraIconViewItem* iconItem = static_cast<CameraIconViewItem*>(item); + TQString itemPath = iconItem->itemInfo()->folder + iconItem->itemInfo()->name; + lst.append(itemPath); + } + + TQDragObject * drag = new CameraItemListDrag(lst, d->cameraUI); + if (drag) + { + TQPixmap icon(DesktopIcon("image-x-generic", 48)); + int w = icon.width(); + int h = icon.height(); + + TQPixmap pix(w+4,h+4); + TQString text(TQString::number(lst.count())); + + TQPainter p(&pix); + p.fillRect(0, 0, w+4, h+4, TQColor(TQt::white)); + p.setPen(TQPen(TQt::black, 1)); + p.drawRect(0, 0, w+4, h+4); + p.drawPixmap(2, 2, icon); + TQRect r = p.boundingRect(2,2,w,h,TQt::AlignLeft|TQt::AlignTop,text); + r.setWidth(TQMAX(r.width(),r.height())); + r.setHeight(TQMAX(r.width(),r.height())); + p.fillRect(r, TQColor(0,80,0)); + p.setPen(TQt::white); + TQFont f(font()); + f.setBold(true); + p.setFont(f); + p.drawText(r, TQt::AlignCenter, text); + p.end(); + + drag->setPixmap(pix); + drag->drag(); + } +} + +void CameraIconView::contentsDropEvent(TQDropEvent *event) +{ + // don't popup context menu if the camera is busy + if (d->cameraUI->isBusy()) + return; + + if ( (!TQUriDrag::canDecode(event) && !CameraDragObject::canDecode(event) ) + || event->source() == this) + { + event->ignore(); + return; + } + + KURL::List srcURLs; + KURLDrag::decode(event, srcURLs); + uploadItemPopupMenu(srcURLs); +} + +void CameraIconView::slotRightButtonClicked(const TQPoint&) +{ + // don't popup context menu if the camera is busy + if (d->cameraUI->isBusy()) + return; + + TQMimeSource *data = kapp->clipboard()->data(TQClipboard::Clipboard); + if(!data || !TQUriDrag::canDecode(data)) + return; + + KURL::List srcURLs; + KURLDrag::decode(data, srcURLs); + uploadItemPopupMenu(srcURLs); +} + +void CameraIconView::uploadItemPopupMenu(const KURL::List& srcURLs) +{ + TDEPopupMenu popMenu(this); + popMenu.insertTitle(SmallIcon("digikam"), d->cameraUI->cameraTitle()); + popMenu.insertItem( SmallIcon("goto"), i18n("&Upload to camera"), 10 ); + popMenu.insertSeparator(-1); + popMenu.insertItem( SmallIcon("cancel"), i18n("C&ancel") ); + + popMenu.setMouseTracking(true); + int id = popMenu.exec(TQCursor::pos()); + switch(id) + { + case 10: + { + emit signalUpload(srcURLs); + break; + } + default: + break; + } +} + +TQRect CameraIconView::itemRect() const +{ + return d->itemRect; +} + +void CameraIconView::setThumbnailSize(const ThumbnailSize& thumbSize) +{ + if ( d->thumbSize != thumbSize) + { + d->thumbSize = thumbSize; + updateItemRectsPixmap(); + triggerRearrangement(); + } +} + +ThumbnailSize CameraIconView::thumbnailSize() const +{ + return d->thumbSize; +} + +void CameraIconView::updateItemRectsPixmap() +{ + int thumbSize = d->thumbSize.size(); + + TQRect pixRect; + TQRect textRect; + TQRect extraRect; + + pixRect.setWidth(thumbSize); + pixRect.setHeight(thumbSize); + + TQFontMetrics fm(font()); + TQRect r = TQRect(fm.boundingRect(0, 0, thumbSize, 0xFFFFFFFF, + TQt::AlignHCenter | TQt::AlignTop, + "XXXXXXXXX")); + textRect.setWidth(r.width()); + textRect.setHeight(r.height()); + + TQFont fn(font()); + if (fn.pointSize() > 0) + { + fn.setPointSize(TQMAX(fn.pointSize()-2, 6)); + } + + fm = TQFontMetrics(fn); + r = TQRect(fm.boundingRect(0, 0, thumbSize, 0xFFFFFFFF, + TQt::AlignHCenter | TQt::AlignTop, + "XXXXXXXXX")); + extraRect.setWidth(r.width()); + extraRect.setHeight(r.height()); + + r = TQRect(); + r.setWidth(TQMAX(TQMAX(pixRect.width(), textRect.width()), extraRect.width()) + 4); + r.setHeight(pixRect.height() + textRect.height() + extraRect.height() + 4); + + d->itemRect = r; + + d->itemRegPixmap = ThemeEngine::instance()->thumbRegPixmap(d->itemRect.width(), + d->itemRect.height()); + + d->itemSelPixmap = ThemeEngine::instance()->thumbSelPixmap(d->itemRect.width(), + d->itemRect.height()); +} + +void CameraIconView::slotThemeChanged() +{ + updateItemRectsPixmap(); + viewport()->update(); +} + +int CameraIconView::itemsDownloaded() +{ + int downloaded = 0; + + for (IconItem* item = firstItem(); item; item = item->nextItem()) + { + CameraIconViewItem* iconItem = static_cast<CameraIconViewItem*>(item); + + if (iconItem->itemInfo()->downloaded == GPItemInfo::DownloadedYes) + downloaded++; + } + + return downloaded; +} + +void CameraIconView::itemsSelectionSizeInfo(unsigned long& fSizeKB, unsigned long& dSizeKB) +{ + long long fSize = 0; // Files size + long long dSize = 0; // Estimated space requires to download and process files. + for (IconItem* item = firstItem(); item; item = item->nextItem()) + { + if (item->isSelected()) + { + CameraIconViewItem* iconItem = static_cast<CameraIconViewItem*>(item); + long long size = iconItem->itemInfo()->size; + if (size < 0) // -1 if size is not provided by camera + continue; + fSize += size; + + if (iconItem->itemInfo()->mime == TQString("image/jpeg")) + { + if (d->cameraUI->convertLosslessJpegFiles()) + { + // Estimated size is aroud 5 x original size when JPEG=>PNG. + dSize += size*5; + } + else if (d->cameraUI->autoRotateJpegFiles()) + { + // We need a double size to perform rotation. + dSize += size*2; + } + else + { + // Real file size is added. + dSize += size; + } + } + else + dSize += size; + + } + } + + fSizeKB = fSize / 1024; + dSizeKB = dSize / 1024; +} + +} // namespace Digikam diff --git a/src/utilities/cameragui/cameraiconview.h b/src/utilities/cameragui/cameraiconview.h new file mode 100644 index 00000000..f621fedf --- /dev/null +++ b/src/utilities/cameragui/cameraiconview.h @@ -0,0 +1,141 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-18 + * Description : camera icon view + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef CAMERAICONVIEW_H +#define CAMERAICONVIEW_H + +// TQt includes. + +#include <tqdict.h> +#include <tqrect.h> + +// KDE includes. + +#include <kurl.h> + +// Local includes. + +#include "iconview.h" +#include "renamecustomizer.h" + +class TQPixmap; + +namespace Digikam +{ + +class ThumbnailSize; +class GPItemInfo; +class RenameCustomizer; +class CameraUI; +class CameraIconViewItem; +class CameraIconViewPriv; + +class CameraIconView : public IconView +{ + TQ_OBJECT + + +public: + + CameraIconView(CameraUI* ui, TQWidget* parent); + ~CameraIconView(); + + void setRenameCustomizer(RenameCustomizer* renamer); + + void addItem(const GPItemInfo& itemInfo); + void removeItem(const TQString& folder, const TQString& file); + void setThumbnail(const TQString& folder, const TQString& filename, const TQImage& image); + + void ensureItemVisible(CameraIconViewItem *item); + void ensureItemVisible(const GPItemInfo& itemInfo); + void ensureItemVisible(const TQString& folder, const TQString& file); + + void setThumbnailSize(const ThumbnailSize& thumbSize); + ThumbnailSize thumbnailSize() const; + + CameraIconViewItem* findItem(const TQString& folder, const TQString& file); + + int countItemsByFolder(TQString folder); + int itemsDownloaded(); + + TQPixmap* itemBaseRegPixmap() const; + TQPixmap* itemBaseSelPixmap() const; + TQPixmap newPicturePixmap() const; + TQPixmap unknowPicturePixmap() const; + + virtual TQRect itemRect() const; + + TQString defaultDownloadName(CameraIconViewItem *item); + + void itemsSelectionSizeInfo(unsigned long& fSize, unsigned long& dSize); + +signals: + + void signalSelected(CameraIconViewItem*, bool); + void signalFileView(CameraIconViewItem*); + + void signalUpload(const KURL::List&); + void signalDownload(); + void signalDownloadAndDelete(); + void signalDelete(); + void signalToggleLock(); + void signalNewSelection(bool); + +public slots: + + void slotDownloadNameChanged(); + void slotSelectionChanged(); + void slotSelectAll(); + void slotSelectNone(); + void slotSelectInvert(); + void slotSelectNew(); + +private slots: + + void slotRightButtonClicked(const TQPoint& pos); + void slotContextMenu(IconItem* item, const TQPoint& pos); + void slotDoubleClicked(IconItem* item); + void slotThemeChanged(); + void slotUpdateDownloadNames(bool hasSelection); + +protected: + + void startDrag(); + void contentsDropEvent(TQDropEvent *event); + void updateItemRectsPixmap(); + +private: + + TQString getTemplatedName(const GPItemInfo* itemInfo, int position); + TQString getCasedName(const RenameCustomizer::Case ccase, const GPItemInfo* itemInfo); + void uploadItemPopupMenu(const KURL::List& srcURLs); + +private: + + CameraIconViewPriv* d; +}; + +} // namespace Digikam + +#endif /* CAMERAICONVIEW_H */ diff --git a/src/utilities/cameragui/camerainfodialog.cpp b/src/utilities/cameragui/camerainfodialog.cpp new file mode 100644 index 00000000..ba565a59 --- /dev/null +++ b/src/utilities/cameragui/camerainfodialog.cpp @@ -0,0 +1,85 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-28 + * Description : a dialog to display camera information. + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlayout.h> +#include <tqframe.h> +#include <tqtextedit.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kiconloader.h> + +// Local includes. + +#include "camerainfodialog.h" + +namespace Digikam +{ + +CameraInfoDialog::CameraInfoDialog(TQWidget *parent, const TQString& summary, const TQString& manual, + const TQString& about) + : KDialogBase(IconList, i18n("Camera Information"), Help|Ok, Ok, parent, 0, true, true) +{ + setHelp("digitalstillcamera.anchor", "digikam"); + resize(500, 400); + + // ---------------------------------------------------------- + + TQFrame *p1 = addPage( i18n("Summary"), i18n("Camera Summary"), BarIcon("contents2", TDEIcon::SizeMedium) ); + TQVBoxLayout *p1layout = new TQVBoxLayout( p1, 0, 6 ); + + TQTextEdit *summaryView = new TQTextEdit(summary, TQString(), p1); + summaryView->setWordWrap(TQTextEdit::WidgetWidth); + summaryView->setReadOnly(true); + p1layout->addWidget(summaryView); + + // ---------------------------------------------------------- + + TQFrame *p2 = addPage( i18n("Manual"), i18n("Camera Manual"), BarIcon("contents", TDEIcon::SizeMedium) ); + TQVBoxLayout *p2layout = new TQVBoxLayout( p2, 0, 6 ); + + TQTextEdit *manualView = new TQTextEdit(manual, TQString(), p2); + manualView->setWordWrap(TQTextEdit::WidgetWidth); + manualView->setReadOnly(true); + p2layout->addWidget(manualView); + + // ---------------------------------------------------------- + + TQFrame *p3 = addPage( i18n("About"), i18n("About Driver"), BarIcon("camera-photo", TDEIcon::SizeMedium) ); + TQVBoxLayout *p3layout = new TQVBoxLayout( p3, 0, 6 ); + + TQTextEdit *aboutView = new TQTextEdit(about, TQString(), p3); + aboutView->setWordWrap(TQTextEdit::WidgetWidth); + aboutView->setReadOnly(true); + p3layout->addWidget(aboutView); +} + +CameraInfoDialog::~CameraInfoDialog() +{ +} + +} // namespace Digikam diff --git a/src/utilities/cameragui/camerainfodialog.h b/src/utilities/cameragui/camerainfodialog.h new file mode 100644 index 00000000..7ec3120f --- /dev/null +++ b/src/utilities/cameragui/camerainfodialog.h @@ -0,0 +1,50 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-28 + * Description : a dialog to display camera information. + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef CAMERAINFODIALOG_H +#define CAMERAINFODIALOG_H + +// TQt includes. + +#include <tqstring.h> + +// KDE includes. + +#include <kdialogbase.h> + +namespace Digikam +{ + +class CameraInfoDialog : public KDialogBase +{ +public: + + CameraInfoDialog(TQWidget *parent, const TQString& summary, const TQString& manual, + const TQString& about); + ~CameraInfoDialog(); +}; + +} // namespace Digikam + +#endif /* CAMERAINFODIALOG_H */ diff --git a/src/utilities/cameragui/cameraui.cpp b/src/utilities/cameragui/cameraui.cpp new file mode 100644 index 00000000..8b531ea2 --- /dev/null +++ b/src/utilities/cameragui/cameraui.cpp @@ -0,0 +1,1734 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-16 + * Description : Camera interface dialog + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#define CAMERA_INFO_MENU_ID 255 + +// TQt includes. + +#include <tqvgroupbox.h> +#include <tqlayout.h> +#include <tqpushbutton.h> +#include <tqtoolbutton.h> +#include <tqiconview.h> +#include <tqvbox.h> +#include <tqhbox.h> +#include <tqpopupmenu.h> +#include <tqsplitter.h> +#include <tqpixmap.h> +#include <tqcombobox.h> +#include <tqtoolbox.h> +#include <tqframe.h> +#include <tqvbuttongroup.h> +#include <tqradiobutton.h> +#include <tqcheckbox.h> +#include <tqlineedit.h> +#include <tqtooltip.h> +#include <tqtimer.h> +#include <tqwhatsthis.h> +#include <tqfile.h> +#include <tqfileinfo.h> + +// KDE includes. + +#include <tdefiledialog.h> +#include <kimageio.h> +#include <tdeaboutdata.h> +#include <tdemessagebox.h> +#include <kprogress.h> +#include <tdeglobal.h> +#include <tdelocale.h> +#include <tdeconfig.h> +#include <tdeapplication.h> +#include <kiconloader.h> +#include <tdepopupmenu.h> +#include <kstandarddirs.h> +#include <khelpmenu.h> +#include <kcalendarsystem.h> +#include <kurllabel.h> +#include <ksqueezedtextlabel.h> + +#if KDE_IS_VERSION(3,2,0) +#include <kinputdialog.h> +#else +#include <klineeditdlg.h> +#endif + +// LibKDcraw includes. + +#include <libkdcraw/version.h> +#include <libkdcraw/kdcraw.h> + +#if KDCRAW_VERSION < 0x000106 +#include <libkdcraw/dcrawbinary.h> +#endif + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "thumbnailsize.h" +#include "kdatetimeedit.h" +#include "sidebar.h" +#include "scanlib.h" +#include "downloadsettingscontainer.h" +#include "imagepropertiessidebarcamgui.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "album.h" +#include "albumselectdialog.h" +#include "renamecustomizer.h" +#include "animwidget.h" +#include "freespacewidget.h" +#include "camerafolderdialog.h" +#include "camerainfodialog.h" +#include "cameraiconview.h" +#include "cameraiconitem.h" +#include "cameracontroller.h" +#include "cameralist.h" +#include "cameratype.h" +#include "cameraui.h" +#include "cameraui.moc" + +namespace Digikam +{ + +class CameraUIPriv +{ +public: + + enum SettingsTab + { + RENAMEFILEPAGE=0, + AUTOALBUMPAGE, + ONFLYPAGE + }; + + enum DateFormatOptions + { + IsoDateFormat=0, + TextDateFormat, + LocalDateFormat + }; + + CameraUIPriv() + { + deleteAfter = false; + busy = false; + closed = false; + helpMenu = 0; + advBox = 0; + downloadMenu = 0; + deleteMenu = 0; + imageMenu = 0; + cancelBtn = 0; + splitter = 0; + rightSidebar = 0; + fixDateTimeCheck = 0; + autoRotateCheck = 0; + autoAlbumDateCheck = 0; + autoAlbumExtCheck = 0; + status = 0; + progress = 0; + controller = 0; + view = 0; + renameCustomizer = 0; + anim = 0; + dateTimeEdit = 0; + setPhotographerId = 0; + setCredits = 0; + losslessFormat = 0; + convertJpegCheck = 0; + formatLabel = 0; + folderDateLabel = 0; + folderDateFormat = 0; + freeSpaceWidget = 0; + } + + bool deleteAfter; + bool busy; + bool closed; + + TQString cameraTitle; + + TQStringList currentlyDeleting; + TQStringList foldersToScan; + TQStringList cameraFolderList; + + TQPopupMenu *downloadMenu; + TQPopupMenu *deleteMenu; + TQPopupMenu *imageMenu; + + TQToolButton *cancelBtn; + + TQToolBox *advBox; + + TQCheckBox *autoRotateCheck; + TQCheckBox *autoAlbumDateCheck; + TQCheckBox *autoAlbumExtCheck; + TQCheckBox *fixDateTimeCheck; + TQCheckBox *setPhotographerId; + TQCheckBox *setCredits; + TQCheckBox *convertJpegCheck; + + TQLabel *formatLabel; + TQLabel *folderDateLabel; + + TQComboBox *losslessFormat; + TQComboBox *folderDateFormat; + + TQSplitter *splitter; + + TQDateTime lastAccess; + + KProgress *progress; + + KSqueezedTextLabel *status; + + KURL lastDestURL; + + KHelpMenu *helpMenu; + + KDateTimeEdit *dateTimeEdit; + + CameraController *controller; + + CameraIconView *view; + + RenameCustomizer *renameCustomizer; + + AnimWidget *anim; + + ImagePropertiesSideBarCamGui *rightSidebar; + + FreeSpaceWidget *freeSpaceWidget; +}; + +CameraUI::CameraUI(TQWidget* /*parent*/, const TQString& cameraTitle, + const TQString& model, const TQString& port, + const TQString& path, const TQDateTime lastAccess) + : KDialogBase(Plain, cameraTitle, + Help|User1|User2|User3|Close, Close, + 0, // B.K.O # 116485: no parent for this modal dialog. + 0, false, true, + i18n("D&elete"), + i18n("&Download"), + i18n("&Images")) +{ + d = new CameraUIPriv; + d->lastAccess = lastAccess; + d->cameraTitle = cameraTitle; + setHelp("camerainterface.anchor", "digikam"); + + // ------------------------------------------------------------------------- + + TQGridLayout* viewBoxLayout = new TQGridLayout(plainPage(), 2, 7); + + TQHBox* widget = new TQHBox(plainPage()); + d->splitter = new TQSplitter(widget); + d->view = new CameraIconView(this, d->splitter); + + TQSizePolicy rightSzPolicy(TQSizePolicy::Preferred, TQSizePolicy::Expanding, 2, 1); + d->view->setSizePolicy(rightSzPolicy); + + d->rightSidebar = new ImagePropertiesSideBarCamGui(widget, "CameraGui Sidebar Right", d->splitter, + Sidebar::Right, true); + d->splitter->setFrameStyle( TQFrame::NoFrame ); + d->splitter->setFrameShadow( TQFrame::Plain ); + d->splitter->setFrameShape( TQFrame::NoFrame ); + d->splitter->setOpaqueResize(false); + + // ------------------------------------------------------------------------- + + d->advBox = new TQToolBox(d->rightSidebar); + d->renameCustomizer = new RenameCustomizer(d->advBox, d->cameraTitle); + d->view->setRenameCustomizer(d->renameCustomizer); + + TQWhatsThis::add( d->advBox, i18n("<p>Set how digiKam will rename files as they are downloaded.")); + + d->advBox->insertItem(CameraUIPriv::RENAMEFILEPAGE, d->renameCustomizer, + SmallIconSet("fileimport"), i18n("File Renaming Options")); + + // -- Albums Auto-creation options ----------------------------------------- + + TQWidget* albumBox = new TQWidget(d->advBox); + TQVBoxLayout* albumVlay = new TQVBoxLayout(albumBox, marginHint(), spacingHint()); + d->autoAlbumExtCheck = new TQCheckBox(i18n("Extension-based sub-albums"), albumBox); + d->autoAlbumDateCheck = new TQCheckBox(i18n("Date-based sub-albums"), albumBox); + TQHBox *hbox1 = new TQHBox(albumBox); + d->folderDateLabel = new TQLabel(i18n("Date format:"), hbox1); + d->folderDateFormat = new TQComboBox(hbox1); + d->folderDateFormat->insertItem(i18n("ISO"), CameraUIPriv::IsoDateFormat); + d->folderDateFormat->insertItem(i18n("Full Text"), CameraUIPriv::TextDateFormat); + d->folderDateFormat->insertItem(i18n("Local Settings"), CameraUIPriv::LocalDateFormat); + albumVlay->addWidget(d->autoAlbumExtCheck); + albumVlay->addWidget(d->autoAlbumDateCheck); + albumVlay->addWidget(hbox1); + albumVlay->addStretch(); + + TQWhatsThis::add( albumBox, i18n("<p>Set how digiKam creates albums automatically when downloading.")); + TQWhatsThis::add( d->autoAlbumExtCheck, i18n("<p>Enable this option if you want to download your " + "pictures into automatically created file extension-based sub-albums of the destination " + "album. This way, you can separate JPEG and RAW files as they are downloaded from your camera.")); + TQWhatsThis::add( d->autoAlbumDateCheck, i18n("<p>Enable this option if you want to " + "download your pictures into automatically created file date-based sub-albums " + "of the destination album.")); + TQWhatsThis::add( d->folderDateFormat, i18n("<p>Select your preferred date format used to " + "create new albums. The options available are:<p>" + "<b>ISO</b>: the date format is in accordance with ISO 8601 " + "(YYYY-MM-DD). E.g.: <i>2006-08-24</i><p>" + "<b>Full Text</b>: the date format is in a user-readable string. " + "E.g.: <i>Thu Aug 24 2006</i><p>" + "<b>Local Settings</b>: the date format depending on TDE control panel settings.<p>")); + + d->advBox->insertItem(CameraUIPriv::AUTOALBUMPAGE, albumBox, SmallIconSet("folder-new"), + i18n("Auto-creation of Albums")); + + // -- On the Fly options --------------------------------------------------- + + TQWidget* onFlyBox = new TQWidget(d->advBox); + TQVBoxLayout* onFlyVlay = new TQVBoxLayout(onFlyBox, marginHint(), spacingHint()); + d->setPhotographerId = new TQCheckBox(i18n("Set default photographer identity"), onFlyBox); + d->setCredits = new TQCheckBox(i18n("Set default credit and copyright"), onFlyBox); + d->fixDateTimeCheck = new TQCheckBox(i18n("Fix internal date && time"), onFlyBox); + d->dateTimeEdit = new KDateTimeEdit(onFlyBox, "datepicker"); + d->autoRotateCheck = new TQCheckBox(i18n("Auto-rotate/flip image"), onFlyBox); + d->convertJpegCheck = new TQCheckBox(i18n("Convert to lossless file format"), onFlyBox); + TQHBox *hbox2 = new TQHBox(onFlyBox); + d->formatLabel = new TQLabel(i18n("New image format:"), hbox2); + d->losslessFormat = new TQComboBox(hbox2); + d->losslessFormat->insertItem("PNG", 0); + onFlyVlay->addWidget(d->setPhotographerId); + onFlyVlay->addWidget(d->setCredits); + onFlyVlay->addWidget(d->fixDateTimeCheck); + onFlyVlay->addWidget(d->dateTimeEdit); + onFlyVlay->addWidget(d->autoRotateCheck); + onFlyVlay->addWidget(d->convertJpegCheck); + onFlyVlay->addWidget(hbox2); + onFlyVlay->addStretch(); + + TQWhatsThis::add( onFlyBox, i18n("<p>Set here all options to fix/transform JPEG files automatically " + "as they are downloaded.")); + TQWhatsThis::add( d->autoRotateCheck, i18n("<p>Enable this option if you want images automatically " + "rotated or flipped using EXIF information provided by the camera.")); + TQWhatsThis::add( d->setPhotographerId, i18n("<p>Enable this option to store the default " + "photographer identity in the IPTC tags using digiKam's metadata settings.")); + TQWhatsThis::add( d->setCredits, i18n("<p>Enable this option to store the default credit " + "and copyright information in the IPTC tags using digiKam's metadata settings.")); + TQWhatsThis::add( d->fixDateTimeCheck, i18n("<p>Enable this option to set date and time metadata " + "tags to the right values if your camera does not set " + "these tags correctly when pictures are taken. The values will " + "be saved in the DateTimeDigitized and DateTimeCreated EXIF/IPTC fields.")); + TQWhatsThis::add( d->convertJpegCheck, i18n("<p>Enable this option to automatically convert " + "all JPEG files to a lossless image format. <b>Note:</b> Image conversion can take a " + "while on a slow computer.")); + TQWhatsThis::add( d->losslessFormat, i18n("<p>Select your preferred lossless image file format to " + "convert to. <b>Note:</b> All metadata will be preserved during the conversion.")); + + d->advBox->insertItem(CameraUIPriv::ONFLYPAGE, onFlyBox, SmallIconSet("system-run"), + i18n("On the Fly Operations (JPEG only)")); + + d->rightSidebar->appendTab(d->advBox, SmallIcon("configure"), i18n("Settings")); + d->rightSidebar->loadViewState(); + + // ------------------------------------------------------------------------- + + d->cancelBtn = new TQToolButton(plainPage()); + d->cancelBtn->setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Minimum ) ); + d->cancelBtn->setPixmap( SmallIcon( "cancel" ) ); + d->cancelBtn->setEnabled(false); + + d->status = new KSqueezedTextLabel(plainPage()); + d->progress = new KProgress(plainPage()); + d->progress->setMaximumHeight( fontMetrics().height()+4 ); + d->progress->hide(); + + TQWidget *frame = new TQWidget(plainPage()); + TQHBoxLayout* layout = new TQHBoxLayout(frame); + frame->setSizePolicy(TQSizePolicy(TQSizePolicy::Minimum, TQSizePolicy::Minimum)); + + KURLLabel *pixmapLogo = new KURLLabel( Digikam::webProjectUrl(), TQString(), frame ); + pixmapLogo->setMargin(0); + pixmapLogo->setScaledContents( false ); + pixmapLogo->setSizePolicy(TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Minimum)); + TQToolTip::add(pixmapLogo, i18n("Visit digiKam project website")); + TDEGlobal::dirs()->addResourceType("logo-digikam", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("logo-digikam", "logo-digikam.png"); + pixmapLogo->setPixmap( TQPixmap( directory + "logo-digikam.png" ) ); + pixmapLogo->setFocusPolicy(TQWidget::NoFocus); + + d->anim = new AnimWidget(frame, pixmapLogo->height()-2); + + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget( pixmapLogo ); + layout->addWidget( d->anim ); + + d->freeSpaceWidget = new FreeSpaceWidget(plainPage(), 100); + + viewBoxLayout->addMultiCellWidget(widget, 0, 0, 0, 7); + viewBoxLayout->addMultiCellWidget(d->cancelBtn, 2, 2, 0, 0); + viewBoxLayout->addMultiCellWidget(d->status, 2, 2, 2, 2); + viewBoxLayout->addMultiCellWidget(d->progress, 2, 2, 3, 3); + viewBoxLayout->addMultiCellWidget(d->freeSpaceWidget, 2, 2, 5, 5); + viewBoxLayout->addMultiCellWidget(frame, 2, 2, 7, 7); + viewBoxLayout->setRowSpacing(1, spacingHint()); + viewBoxLayout->setColSpacing(1, spacingHint()); + viewBoxLayout->setColSpacing(4, spacingHint()); + viewBoxLayout->setColSpacing(6, spacingHint()); + viewBoxLayout->setColStretch( 0, 0 ); + viewBoxLayout->setColStretch( 1, 0 ); + viewBoxLayout->setColStretch( 2, 3 ); + viewBoxLayout->setColStretch( 3, 1 ); + viewBoxLayout->setColStretch( 4, 0 ); + viewBoxLayout->setColStretch( 5, 0 ); + viewBoxLayout->setColStretch( 6, 0 ); + viewBoxLayout->setColStretch( 7, 0 ); + + // ------------------------------------------------------------------------- + + d->imageMenu = new TQPopupMenu(this); + d->imageMenu->insertItem(i18n("Select &All"), d->view, TQ_SLOT(slotSelectAll()), CTRL+Key_A, 0); + d->imageMenu->insertItem(i18n("Select N&one"), d->view, TQ_SLOT(slotSelectNone()), CTRL+Key_U, 1); + d->imageMenu->insertItem(i18n("&Invert Selection"), d->view, TQ_SLOT(slotSelectInvert()), CTRL+Key_Asterisk, 2); + d->imageMenu->insertSeparator(); + d->imageMenu->insertItem(i18n("Select &New Items"), d->view, TQ_SLOT(slotSelectNew()), 0, 3); + d->imageMenu->insertSeparator(); + d->imageMenu->insertItem(i18n("Increase Thumbnail Size"), this, TQ_SLOT(slotIncreaseThumbSize()), CTRL+Key_Plus, 4); + d->imageMenu->insertItem(i18n("Decrease Thumbnail Size"), this, TQ_SLOT(slotDecreaseThumbSize()), CTRL+Key_Minus, 5); + d->imageMenu->insertSeparator(); + d->imageMenu->insertItem(i18n("Toggle Lock"), this, TQ_SLOT(slotToggleLock()), 0, 6); + actionButton(User3)->setPopup(d->imageMenu); + + // ------------------------------------------------------------------------- + + d->downloadMenu = new TQPopupMenu(this); + d->downloadMenu->insertItem(i18n("Download Selected"), + this, TQ_SLOT(slotDownloadSelected()), 0, 0); + d->downloadMenu->insertItem(i18n("Download All"), + this, TQ_SLOT(slotDownloadAll()), 0, 1); + d->downloadMenu->insertSeparator(); + d->downloadMenu->insertItem(i18n("Download/Delete Selected"), + this, TQ_SLOT(slotDownloadAndDeleteSelected()), 0, 2); + d->downloadMenu->insertItem(i18n("Download/Delete All"), + this, TQ_SLOT(slotDownloadAndDeleteAll()), 0, 3); + d->downloadMenu->insertSeparator(); + d->downloadMenu->insertItem(i18n("Upload..."), + this, TQ_SLOT(slotUpload()), 0, 4); + d->downloadMenu->setItemEnabled(0, false); + d->downloadMenu->setItemEnabled(2, false); + actionButton(User2)->setPopup(d->downloadMenu); + + // ------------------------------------------------------------------------- + + d->deleteMenu = new TQPopupMenu(this); + d->deleteMenu->insertItem(i18n("Delete Selected"), this, TQ_SLOT(slotDeleteSelected()), 0, 0); + d->deleteMenu->insertItem(i18n("Delete All"), this, TQ_SLOT(slotDeleteAll()), 0, 1); + d->deleteMenu->setItemEnabled(0, false); + actionButton(User1)->setPopup(d->deleteMenu); + + // ------------------------------------------------------------------------- + + TQPushButton *helpButton = actionButton( Help ); + d->helpMenu = new KHelpMenu(this, kapp->aboutData(), false); + d->helpMenu->menu()->insertItem(SmallIcon("camera-photo"), i18n("Camera Information"), + this, TQ_SLOT(slotInformations()), 0, CAMERA_INFO_MENU_ID, 0); + helpButton->setPopup( d->helpMenu->menu() ); + + // ------------------------------------------------------------------------- + + connect(d->autoAlbumDateCheck, TQ_SIGNAL(toggled(bool)), + d->folderDateFormat, TQ_SLOT(setEnabled(bool))); + + connect(d->autoAlbumDateCheck, TQ_SIGNAL(toggled(bool)), + d->folderDateLabel, TQ_SLOT(setEnabled(bool))); + + connect(d->convertJpegCheck, TQ_SIGNAL(toggled(bool)), + d->losslessFormat, TQ_SLOT(setEnabled(bool))); + + connect(d->convertJpegCheck, TQ_SIGNAL(toggled(bool)), + d->formatLabel, TQ_SLOT(setEnabled(bool))); + + connect(d->convertJpegCheck, TQ_SIGNAL(toggled(bool)), + d->view, TQ_SLOT(slotDownloadNameChanged())); + + connect(d->fixDateTimeCheck, TQ_SIGNAL(toggled(bool)), + d->dateTimeEdit, TQ_SLOT(setEnabled(bool))); + + connect(pixmapLogo, TQ_SIGNAL(leftClickedURL(const TQString&)), + this, TQ_SLOT(slotProcessURL(const TQString&))); + + // ------------------------------------------------------------------------- + + connect(d->view, TQ_SIGNAL(signalSelected(CameraIconViewItem*, bool)), + this, TQ_SLOT(slotItemsSelected(CameraIconViewItem*, bool))); + + connect(d->view, TQ_SIGNAL(signalFileView(CameraIconViewItem*)), + this, TQ_SLOT(slotFileView(CameraIconViewItem*))); + + connect(d->view, TQ_SIGNAL(signalUpload(const KURL::List&)), + this, TQ_SLOT(slotUploadItems(const KURL::List&))); + + connect(d->view, TQ_SIGNAL(signalDownload()), + this, TQ_SLOT(slotDownloadSelected())); + + connect(d->view, TQ_SIGNAL(signalDownloadAndDelete()), + this, TQ_SLOT(slotDownloadAndDeleteSelected())); + + connect(d->view, TQ_SIGNAL(signalDelete()), + this, TQ_SLOT(slotDeleteSelected())); + + connect(d->view, TQ_SIGNAL(signalToggleLock()), + this, TQ_SLOT(slotToggleLock())); + + connect(d->view, TQ_SIGNAL(signalNewSelection(bool)), + this, TQ_SLOT(slotNewSelection(bool))); + + // ------------------------------------------------------------------------- + + connect(d->rightSidebar, TQ_SIGNAL(signalFirstItem()), + this, TQ_SLOT(slotFirstItem())); + + connect(d->rightSidebar, TQ_SIGNAL(signalNextItem()), + this, TQ_SLOT(slotNextItem())); + + connect(d->rightSidebar, TQ_SIGNAL(signalPrevItem()), + this, TQ_SLOT(slotPrevItem())); + + connect(d->rightSidebar, TQ_SIGNAL(signalLastItem()), + this, TQ_SLOT(slotLastItem())); + + // ------------------------------------------------------------------------- + + connect(d->cancelBtn, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotCancelButton())); + + // -- Read settings & Check free space availability on album root path ----- + + readSettings(); + + // -- camera controller ---------------------------------------------------- + + d->controller = new CameraController(this, d->cameraTitle, model, port, path); + + connect(d->controller, TQ_SIGNAL(signalConnected(bool)), + this, TQ_SLOT(slotConnected(bool))); + + connect(d->controller, TQ_SIGNAL(signalInfoMsg(const TQString&)), + d->status, TQ_SLOT(setText(const TQString&))); + + connect(d->controller, TQ_SIGNAL(signalErrorMsg(const TQString&)), + this, TQ_SLOT(slotErrorMsg(const TQString&))); + + connect(d->controller, TQ_SIGNAL(signalCameraInformations(const TQString&, const TQString&, const TQString&)), + this, TQ_SLOT(slotCameraInformations(const TQString&, const TQString&, const TQString&))); + + connect(d->controller, TQ_SIGNAL(signalBusy(bool)), + this, TQ_SLOT(slotBusy(bool))); + + connect(d->controller, TQ_SIGNAL(signalFolderList(const TQStringList&)), + this, TQ_SLOT(slotFolderList(const TQStringList&))); + + connect(d->controller, TQ_SIGNAL(signalFileList(const GPItemInfoList&)), + this, TQ_SLOT(slotFileList(const GPItemInfoList&))); + + connect(d->controller, TQ_SIGNAL(signalThumbnail(const TQString&, const TQString&, const TQImage&)), + this, TQ_SLOT(slotThumbnail(const TQString&, const TQString&, const TQImage&))); + + connect(d->controller, TQ_SIGNAL(signalDownloaded(const TQString&, const TQString&, int)), + this, TQ_SLOT(slotDownloaded(const TQString&, const TQString&, int))); + + connect(d->controller, TQ_SIGNAL(signalSkipped(const TQString&, const TQString&)), + this, TQ_SLOT(slotSkipped(const TQString&, const TQString&))); + + connect(d->controller, TQ_SIGNAL(signalDeleted(const TQString&, const TQString&, bool)), + this, TQ_SLOT(slotDeleted(const TQString&, const TQString&, bool))); + + connect(d->controller, TQ_SIGNAL(signalLocked(const TQString&, const TQString&, bool)), + this, TQ_SLOT(slotLocked(const TQString&, const TQString&, bool))); + + connect(d->controller, TQ_SIGNAL(signalExifFromFile(const TQString&, const TQString&)), + this, TQ_SLOT(slotExifFromFile(const TQString&, const TQString&))); + + connect(d->controller, TQ_SIGNAL(signalExifData(const TQByteArray&)), + this, TQ_SLOT(slotExifFromData(const TQByteArray&))); + + connect(d->controller, TQ_SIGNAL(signalUploaded(const GPItemInfo&)), + this, TQ_SLOT(slotUploaded(const GPItemInfo&))); + + // ------------------------------------------------------------------------- + + d->view->setFocus(); + TQTimer::singleShot(0, d->controller, TQ_SLOT(slotConnect())); +} + +CameraUI::~CameraUI() +{ + delete d->rightSidebar; + delete d->controller; + delete d; +} + +void CameraUI::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("Camera Settings"); + d->advBox->setCurrentIndex(config->readNumEntry("Settings Tab", CameraUIPriv::RENAMEFILEPAGE)); + d->autoRotateCheck->setChecked(config->readBoolEntry("AutoRotate", true)); + d->autoAlbumDateCheck->setChecked(config->readBoolEntry("AutoAlbumDate", false)); + d->autoAlbumExtCheck->setChecked(config->readBoolEntry("AutoAlbumExt", false)); + d->fixDateTimeCheck->setChecked(config->readBoolEntry("FixDateTime", false)); + d->setPhotographerId->setChecked(config->readBoolEntry("SetPhotographerId", false)); + d->setCredits->setChecked(config->readBoolEntry("SetCredits", false)); + d->convertJpegCheck->setChecked(config->readBoolEntry("ConvertJpeg", false)); + d->losslessFormat->setCurrentItem(config->readNumEntry("LossLessFormat", 0)); // PNG by default + d->folderDateFormat->setCurrentItem(config->readNumEntry("FolderDateFormat", CameraUIPriv::IsoDateFormat)); + + d->view->setThumbnailSize(ThumbnailSize((ThumbnailSize::Size)config->readNumEntry("ThumbnailSize", + ThumbnailSize::Large))); + + if(config->hasKey("Splitter Sizes")) + d->splitter->setSizes(config->readIntListEntry("Splitter Sizes")); + + d->dateTimeEdit->setEnabled(d->fixDateTimeCheck->isChecked()); + d->losslessFormat->setEnabled(convertLosslessJpegFiles()); + d->formatLabel->setEnabled(convertLosslessJpegFiles()); + d->folderDateFormat->setEnabled(d->autoAlbumDateCheck->isChecked()); + d->folderDateLabel->setEnabled(d->autoAlbumDateCheck->isChecked()); + + resize(configDialogSize("Camera Settings")); +} + +void CameraUI::saveSettings() +{ + saveDialogSize("Camera Settings"); + + TDEConfig* config = kapp->config(); + config->setGroup("Camera Settings"); + config->writeEntry("Settings Tab", d->advBox->currentIndex()); + config->writeEntry("AutoRotate", d->autoRotateCheck->isChecked()); + config->writeEntry("AutoAlbumDate", d->autoAlbumDateCheck->isChecked()); + config->writeEntry("AutoAlbumExt", d->autoAlbumExtCheck->isChecked()); + config->writeEntry("FixDateTime", d->fixDateTimeCheck->isChecked()); + config->writeEntry("SetPhotographerId", d->setPhotographerId->isChecked()); + config->writeEntry("SetCredits", d->setCredits->isChecked()); + config->writeEntry("ConvertJpeg", convertLosslessJpegFiles()); + config->writeEntry("LossLessFormat", d->losslessFormat->currentItem()); + config->writeEntry("ThumbnailSize", d->view->thumbnailSize().size()); + config->writeEntry("Splitter Sizes", d->splitter->sizes()); + config->writeEntry("FolderDateFormat", d->folderDateFormat->currentItem()); + config->sync(); +} + +void CameraUI::slotProcessURL(const TQString& url) +{ + TDEApplication::kApplication()->invokeBrowser(url); +} + +bool CameraUI::isBusy() const +{ + return d->busy; +} + +bool CameraUI::isClosed() const +{ + return d->closed; +} + +bool CameraUI::convertLosslessJpegFiles() const +{ + return d->convertJpegCheck->isChecked(); +} + +bool CameraUI::autoRotateJpegFiles() const +{ + return d->autoRotateCheck->isChecked(); +} + +TQString CameraUI::losslessFormat() +{ + return d->losslessFormat->currentText(); +} + +TQString CameraUI::cameraTitle() const +{ + return d->cameraTitle; +} + +void CameraUI::slotCancelButton() +{ + d->status->setText(i18n("Cancelling current operation, please wait...")); + d->progress->hide(); + TQTimer::singleShot(0, d->controller, TQ_SLOT(slotCancel())); + d->currentlyDeleting.clear(); +} + +void CameraUI::closeEvent(TQCloseEvent* e) +{ + if (dialogClosed()) + e->accept(); + else + e->ignore(); +} + +void CameraUI::slotClose() +{ + if (dialogClosed()) + reject(); +} + +bool CameraUI::dialogClosed() +{ + if (d->closed) + return true; + + if (isBusy()) + { + if (KMessageBox::questionYesNo(this, + i18n("Do you want to close the dialog " + "and cancel the current operation?")) + == KMessageBox::No) + return false; + } + + d->status->setText(i18n("Disconnecting from camera, please wait...")); + d->progress->hide(); + + if (isBusy()) + { + d->controller->slotCancel(); + // will be read in slotBusy later and finishDialog + // will be called only when everything is finished + d->closed = true; + } + else + { + d->closed = true; + finishDialog(); + } + + return true; +} + +void CameraUI::finishDialog() +{ + // Look if an item have been downloaded to computer during camera gui session. + // If yes, update the lastAccess date property of camera in digiKam camera list. + + if (d->view->itemsDownloaded() > 0) + { + CameraList* clist = CameraList::instance(); + if (clist) + clist->changeCameraAccessTime(d->cameraTitle, TQDateTime::TQDateTime::currentDateTime()); + } + + // When a directory is created, a watch is put on it to spot new files + // but it can occur that the file is copied there before the watch is + // completely setup. That is why as an extra safeguard run scanlib + // over the folders we used. Bug: 119201 + + d->status->setText(i18n("Scanning for new files, please wait...")); + ScanLib sLib; + for (TQStringList::iterator it = d->foldersToScan.begin(); + it != d->foldersToScan.end(); ++it) + { + //DDebug() << "Scanning " << (*it) << endl; + sLib.findMissingItems( (*it) ); + } + + // Never call finalScan after deleteLater() - ScanLib will call processEvent(), + // and the delete event may be executed! + deleteLater(); + + if(!d->lastDestURL.isEmpty()) + emit signalLastDestination(d->lastDestURL); + + saveSettings(); +} + +void CameraUI::slotBusy(bool val) +{ + if (!val) + { + if (!d->busy) + return; + + d->busy = false; + d->cancelBtn->setEnabled(false); + d->view->viewport()->setEnabled(true); + + d->advBox->setEnabled(true); + // B.K.O #127614: The Focus need to be restored in custom prefix widget. + //commenting this out again: If we do not disable, no need to restore focus + //d->renameCustomizer->restoreFocus(); + + enableButton(User3, true); + enableButton(User2, true); + enableButton(User1, true); + d->helpMenu->menu()->setItemEnabled(CAMERA_INFO_MENU_ID, true); + + d->anim->stop(); + d->status->setText(i18n("Ready")); + d->progress->hide(); + + // like WDestructiveClose, but after camera controller operation has safely finished + if (d->closed) + { + finishDialog(); + } + } + else + { + if (d->busy) + return; + + if (!d->anim->running()) + d->anim->start(); + + d->busy = true; + d->cancelBtn->setEnabled(true); + + // Has camera icon view item selection is used to control download post processing, + // all selection actions are disable when camera interface is busy. + d->view->viewport()->setEnabled(false); + + // Settings tab is disabled in slotDownload, selectively when downloading + // Fast dis/enabling would create the impression of flicker, e.g. when retrieving EXIF from camera + //d->advBox->setEnabled(false); + + enableButton(User3, false); + enableButton(User2, false); + enableButton(User1, false); + d->helpMenu->menu()->setItemEnabled(CAMERA_INFO_MENU_ID, false); + } +} + +void CameraUI::slotIncreaseThumbSize() +{ + int thumbSize = d->view->thumbnailSize().size(); + if (thumbSize >= ThumbnailSize::Huge) return; + + thumbSize += ThumbnailSize::Step; + + if (thumbSize >= ThumbnailSize::Huge) + { + d->imageMenu->setItemEnabled(4, false); + } + d->imageMenu->setItemEnabled(5, true); + + d->view->setThumbnailSize(thumbSize); +} + +void CameraUI::slotDecreaseThumbSize() +{ + int thumbSize = d->view->thumbnailSize().size(); + if (thumbSize <= ThumbnailSize::Small) return; + + thumbSize -= ThumbnailSize::Step; + + if (thumbSize <= ThumbnailSize::Small) + { + d->imageMenu->setItemEnabled(5, false); + } + d->imageMenu->setItemEnabled(4, true); + + d->view->setThumbnailSize(thumbSize); +} + +void CameraUI::slotConnected(bool val) +{ + if (!val) + { + if (KMessageBox::warningYesNo(this, + i18n("Failed to connect to the camera. " + "Please make sure it is connected " + "properly and turned on. " + "Would you like to try again?"), + i18n("Connection Failed"), + i18n("Retry"), + i18n("Abort")) + == KMessageBox::Yes) + TQTimer::singleShot(0, d->controller, TQ_SLOT(slotConnect())); + else + close(); + } + else + { + d->controller->listFolders(); + } +} + +void CameraUI::slotFolderList(const TQStringList& folderList) +{ + if (d->closed) + return; + + d->progress->setProgress(0); + d->progress->setTotalSteps(0); + d->progress->show(); + + d->cameraFolderList = folderList; + for (TQStringList::const_iterator it = folderList.begin(); + it != folderList.end(); ++it) + { + d->controller->listFiles(*it); + } +} + +void CameraUI::slotFileList(const GPItemInfoList& fileList) +{ + if (d->closed) + return; + + if (fileList.empty()) + return; + + kdDebug() << fileList.count() << endl; + + // We sort the map by time stamp + // and we remove internal camera files which are not image/video/sounds. + TQStringList fileNames, fileExts; + TQFileInfo info; + + // JVC camera (see B.K.O #133185). + fileNames.append("mgr_data"); + fileNames.append("pgr_mgr"); + + // HP Photosmart camera (see B.K.O #156338). + fileExts.append("dsp"); + + // Minolta camera in PTP mode + fileExts.append("dps"); + + // We sort the map by time stamp. + GPItemInfoList sfileList; + GPItemInfoList::const_iterator it; + GPItemInfoList::iterator it2; + + for(it = fileList.begin() ; it != fileList.end() ; ++it) + { + info.setFile((*it).name); + if (!fileNames.contains(info.fileName().lower()) && + !fileExts.contains(info.extension(false).lower())) + { + kdDebug() << info.fileName() << " : " << (*it).mtime << endl; + + for(it2 = sfileList.begin() ; it2 != sfileList.end() ; ++it2) + if ((*it2).mtime <= (*it).mtime) break; + + sfileList.insert(it2, *it); + } + } + + if (sfileList.empty()) + return; + + kdDebug() << sfileList.count() << endl; + + GPItemInfoList::const_iterator it3 = sfileList.begin(); + + do + { + GPItemInfo item = *it3; + + if (item.mtime > (time_t)d->lastAccess.toTime_t() && item.downloaded == GPItemInfo::DownloadUnknow) + item.downloaded = GPItemInfo::NewPicture; + + d->view->addItem(item); + d->controller->getThumbnail(item.folder, item.name); + ++it3; + } + while(it3 != sfileList.end()); + + d->progress->setTotalSteps(d->progress->totalSteps() + fileList.count()); +} + +void CameraUI::slotThumbnail(const TQString& folder, const TQString& file, + const TQImage& thumbnail) +{ + d->view->setThumbnail(folder, file, thumbnail); + int curr = d->progress->progress(); + d->progress->setProgress(curr+1); +} + +void CameraUI::slotInformations() +{ + if (d->busy) + return; + + d->controller->getCameraInformations(); +} + +void CameraUI::slotCameraInformations(const TQString& summary, const TQString& manual, const TQString& about) +{ + CameraInfoDialog *infoDlg = new CameraInfoDialog(this, summary, manual, about); + infoDlg->show(); +} + +void CameraUI::slotErrorMsg(const TQString& msg) +{ + KMessageBox::error(this, msg); +} + +void CameraUI::slotUpload() +{ + if (d->busy) + return; + + TQString fileformats; + + TQStringList patternList = TQStringList::split('\n', KImageIO::pattern(KImageIO::Reading)); + + // All Images from list must been always the first entry given by KDE API + TQString allPictures = patternList[0]; + + // Add RAW file format to All Images" type mime and remplace current. +#if KDCRAW_VERSION < 0x000106 + allPictures.insert(allPictures.find("|"), TQString(KDcrawIface::DcrawBinary::instance()->rawFiles())); +#else + allPictures.insert(allPictures.find("|"), TQString(KDcrawIface::KDcraw::rawFiles())); +#endif + patternList.remove(patternList[0]); + patternList.prepend(allPictures); + + // Added RAW file formats supported by dcraw program like a type mime. + // Nota: we cannot use here "image/x-raw" type mime from KDE because it uncomplete + // or unavailable(dcraw_0)(see file #121242 in B.K.O). +#if KDCRAW_VERSION < 0x000106 + patternList.append(TQString("\n%1|Camera RAW files").arg(TQString(KDcrawIface::DcrawBinary::instance()->rawFiles()))); +#else + patternList.append(TQString("\n%1|Camera RAW files").arg(TQString(KDcrawIface::KDcraw::rawFiles()))); +#endif + + fileformats = patternList.join("\n"); + + DDebug () << "fileformats=" << fileformats << endl; + + KURL::List urls = KFileDialog::getOpenURLs(AlbumManager::instance()->getLibraryPath(), + fileformats, this, i18n("Select Image to Upload")); + if (!urls.isEmpty()) + slotUploadItems(urls); +} + +void CameraUI::slotUploadItems(const KURL::List& urls) +{ + if (d->busy) + return; + + if (urls.isEmpty()) + return; + + CameraFolderDialog dlg(this, d->view, d->cameraFolderList, d->controller->getCameraTitle(), + d->controller->getCameraPath()); + + if (dlg.exec() != TQDialog::Accepted) + return; + + TQString cameraFolder = dlg.selectedFolderPath(); + + for (KURL::List::const_iterator it = urls.begin() ; it != urls.end() ; ++it) + { + TQFileInfo fi((*it).path()); + if (!fi.exists()) continue; + if (fi.isDir()) continue; + + TQString ext = TQString(".") + fi.extension(); + TQString name = fi.fileName(); + name.truncate(fi.fileName().length() - ext.length()); + + bool ok; + + while (d->view->findItem(cameraFolder, name + ext)) + { + TQString msg(i18n("Camera Folder <b>%1</b> already contains item <b>%2</b><br>" + "Please enter a new file name (without extension):") + .arg(cameraFolder).arg(fi.fileName())); +#if KDE_IS_VERSION(3,2,0) + name = KInputDialog::getText(i18n("File already exists"), msg, name, &ok, this); + +#else + name = KLineEditDlg::getText(i18n("File already exists"), msg, name, &ok, this); +#endif + if (!ok) + return; + } + + d->controller->upload(fi, name + ext, cameraFolder); + } +} + +void CameraUI::slotUploaded(const GPItemInfo& itemInfo) +{ + if (d->closed) + return; + + d->view->addItem(itemInfo); + d->controller->getThumbnail(itemInfo.folder, itemInfo.name); +} + +void CameraUI::slotDownloadSelected() +{ + slotDownload(true, false); +} + +void CameraUI::slotDownloadAndDeleteSelected() +{ + slotDownload(true, true); +} + +void CameraUI::slotDownloadAll() +{ + slotDownload(false, false); +} + +void CameraUI::slotDownloadAndDeleteAll() +{ + slotDownload(false, true); +} + +void CameraUI::slotDownload(bool onlySelected, bool deleteAfter, Album *album) +{ + // See B.K.O #143934: force to select all items to prevent problem + // when !renameCustomizer->useDefault() ==> iconItem->getDownloadName() + // can return an empty string in this case because it depends on selection. + if (!onlySelected) + d->view->slotSelectAll(); + + // See B.K.O #139519: Always check free space available before to + // download items selection from camera. + unsigned long fSize = 0; + unsigned long dSize = 0; + d->view->itemsSelectionSizeInfo(fSize, dSize); + if (d->freeSpaceWidget->isValid() && (dSize >= d->freeSpaceWidget->kBAvail())) + { + KMessageBox::error(this, i18n("There is no enough free space on Album Library Path " + "to download and process selected pictures from camera.\n\n" + "Estimated space require: %1\n" + "Available free space: %2") + .arg(TDEIO::convertSizeFromKB(dSize)) + .arg(TDEIO::convertSizeFromKB(d->freeSpaceWidget->kBAvail()))); + return; + } + + TQString newDirName; + IconItem* firstItem = d->view->firstItem(); + if (firstItem) + { + CameraIconViewItem* iconItem = static_cast<CameraIconViewItem*>(firstItem); + + TQDateTime dateTime; + dateTime.setTime_t(iconItem->itemInfo()->mtime); + + switch(d->folderDateFormat->currentItem()) + { + case CameraUIPriv::TextDateFormat: + newDirName = dateTime.date().toString(TQt::TextDate); + break; + case CameraUIPriv::LocalDateFormat: + newDirName = dateTime.date().toString(TQt::LocalDate); + break; + default: // IsoDateFormat + newDirName = dateTime.date().toString(TQt::ISODate); + break; + } + } + + // -- Get the destination album from digiKam library if necessary --------------- + + if (!album) + { + AlbumManager* man = AlbumManager::instance(); + album = man->currentAlbum(); + + if (album && album->type() != Album::PHYSICAL) + album = 0; + + TQString header(i18n("<p>Please select the destination album from the digiKam library to " + "import the camera pictures into.</p>")); + + album = AlbumSelectDialog::selectAlbum(this, (PAlbum*)album, header, newDirName, + d->autoAlbumDateCheck->isChecked()); + + if (!album) return; + } + + PAlbum *pAlbum = dynamic_cast<PAlbum*>(album); + if (!pAlbum) return; + + // -- Prepare downloading of camera items ------------------------ + + KURL url; + url.setPath(pAlbum->folderPath()); + + d->controller->downloadPrep(); + + DownloadSettingsContainer downloadSettings; + TQString downloadName; + time_t mtime; + int total = 0; + + downloadSettings.autoRotate = d->autoRotateCheck->isChecked(); + downloadSettings.fixDateTime = d->fixDateTimeCheck->isChecked(); + downloadSettings.newDateTime = d->dateTimeEdit->dateTime(); + downloadSettings.setPhotographerId = d->setPhotographerId->isChecked(); + downloadSettings.setCredits = d->setCredits->isChecked(); + downloadSettings.convertJpeg = convertLosslessJpegFiles(); + downloadSettings.losslessFormat = losslessFormat(); + + AlbumSettings* settings = AlbumSettings::instance(); + if (settings) + { + downloadSettings.author = settings->getIptcAuthor(); + downloadSettings.authorTitle = settings->getIptcAuthorTitle(); + downloadSettings.credit = settings->getIptcCredit(); + downloadSettings.source = settings->getIptcSource(); + downloadSettings.copyright = settings->getIptcCopyright(); + } + + // -- Download camera items ------------------------------- + // Since we show camera items in reverse order, downloading need to be done also in reverse order. + + for (IconItem* item = d->view->lastItem(); item; + item = item->prevItem()) + { + if (onlySelected && !(item->isSelected())) + continue; + + CameraIconViewItem* iconItem = static_cast<CameraIconViewItem*>(item); + downloadSettings.folder = iconItem->itemInfo()->folder; + downloadSettings.file = iconItem->itemInfo()->name; + downloadName = iconItem->getDownloadName(); + mtime = iconItem->itemInfo()->mtime; + + KURL u(url); + TQString errMsg; + TQDateTime dateTime; + dateTime.setTime_t(mtime); + + // Auto sub-albums creation based on file date. + + if (d->autoAlbumDateCheck->isChecked()) + { + TQString dirName; + + switch(d->folderDateFormat->currentItem()) + { + case CameraUIPriv::TextDateFormat: + dirName = dateTime.date().toString(TQt::TextDate); + break; + case CameraUIPriv::LocalDateFormat: + dirName = dateTime.date().toString(TQt::LocalDate); + break; + default: // IsoDateFormat + dirName = dateTime.date().toString(TQt::ISODate); + break; + } + // See B.K.O #136927 : we need to support file system which do not + // handle upper case properly. + dirName = dirName.lower(); + if (!createAutoAlbum(url, dirName, dateTime.date(), errMsg)) + { + KMessageBox::error(this, errMsg); + return; + } + + u.addPath(dirName); + } + + // Auto sub-albums creation based on file extensions. + + if (d->autoAlbumExtCheck->isChecked()) + { + // We use the target file name to compute sub-albums name to take a care about + // convertion on the fly option. + TQFileInfo fi(downloadName); + + TQString subAlbum = fi.extension(false).upper(); + if (fi.extension(false).upper() == TQString("JPEG") || + fi.extension(false).upper() == TQString("JPE")) + subAlbum = TQString("JPG"); + if (fi.extension(false).upper() == TQString("TIFF")) + subAlbum = TQString("TIF"); + if (fi.extension(false).upper() == TQString("MPEG") || + fi.extension(false).upper() == TQString("MPE") || + fi.extension(false).upper() == TQString("MPO")) + subAlbum = TQString("MPG"); + + // See B.K.O #136927 : we need to support file system which do not + // handle upper case properly. + subAlbum = subAlbum.lower(); + if (!createAutoAlbum(u, subAlbum, dateTime.date(), errMsg)) + { + KMessageBox::error(this, errMsg); + return; + } + + u.addPath(subAlbum); + } + + d->foldersToScan.append(u.path()); + u.addPath(downloadName.isEmpty() ? downloadSettings.file : downloadName); + + downloadSettings.dest = u.path(); + + d->controller->download(downloadSettings); + addFileExtension(TQFileInfo(u.path()).extension(false)); + total++; + } + + if (total <= 0) + return; + + d->lastDestURL = url; + d->progress->setProgress(0); + d->progress->setTotalSteps(total); + d->progress->show(); + + // disable settings tab here instead of slotBusy: + // Only needs to be disabled while downloading + d->advBox->setEnabled(false); + + d->deleteAfter = deleteAfter; +} + +void CameraUI::slotDownloaded(const TQString& folder, const TQString& file, int status) +{ + CameraIconViewItem* iconItem = d->view->findItem(folder, file); + if (iconItem) + iconItem->setDownloaded(status); + + if (status == GPItemInfo::DownloadedYes || status == GPItemInfo::DownloadFailed) + { + int curr = d->progress->progress(); + d->progress->setProgress(curr+1); + } + + // Download all items is complete. + if (d->progress->progress() == d->progress->totalSteps()) + { + if (d->deleteAfter) + deleteItems(true, true); + } +} + +void CameraUI::slotSkipped(const TQString& folder, const TQString& file) +{ + CameraIconViewItem* iconItem = d->view->findItem(folder, file); + if (iconItem) + iconItem->setDownloaded(GPItemInfo::DownloadedNo); + + int curr = d->progress->progress(); + d->progress->setProgress(curr+1); +} + +void CameraUI::slotToggleLock() +{ + int count = 0; + for (IconItem* item = d->view->firstItem(); item; + item = item->nextItem()) + { + CameraIconViewItem* iconItem = static_cast<CameraIconViewItem*>(item); + if (iconItem->isSelected()) + { + TQString folder = iconItem->itemInfo()->folder; + TQString file = iconItem->itemInfo()->name; + int writePerm = iconItem->itemInfo()->writePermissions; + bool lock = true; + + // If item is currently locked, unlock it. + if (writePerm == 0) + lock = false; + + d->controller->lockFile(folder, file, lock); + count++; + } + } + + if (count > 0) + { + d->progress->setProgress(0); + d->progress->setTotalSteps(count); + d->progress->show(); + } +} + +void CameraUI::slotLocked(const TQString& folder, const TQString& file, bool status) +{ + if (status) + { + CameraIconViewItem* iconItem = d->view->findItem(folder, file); + if (iconItem) + { + iconItem->toggleLock(); + //if (iconItem->isSelected()) + // slotItemsSelected(iconItem, true); + } + } + + int curr = d->progress->progress(); + d->progress->setProgress(curr+1); +} + +void CameraUI::checkItem4Deletion(CameraIconViewItem* iconItem, TQStringList& folders, TQStringList& files, + TQStringList& deleteList, TQStringList& lockedList) +{ + if (iconItem->itemInfo()->writePermissions != 0) // Item not locked ? + { + TQString folder = iconItem->itemInfo()->folder; + TQString file = iconItem->itemInfo()->name; + folders.append(folder); + files.append(file); + deleteList.append(folder + TQString("/") + file); + } + else + { + lockedList.append(iconItem->itemInfo()->name); + } +} + +void CameraUI::deleteItems(bool onlySelected, bool onlyDownloaded) +{ + TQStringList folders; + TQStringList files; + TQStringList deleteList; + TQStringList lockedList; + + for (IconItem* item = d->view->firstItem(); item; item = item->nextItem()) + { + CameraIconViewItem* iconItem = dynamic_cast<CameraIconViewItem*>(item); + if (iconItem) + { + if (onlySelected) + { + if (iconItem->isSelected()) + { + if (onlyDownloaded) + { + if (iconItem->isDownloaded()) + checkItem4Deletion(iconItem, folders, files, deleteList, lockedList); + } + else + { + checkItem4Deletion(iconItem, folders, files, deleteList, lockedList); + } + } + } + else // All items + { + if (onlyDownloaded) + { + if (iconItem->isDownloaded()) + checkItem4Deletion(iconItem, folders, files, deleteList, lockedList); + } + else + { + checkItem4Deletion(iconItem, folders, files, deleteList, lockedList); + } + } + } + } + + // If we want to delete some locked files, just give a feedback to user. + if (!lockedList.isEmpty()) + { + TQString infoMsg(i18n("The items listed below are locked by camera (read-only). " + "These items will not be deleted. If you really want to delete these items, " + "please unlock them and try again.")); + KMessageBox::informationList(this, infoMsg, lockedList, i18n("Information")); + } + + if (folders.isEmpty()) + return; + + TQString warnMsg(i18n("About to delete this image. " + "Deleted files are unrecoverable. " + "Are you sure?", + "About to delete these %n images. " + "Deleted files are unrecoverable. " + "Are you sure?", + deleteList.count())); + if (KMessageBox::warningContinueCancelList(this, warnMsg, + deleteList, + i18n("Warning"), + i18n("Delete")) + == KMessageBox::Continue) + { + TQStringList::iterator itFolder = folders.begin(); + TQStringList::iterator itFile = files.begin(); + + d->progress->setProgress(0); + d->progress->setTotalSteps(deleteList.count()); + d->progress->show(); + + for ( ; itFolder != folders.end(); ++itFolder, ++itFile) + { + d->controller->deleteFile(*itFolder, *itFile); + // the currentlyDeleting list is used to prevent loading items which + // will immenently be deleted into the sidebar and wasting time + d->currentlyDeleting.append(*itFolder + *itFile); + } + } +} + +void CameraUI::slotDeleteSelected() +{ + deleteItems(true, false); +} + +void CameraUI::slotDeleteAll() +{ + deleteItems(false, false); +} + +void CameraUI::slotDeleted(const TQString& folder, const TQString& file, bool status) +{ + if (status) + { + d->view->removeItem(folder, file); + // do this after removeItem, which will signal to slotItemsSelected, which checks for the list + d->currentlyDeleting.remove(folder + file); + } + + int curr = d->progress->progress(); + d->progress->setProgress(curr+1); +} + +void CameraUI::slotFileView(CameraIconViewItem* item) +{ + d->controller->openFile(item->itemInfo()->folder, item->itemInfo()->name); +} + +void CameraUI::slotExifFromFile(const TQString& folder, const TQString& file) +{ + CameraIconViewItem* item = d->view->findItem(folder, file); + if (!item) + return; + + d->rightSidebar->itemChanged(item->itemInfo(), folder + TQString("/") + file, + TQByteArray(), d->view, item); +} + +void CameraUI::slotExifFromData(const TQByteArray& exifData) +{ + CameraIconViewItem* item = dynamic_cast<CameraIconViewItem*>(d->view->currentItem()); + + if (!item) + return; + + KURL url(item->itemInfo()->folder + '/' + item->itemInfo()->name); + + // Sometimes, GPhoto2 drivers return complete APP1 JFIF section. Exiv2 cannot + // decode (yet) exif metadata from APP1. We will find Exif header to get data at this place + // to please with Exiv2... + + DDebug() << "Size of Exif metadata from camera = " << exifData.size() << endl; + char exifHeader[] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; + + if (!exifData.isEmpty()) + { + int i = exifData.find(*exifHeader); + if (i != -1) + { + DDebug() << "Exif header found at position " << i << endl; + i = i + sizeof(exifHeader); + TQByteArray data(exifData.size()-i); + memcpy(data.data(), exifData.data()+i, data.size()); + d->rightSidebar->itemChanged(item->itemInfo(), url, data, d->view, item); + return; + } + } + + d->rightSidebar->itemChanged(item->itemInfo(), url, exifData, d->view, item); +} + +void CameraUI::slotNewSelection(bool hasSelection) +{ + if (!d->renameCustomizer->useDefault()) + { + d->downloadMenu->setItemEnabled(0, hasSelection); + d->downloadMenu->setItemEnabled(2, hasSelection); + } + else + { + d->downloadMenu->setItemEnabled(0, hasSelection); + d->downloadMenu->setItemEnabled(2, hasSelection); + } + + unsigned long fSize = 0; + unsigned long dSize = 0; + d->view->itemsSelectionSizeInfo(fSize, dSize); + d->freeSpaceWidget->setEstimatedDSizeKb(dSize); +} + +void CameraUI::slotItemsSelected(CameraIconViewItem* item, bool selected) +{ + d->downloadMenu->setItemEnabled(0, selected); + d->downloadMenu->setItemEnabled(2, selected); + d->deleteMenu->setItemEnabled(0, selected); + + if (selected) + { + // if selected item is in the list of item which will be deleted, set no current item + if (d->currentlyDeleting.find(item->itemInfo()->folder + item->itemInfo()->name) + == d->currentlyDeleting.end()) + { + KURL url(item->itemInfo()->folder + '/' + item->itemInfo()->name); + d->rightSidebar->itemChanged(item->itemInfo(), url, TQByteArray(), d->view, item); + d->controller->getExif(item->itemInfo()->folder, item->itemInfo()->name); + } + else + { + d->rightSidebar->slotNoCurrentItem(); + } + } + else + d->rightSidebar->slotNoCurrentItem(); +} + +bool CameraUI::createAutoAlbum(const KURL& parentURL, const TQString& sub, + const TQDate& date, TQString& errMsg) +{ + KURL u(parentURL); + u.addPath(sub); + + // first stat to see if the album exists + TQFileInfo info(u.path()); + if (info.exists()) + { + // now check if its really a directory + if (info.isDir()) + return true; + else + { + errMsg = i18n("A file with same name (%1) exists in folder %2") + .arg(sub) + .arg(parentURL.path()); + return false; + } + } + + // looks like the directory does not exist, try to create it + + AlbumManager* aman = AlbumManager::instance(); + PAlbum* parent = aman->findPAlbum(parentURL); + if (!parent) + { + errMsg = i18n("Failed to find Album for path '%1'") + .arg(parentURL.path()); + return false; + } + + return aman->createPAlbum(parent, sub, TQString(""), date, TQString(""), errMsg); +} + +void CameraUI::addFileExtension(const TQString& ext) +{ + AlbumSettings* settings = AlbumSettings::instance(); + if (!settings) + return; + + if (settings->getImageFileFilter().upper().contains(ext.upper()) || + settings->getMovieFileFilter().upper().contains(ext.upper()) || + settings->getAudioFileFilter().upper().contains(ext.upper()) || + settings->getRawFileFilter().upper().contains(ext.upper())) + return; + + settings->setImageFileFilter(settings->getImageFileFilter() + TQString(" *.") + ext); + emit signalAlbumSettingsChanged(); +} + +void CameraUI::slotFirstItem() +{ + CameraIconViewItem *currItem = dynamic_cast<CameraIconViewItem*>(d->view->firstItem()); + d->view->clearSelection(); + d->view->updateContents(); + if (currItem) + d->view->setCurrentItem(currItem); +} + +void CameraUI::slotPrevItem() +{ + CameraIconViewItem *currItem = dynamic_cast<CameraIconViewItem*>(d->view->currentItem()); + d->view->clearSelection(); + d->view->updateContents(); + if (currItem) + d->view->setCurrentItem(currItem->prevItem()); +} + +void CameraUI::slotNextItem() +{ + CameraIconViewItem *currItem = dynamic_cast<CameraIconViewItem*>(d->view->currentItem()); + d->view->clearSelection(); + d->view->updateContents(); + if (currItem) + d->view->setCurrentItem(currItem->nextItem()); +} + +void CameraUI::slotLastItem(void) +{ + CameraIconViewItem *currItem = dynamic_cast<CameraIconViewItem*>(d->view->lastItem()); + d->view->clearSelection(); + d->view->updateContents(); + if (currItem) + d->view->setCurrentItem(currItem); +} + +// Backport KDialog::keyPressEvent() implementation from KDELibs to ignore Enter/Return Key events +// to prevent any conflicts between dialog keys events and SpinBox keys events. + +void CameraUI::keyPressEvent(TQKeyEvent *e) +{ + if ( e->state() == 0 ) + { + switch ( e->key() ) + { + case Key_Escape: + e->accept(); + reject(); + break; + case Key_Enter: + case Key_Return: + e->ignore(); + break; + default: + e->ignore(); + return; + } + } + else + { + // accept the dialog when Ctrl-Return is pressed + if ( e->state() == ControlButton && + (e->key() == Key_Return || e->key() == Key_Enter) ) + { + e->accept(); + accept(); + } + else + { + e->ignore(); + } + } +} + +} // namespace Digikam diff --git a/src/utilities/cameragui/cameraui.h b/src/utilities/cameragui/cameraui.h new file mode 100644 index 00000000..df9c7885 --- /dev/null +++ b/src/utilities/cameragui/cameraui.h @@ -0,0 +1,155 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-16 + * Description : Camera interface dialog + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef CAMERAUI_H +#define CAMERAUI_H + +// TQt includes. + +#include <tqdatetime.h> +#include <tqstring.h> +#include <tqimage.h> + +// KDE includes. + +#include <kdialogbase.h> +#include <kurl.h> + +// Local includes. + +#include "gpiteminfo.h" + +namespace Digikam +{ + +class Album; +class CameraIconViewItem; +class CameraUIPriv; + +class CameraUI : public KDialogBase +{ + TQ_OBJECT + + +public: + + CameraUI(TQWidget* parent, const TQString& cameraTitle, + const TQString& model, const TQString& port, + const TQString& path, const TQDateTime lastAccess); + ~CameraUI(); + + bool isBusy() const; + bool isClosed() const; + + bool autoRotateJpegFiles() const; + + /** Get status of JPEG conversion files to lossless format during download.*/ + bool convertLosslessJpegFiles() const; + TQString losslessFormat(); + + TQString cameraTitle() const; + +signals: + + void signalLastDestination(const KURL&); + void signalAlbumSettingsChanged(); + +public slots: + + void slotDownload(bool onlySelected, bool deleteAfter, Album *album=0); + +protected: + + void closeEvent(TQCloseEvent* e); + void keyPressEvent(TQKeyEvent *e); + +private: + + void readSettings(); + void saveSettings(); + bool dialogClosed(); + bool createAutoAlbum(const KURL& parentURL, const TQString& sub, + const TQDate& date, TQString& errMsg); + void addFileExtension(const TQString& ext); + void finishDialog(); + void deleteItems(bool onlySelected, bool onlyDownloaded); + void checkItem4Deletion(CameraIconViewItem* iconItem, TQStringList& folders, TQStringList& files, + TQStringList& deleteList, TQStringList& lockedList); + +private slots: + + void slotClose(); + void slotCancelButton(); + void slotProcessURL(const TQString& url); + + void slotConnected(bool val); + void slotBusy(bool val); + void slotErrorMsg(const TQString& msg); + void slotInformations(); + void slotCameraInformations(const TQString&, const TQString&, const TQString&); + + void slotFolderList(const TQStringList& folderList); + void slotFileList(const GPItemInfoList& fileList); + void slotThumbnail(const TQString&, const TQString&, const TQImage&); + + void slotIncreaseThumbSize(); + void slotDecreaseThumbSize(); + + void slotUpload(); + void slotUploadItems(const KURL::List&); + void slotDownloadSelected(); + void slotDownloadAll(); + void slotDeleteSelected(); + void slotDownloadAndDeleteSelected(); + void slotDeleteAll(); + void slotDownloadAndDeleteAll(); + void slotToggleLock(); + + void slotFileView(CameraIconViewItem* item); + + void slotUploaded(const GPItemInfo&); + void slotDownloaded(const TQString&, const TQString&, int); + void slotSkipped(const TQString&, const TQString&); + void slotDeleted(const TQString&, const TQString&, bool); + void slotLocked(const TQString&, const TQString&, bool); + + void slotNewSelection(bool); + void slotItemsSelected(CameraIconViewItem* item, bool selected); + + void slotExifFromFile(const TQString& folder, const TQString& file); + void slotExifFromData(const TQByteArray& exifData); + + void slotFirstItem(void); + void slotPrevItem(void); + void slotNextItem(void); + void slotLastItem(void); + +private: + + CameraUIPriv* d; +}; + +} // namespace Digikam + +#endif /* CAMERAUI_H */ diff --git a/src/utilities/cameragui/dkcamera.cpp b/src/utilities/cameragui/dkcamera.cpp new file mode 100644 index 00000000..8f318855 --- /dev/null +++ b/src/utilities/cameragui/dkcamera.cpp @@ -0,0 +1,113 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-21 + * Description : abstract camera interface class + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqdeepcopy.h> + +// Local includes. + +#include "albumsettings.h" +#include "dkcamera.h" + +namespace Digikam +{ + +DKCamera::DKCamera(const TQString& title, const TQString& model, const TQString& port, const TQString& path) +{ + m_title = title; + m_model = model; + m_port = port; + m_path = path; + + AlbumSettings* settings = AlbumSettings::instance(); + m_imageFilter = TQDeepCopy<TQString>(settings->getImageFileFilter()); + m_movieFilter = TQDeepCopy<TQString>(settings->getMovieFileFilter()); + m_audioFilter = TQDeepCopy<TQString>(settings->getAudioFileFilter()); + m_rawFilter = TQDeepCopy<TQString>(settings->getRawFileFilter()); + + m_imageFilter = m_imageFilter.lower(); + m_movieFilter = m_movieFilter.lower(); + m_audioFilter = m_audioFilter.lower(); + m_rawFilter = m_rawFilter.lower(); +} + +DKCamera::~DKCamera() +{ +} + +TQString DKCamera::title() const +{ + return m_title; +} + +TQString DKCamera::model() const +{ + return m_model; +} + +TQString DKCamera::port() const +{ + return m_port; +} + +TQString DKCamera::path() const +{ + return m_path; +} + +TQString DKCamera::mimeType(const TQString& fileext) const +{ + if (fileext.isEmpty()) return TQString(); + + TQString ext = fileext; + TQString mime; + + // Massage known variations of known mimetypes into KDE specific ones + if (ext == "jpg" || ext == "jpe") + ext = "jpeg"; + else if (ext == "tif") + ext = "tiff"; + + if (m_rawFilter.contains(ext)) + { + mime = TQString("image/x-raw"); + } + else if (m_imageFilter.contains(ext)) + { + mime = TQString("image/") + ext; + } + else if (m_movieFilter.contains(ext)) + { + mime = TQString("video/") + ext; + } + else if (m_audioFilter.contains(ext)) + { + mime = TQString("audio/") + ext; + } + + return mime; +} + +} // namespace Digikam diff --git a/src/utilities/cameragui/dkcamera.h b/src/utilities/cameragui/dkcamera.h new file mode 100644 index 00000000..2ef76723 --- /dev/null +++ b/src/utilities/cameragui/dkcamera.h @@ -0,0 +1,97 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-21 + * Description : abstract camera interface class + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DKCAMERA_H +#define DKCAMERA_H + +// TQt includes. + +#include <tqstring.h> + +// Local includes. + +#include "gpiteminfo.h" + +class TQStringList; +class TQImage; + +namespace Digikam +{ + +class DKCamera +{ +public: + + DKCamera(const TQString& title, const TQString& model, const TQString& port, const TQString& path); + virtual ~DKCamera(); + + virtual bool doConnect() = 0; + virtual void cancel() = 0; + + virtual void getAllFolders(const TQString& folder, TQStringList& subFolderList) = 0; + + /// If getImageDimensions is false, the camera shall set width and height to -1 + /// if the values are not immediately available + virtual bool getItemsInfoList(const TQString& folder, GPItemInfoList& infoList, bool getImageDimensions = true) = 0; + + virtual bool getThumbnail(const TQString& folder, const TQString& itemName, TQImage& thumbnail) = 0; + virtual bool getExif(const TQString& folder, const TQString& itemName, char **edata, int& esize) = 0; + + virtual bool downloadItem(const TQString& folder, const TQString& itemName, const TQString& saveFile) = 0; + virtual bool deleteItem(const TQString& folder, const TQString& itemName) = 0; + virtual bool uploadItem(const TQString& folder, const TQString& itemName, const TQString& localFile, + GPItemInfo& itemInfo, bool getImageDimensions=true) = 0; + virtual bool cameraSummary(TQString& summary) = 0; + virtual bool cameraManual(TQString& manual) = 0; + virtual bool cameraAbout(TQString& about) = 0; + + virtual bool setLockItem(const TQString& folder, const TQString& itemName, bool lock) = 0; + + TQString title() const; + TQString model() const; + TQString port() const; + TQString path() const; + +protected: + + TQString mimeType(const TQString& fileext) const; + +protected: + + TQString m_imageFilter; + TQString m_movieFilter; + TQString m_audioFilter; + TQString m_rawFilter; + +private: + + TQString m_title; + TQString m_model; + TQString m_port; + TQString m_path; +}; + +} // namespace Digikam + +#endif /* DKCAMERA_H */ diff --git a/src/utilities/cameragui/downloadsettingscontainer.h b/src/utilities/cameragui/downloadsettingscontainer.h new file mode 100644 index 00000000..b3e59501 --- /dev/null +++ b/src/utilities/cameragui/downloadsettingscontainer.h @@ -0,0 +1,79 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-21-07 + * Description : Camera item download settings container. + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DOWNLOADSETTINGSCONTAINER_H +#define DOWNLOADSETTINGSCONTAINER_H + +// TQt includes. + +#include <tqstring.h> +#include <tqdatetime.h> + +namespace Digikam +{ + +class DownloadSettingsContainer +{ + +public: + + DownloadSettingsContainer() + { + autoRotate = true; + fixDateTime = false; + setPhotographerId = false; + setCredits = false; + convertJpeg = false; + }; + + ~DownloadSettingsContainer(){}; + +public: + + bool autoRotate; + bool fixDateTime; + bool setPhotographerId; + bool setCredits; + bool convertJpeg; + + TQDateTime newDateTime; + + // File path to download. + TQString folder; + TQString file; + TQString dest; + + // New format to convert Jpeg files. + TQString losslessFormat; + + // IPTC settings + TQString author; + TQString authorTitle; + TQString credit; + TQString source; + TQString copyright; +}; + +} // namespace Digikam + +#endif // DOWNLOADSETTINGSCONTAINER_H diff --git a/src/utilities/cameragui/freespacewidget.cpp b/src/utilities/cameragui/freespacewidget.cpp new file mode 100644 index 00000000..0d7a26b1 --- /dev/null +++ b/src/utilities/cameragui/freespacewidget.cpp @@ -0,0 +1,252 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-08-31 + * Description : a widget to display free space for a mount-point. + * + * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqwhatsthis.h> +#include <tqtooltip.h> +#include <tqpainter.h> +#include <tqpixmap.h> +#include <tqpalette.h> +#include <tqcolor.h> +#include <tqtimer.h> +#include <tqfont.h> +#include <tqfontmetrics.h> + +// KDE includes. + +#include <kurl.h> +#include <tdelocale.h> +#include <kdiskfreesp.h> +#include <tdeio/global.h> +#include <kiconloader.h> + +// Local includes. + +#include "albumsettings.h" +#include "freespacewidget.h" +#include "freespacewidget.moc" + +namespace Digikam +{ + +class FreeSpaceWidgetPriv +{ +public: + + FreeSpaceWidgetPriv() + { + timer = 0; + isValid = false; + kBSize = 0; + kBUsed = 0; + kBAvail = 0; + dSizeKb = 0; + percentUsed = 0; + } + + bool isValid; + + int percentUsed; + + unsigned long dSizeKb; + unsigned long kBSize; + unsigned long kBUsed; + unsigned long kBAvail; + + TQString mountPoint; + + TQTimer *timer; + + TQPixmap pix; +}; + +FreeSpaceWidget::FreeSpaceWidget(TQWidget* parent, int width) + : TQWidget(parent, 0, WResizeNoErase|WRepaintNoErase) +{ + d = new FreeSpaceWidgetPriv; + setBackgroundMode(TQt::NoBackground); + setFixedWidth(width); + setMaximumHeight(fontMetrics().height()+4); + slotTimeout(); + + d->timer = new TQTimer(this); + + connect(d->timer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotTimeout())); + + d->timer->start(10000); +} + +FreeSpaceWidget::~FreeSpaceWidget() +{ + d->timer->stop(); + delete d->timer; + delete d; +} + +void FreeSpaceWidget::setEstimatedDSizeKb(unsigned long dSize) +{ + d->dSizeKb = dSize; + updatePixmap(); + repaint(); +} + +unsigned long FreeSpaceWidget::estimatedDSizeKb() +{ + return d->dSizeKb; +} + +bool FreeSpaceWidget::isValid() +{ + return d->isValid; +} + +int FreeSpaceWidget::percentUsed() +{ + return d->percentUsed; +} + +unsigned long FreeSpaceWidget::kBSize() +{ + return d->kBSize; +} + +unsigned long FreeSpaceWidget::kBUsed() +{ + return d->kBUsed; +} + +unsigned long FreeSpaceWidget::kBAvail() +{ + return d->kBAvail; +} + +TQString FreeSpaceWidget::mountPoint() +{ + return d->mountPoint; +} + +void FreeSpaceWidget::updatePixmap() +{ + TQPixmap fimgPix = SmallIcon("folder_image"); + d->pix = TQPixmap(size()); + d->pix.fill(colorGroup().background()); + + TQPainter p(&d->pix); + p.setPen(colorGroup().mid()); + p.drawRect(0, 0, d->pix.width(), d->pix.height()); + p.drawPixmap(2, d->pix.height()/2-fimgPix.height()/2, + fimgPix, 0, 0, fimgPix.width(), fimgPix.height()); + + if (isValid()) + { + // We will compute the estimated % of space size used to download and process. + unsigned long eUsedKb = d->dSizeKb + d->kBUsed; + int peUsed = (int)(100.0*((double)eUsedKb/(double)d->kBSize)); + int pClamp = peUsed > 100 ? 100 : peUsed; + p.setBrush(peUsed > 95 ? TQt::red : TQt::darkGreen); + p.setPen(TQt::white); + TQRect gRect(fimgPix.height()+2, 1, + (int)(((double)d->pix.width()-2.0-fimgPix.width()-2.0)*(pClamp/100.0)), + d->pix.height()-2); + p.drawRect(gRect); + + TQRect tRect(fimgPix.height()+2, 1, d->pix.width()-2-fimgPix.width()-2, d->pix.height()-2); + TQString text = TQString("%1%").arg(peUsed); + p.setPen(colorGroup().text()); + TQFontMetrics fontMt = p.fontMetrics(); + TQRect fontRect = fontMt.boundingRect(tRect.x(), tRect.y(), + tRect.width(), tRect.height(), 0, text); + p.drawText(tRect, TQt::AlignCenter, text); + + TQString tipText, value; + TQString header = i18n("Album Library"); + TQString headBeg("<tr bgcolor=\"orange\"><td colspan=\"2\">" + "<nobr><font size=\"-1\" color=\"black\"><b>"); + TQString headEnd("</b></font></nobr></td></tr>"); + TQString cellBeg("<tr><td><nobr><font size=-1>"); + TQString cellMid("</font></nobr></td><td><nobr><font size=-1>"); + TQString cellEnd("</font></nobr></td></tr>"); + tipText = "<table cellspacing=0 cellpadding=0>"; + tipText += headBeg + header + headEnd; + + if (d->dSizeKb > 0) + { + tipText += cellBeg + i18n("Capacity:") + cellMid; + tipText += TDEIO::convertSizeFromKB(d->kBSize) + cellEnd; + + tipText += cellBeg + i18n("Available:") + cellMid; + tipText += TDEIO::convertSizeFromKB(d->kBAvail) + cellEnd; + + tipText += cellBeg + i18n("Require:") + cellMid; + tipText += TDEIO::convertSizeFromKB(d->dSizeKb) + cellEnd; + } + else + { + tipText += cellBeg + i18n("Capacity:") + cellMid; + tipText += TDEIO::convertSizeFromKB(d->kBSize) + cellEnd; + + tipText += cellBeg + i18n("Available:") + cellMid; + tipText += TDEIO::convertSizeFromKB(d->kBAvail) + cellEnd; + } + + tipText += "</table>"; + + TQWhatsThis::add(this, tipText); + TQToolTip::add(this, tipText); + } + + p.end(); +} + +void FreeSpaceWidget::paintEvent(TQPaintEvent*) +{ + bitBlt(this, 0, 0, &d->pix); +} + +void FreeSpaceWidget::slotTimeout() +{ + TQString mountPoint = TDEIO::findPathMountPoint(AlbumSettings::instance()->getAlbumLibraryPath()); + KDiskFreeSp *job = new KDiskFreeSp; + connect(job, TQ_SIGNAL(foundMountPoint(const unsigned long&, const unsigned long&, + const unsigned long&, const TQString&)), + this, TQ_SLOT(slotAvailableFreeSpace(const unsigned long&, const unsigned long&, + const unsigned long&, const TQString&))); + job->readDF(mountPoint); +} + +void FreeSpaceWidget::slotAvailableFreeSpace(const unsigned long& kBSize, const unsigned long& kBUsed, + const unsigned long& kBAvail, const TQString& mountPoint) +{ + d->mountPoint = mountPoint; + d->kBSize = kBSize; + d->kBUsed = kBUsed; + d->kBAvail = kBAvail; + d->percentUsed = 100 - (int)(100.0*kBAvail/kBSize); + d->isValid = true; + updatePixmap(); + repaint(); +} + +} // namespace Digikam diff --git a/src/utilities/cameragui/freespacewidget.h b/src/utilities/cameragui/freespacewidget.h new file mode 100644 index 00000000..2111791a --- /dev/null +++ b/src/utilities/cameragui/freespacewidget.h @@ -0,0 +1,75 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-08-31 + * Description : a widget to display free space for a mount-point. + * + * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef FREESPACEWIDGET_H +#define FREESPACEWIDGET_H + +// TQt includes. + +#include <tqstring.h> +#include <tqwidget.h> + +namespace Digikam +{ + +class FreeSpaceWidgetPriv; + +class FreeSpaceWidget : public TQWidget +{ + TQ_OBJECT + + +public: + + FreeSpaceWidget(TQWidget* parent, int width); + ~FreeSpaceWidget(); + + void setEstimatedDSizeKb(unsigned long dSize); + unsigned long estimatedDSizeKb(); + + bool isValid() ; + int percentUsed(); + unsigned long kBSize(); + unsigned long kBUsed(); + unsigned long kBAvail(); + TQString mountPoint(); + +protected: + + void paintEvent(TQPaintEvent*); + void updatePixmap(); + +private slots: + + void slotTimeout(); + void slotAvailableFreeSpace(const unsigned long& kBSize, const unsigned long& kBUsed, + const unsigned long& kBAvail, const TQString& mountPoint); + +private: + + FreeSpaceWidgetPriv* d; +}; + +} // namespace Digikam + +#endif /* FREESPACEWIDGET_H */ diff --git a/src/utilities/cameragui/gpcamera.cpp b/src/utilities/cameragui/gpcamera.cpp new file mode 100644 index 00000000..e03d92d0 --- /dev/null +++ b/src/utilities/cameragui/gpcamera.cpp @@ -0,0 +1,1223 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-21 + * Description : Gphoto2 camera interface + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// C++ includes. + +#include <cstdio> +#include <iostream> + +// TQt includes. + +#include <tqstring.h> +#include <tqstringlist.h> +#include <tqimage.h> +#include <tqpixmap.h> +#include <tqdom.h> +#include <tqfile.h> + +// KDE includes. + +#include <tdelocale.h> + +// C Ansi includes. +extern "C" +{ +#include <gphoto2.h> +} + +// Local includes. + +#include "ddebug.h" +#include "gpcamera.h" + +namespace Digikam +{ + +class GPCameraPrivate +{ + +public: + + GPCameraPrivate() + { + camera = 0; + } + + bool cameraInitialized; + + bool thumbnailSupport; + bool deleteSupport; + bool uploadSupport; + bool mkDirSupport; + bool delDirSupport; + + TQString model; + TQString port; + TQString globalPath; + + Camera *camera; + CameraAbilities cameraAbilities; +}; + +class GPStatus +{ + +public: + + GPStatus() + { + context = gp_context_new(); + cancel = false; + gp_context_set_cancel_func(context, cancel_func, 0); + } + + ~GPStatus() + { + gp_context_unref(context); + cancel = false; + } + + GPContext *context; + static bool cancel; + + static GPContextFeedback cancel_func(GPContext *, void *) + { + return (cancel ? GP_CONTEXT_FEEDBACK_CANCEL : + GP_CONTEXT_FEEDBACK_OK); + } +}; + +bool GPStatus::cancel = false; + +GPCamera::GPCamera(const TQString& title, const TQString& model, const TQString& port, const TQString& path) + : DKCamera(title, model, port, path) +{ + m_status = 0; + + d = new GPCameraPrivate; + d->camera = 0; + d->model = model; + d->port = port; + d->globalPath = path; + d->cameraInitialized = false; + d->thumbnailSupport = false; + d->deleteSupport = false; + d->uploadSupport = false; + d->mkDirSupport = false; + d->delDirSupport = false; +} + +GPCamera::~GPCamera() +{ + if (d->camera) + { + gp_camera_unref(d->camera); + d->camera = 0; + } + + delete d; +} + +TQString GPCamera::model() const +{ + return d->model; +} + +TQString GPCamera::port() const +{ + return d->port; +} + +TQString GPCamera::path() const +{ + return d->globalPath; +} + +bool GPCamera::thumbnailSupport() +{ + return d->thumbnailSupport; +} + +bool GPCamera::deleteSupport() +{ + return d->deleteSupport; +} + +bool GPCamera::uploadSupport() +{ + return d->uploadSupport; +} + +bool GPCamera::mkDirSupport() +{ + return d->mkDirSupport; +} + +bool GPCamera::delDirSupport() +{ + return d->delDirSupport; +} + +bool GPCamera::doConnect() +{ + int errorCode; + // -- first step - setup the camera -------------------- + + if (d->camera) + { + gp_camera_unref(d->camera); + d->camera = 0; + } + + CameraAbilitiesList *abilList; + GPPortInfoList *infoList; + GPPortInfo info; + + gp_camera_new(&d->camera); + + if (m_status) + { + delete m_status; + m_status = 0; + } + + m_status = new GPStatus(); + + gp_abilities_list_new(&abilList); + gp_abilities_list_load(abilList, m_status->context); + gp_port_info_list_new(&infoList); + gp_port_info_list_load(infoList); + + delete m_status; + m_status = 0; + + int modelNum = -1, portNum = -1; + modelNum = gp_abilities_list_lookup_model(abilList, d->model.latin1()); + portNum = gp_port_info_list_lookup_path (infoList, d->port.latin1()); + + gp_abilities_list_get_abilities(abilList, modelNum, &d->cameraAbilities); + + errorCode = gp_camera_set_abilities(d->camera, d->cameraAbilities); + if (errorCode != GP_OK) + { + DDebug() << "Failed to set camera Abilities!" << endl; + printGphotoErrorDescription(errorCode); + gp_camera_unref(d->camera); + d->camera = 0; + gp_abilities_list_free(abilList); + gp_port_info_list_free(infoList); + return false; + } + + if (d->model != "Directory Browse") + { + gp_port_info_list_get_info(infoList, portNum, &info); + errorCode = gp_camera_set_port_info(d->camera, info); + if (errorCode != GP_OK) + { + DDebug() << "Failed to set camera port!" << endl; + printGphotoErrorDescription(errorCode); + gp_camera_unref(d->camera); + d->camera = 0; + gp_abilities_list_free (abilList); + gp_port_info_list_free (infoList); + return false; + } + } + + gp_abilities_list_free (abilList); + gp_port_info_list_free (infoList); + + if (d->cameraAbilities.file_operations & + GP_FILE_OPERATION_PREVIEW) + d->thumbnailSupport = true; + + if (d->cameraAbilities.file_operations & + GP_FILE_OPERATION_DELETE) + d->deleteSupport = true; + + if (d->cameraAbilities.folder_operations & + GP_FOLDER_OPERATION_PUT_FILE) + d->uploadSupport = true; + + if (d->cameraAbilities.folder_operations & + GP_FOLDER_OPERATION_MAKE_DIR) + d->mkDirSupport = true; + + if (d->cameraAbilities.folder_operations & + GP_FOLDER_OPERATION_REMOVE_DIR) + d->delDirSupport = true; + + // -- Now try to initialize the camera ----------------- + + m_status = new GPStatus(); + + // Try and initialize the camera to see if its connected + errorCode = gp_camera_init(d->camera, m_status->context); + if (errorCode != GP_OK) + { + DDebug() << "Failed to initialize camera!" << endl; + printGphotoErrorDescription(errorCode); + gp_camera_unref(d->camera); + d->camera = 0; + delete m_status; + m_status = 0; + return false; + } + + delete m_status; + m_status = 0; + + d->cameraInitialized = true; + return true; +} + +void GPCamera::cancel() +{ + if (!m_status) + return; + m_status->cancel = true; +} + +void GPCamera::getAllFolders(const TQString& rootFolder, + TQStringList& folderList) +{ + TQStringList subfolders; + getSubFolders(rootFolder, subfolders); + + for (TQStringList::iterator it = subfolders.begin(); + it != subfolders.end(); ++it) + { + *it = rootFolder + TQString(rootFolder.endsWith("/") ? "" : "/") + (*it); + folderList.append(*it); + } + + for (TQStringList::iterator it = subfolders.begin(); + it != subfolders.end(); ++it) + { + getAllFolders(*it, folderList); + } +} + +bool GPCamera::getSubFolders(const TQString& folder, TQStringList& subFolderList) +{ + int errorCode; + CameraList *clist; + gp_list_new(&clist); + + if (m_status) + { + delete m_status; + m_status = 0; + } + m_status = new GPStatus(); + + errorCode = gp_camera_folder_list_folders(d->camera, TQFile::encodeName(folder), clist, m_status->context); + if (errorCode != GP_OK) + { + DDebug() << "Failed to get folders list from camera!" << endl; + printGphotoErrorDescription(errorCode); + gp_list_unref(clist); + delete m_status; + m_status = 0; + return false; + } + + delete m_status; + m_status = 0; + + int count = gp_list_count(clist); + for (int i = 0 ; i < count ; i++) + { + const char* subFolder; + errorCode = gp_list_get_name(clist, i, &subFolder); + if (errorCode != GP_OK) + { + DDebug() << "Failed to get folder name from camera!" << endl; + printGphotoErrorDescription(errorCode); + gp_list_unref(clist); + return false; + } + + subFolderList.append(TQFile::decodeName(subFolder)); + } + + gp_list_unref(clist); + return true; +} + +bool GPCamera::getItemsList(const TQString& folder, TQStringList& itemsList) +{ + int errorCode; + CameraList *clist; + const char *cname; + + if (m_status) + { + delete m_status; + m_status = 0; + } + m_status = new GPStatus; + + gp_list_new(&clist); + + errorCode = gp_camera_folder_list_files(d->camera, TQFile::encodeName(folder), clist, m_status->context); + if (errorCode != GP_OK) + { + DDebug() << "Failed to get folder files list from camera!" << endl; + printGphotoErrorDescription(errorCode); + gp_list_unref(clist); + delete m_status; + m_status = 0; + return false; + } + + int count = gp_list_count(clist); + for (int i = 0 ; i < count ; i++) + { + errorCode = gp_list_get_name(clist, i, &cname); + if (errorCode != GP_OK) + { + DDebug() << "Failed to get file name from camera!" << endl; + printGphotoErrorDescription(errorCode); + gp_list_unref(clist); + delete m_status; + m_status = 0; + return false; + } + + itemsList.append(TQFile::decodeName(cname)); + } + + gp_list_unref(clist); + + delete m_status; + m_status = 0; + + return true; +} + +bool GPCamera::getItemsInfoList(const TQString& folder, GPItemInfoList& items, bool /*getImageDimensions*/) +{ + int errorCode; + CameraList *clist; + const char *cname; + + if (m_status) + { + delete m_status; + m_status = 0; + } + m_status = new GPStatus; + + gp_list_new(&clist); + + errorCode = gp_camera_folder_list_files(d->camera, TQFile::encodeName(folder), clist, m_status->context); + if (errorCode != GP_OK) + { + DDebug() << "Failed to get folder files list from camera!" << endl; + printGphotoErrorDescription(errorCode); + gp_list_unref(clist); + delete m_status; + m_status = 0; + return false; + } + + int count = gp_list_count(clist); + for (int i = 0 ; i < count ; i++) + { + errorCode = gp_list_get_name(clist, i, &cname); + if (errorCode != GP_OK) + { + DDebug() << "Failed to get file name from camera!" << endl; + printGphotoErrorDescription(errorCode); + gp_list_unref(clist); + delete m_status; + m_status = 0; + return false; + } + + GPItemInfo itemInfo; + + itemInfo.name = TQFile::decodeName(cname); + itemInfo.folder = folder; + + CameraFileInfo info; + gp_camera_file_get_info(d->camera, TQFile::encodeName(folder), + cname, &info, m_status->context); + + itemInfo.mtime = -1; + itemInfo.mime = ""; + itemInfo.size = -1; + itemInfo.width = -1; + itemInfo.height = -1; + itemInfo.downloaded = GPItemInfo::DownloadUnknow; + itemInfo.readPermissions = -1; + itemInfo.writePermissions = -1; + + /* The mime type returned by Gphoto2 is dummy with all RAW files. + if (info.file.fields & GP_FILE_INFO_TYPE) + itemInfo.mime = info.file.type;*/ + + itemInfo.mime = mimeType(TQString(itemInfo.name.section('.', -1)).lower()); + + if (info.file.fields & GP_FILE_INFO_MTIME) + itemInfo.mtime = info.file.mtime; + + if (info.file.fields & GP_FILE_INFO_SIZE) + itemInfo.size = info.file.size; + + if (info.file.fields & GP_FILE_INFO_WIDTH) + itemInfo.width = info.file.width; + + if (info.file.fields & GP_FILE_INFO_HEIGHT) + itemInfo.height = info.file.height; + + if (info.file.fields & GP_FILE_INFO_STATUS) + { + if (info.file.status == GP_FILE_STATUS_DOWNLOADED) + itemInfo.downloaded = GPItemInfo::DownloadedYes; + } + + if (info.file.fields & GP_FILE_INFO_PERMISSIONS) + { + if (info.file.permissions & GP_FILE_PERM_READ) + itemInfo.readPermissions = 1; + else + itemInfo.readPermissions = 0; + if (info.file.permissions & GP_FILE_PERM_DELETE) + itemInfo.writePermissions = 1; + else + itemInfo.writePermissions = 0; + } + + items.append(itemInfo); + } + + gp_list_unref(clist); + + delete m_status; + m_status = 0; + + return true; +} + +bool GPCamera::getThumbnail(const TQString& folder, const TQString& itemName, TQImage& thumbnail) +{ + int errorCode; + CameraFile *cfile; + const char *data; + unsigned long int size; + + gp_file_new(&cfile); + + if (m_status) + { + delete m_status; + m_status = 0; + } + + m_status = new GPStatus; + + errorCode = gp_camera_file_get(d->camera, TQFile::encodeName(folder), + TQFile::encodeName(itemName), + GP_FILE_TYPE_PREVIEW, + cfile, m_status->context); + if (errorCode != GP_OK) + { + DDebug() << "Failed to get camera item!" << endl; + printGphotoErrorDescription(errorCode); + gp_file_unref(cfile); + delete m_status; + m_status = 0; + return false; + } + + delete m_status; + m_status = 0; + + errorCode = gp_file_get_data_and_size(cfile, &data, &size); + if (errorCode != GP_OK) + { + DDebug() << "Failed to get thumbnail from camera item!" << endl; + printGphotoErrorDescription(errorCode); + gp_file_unref(cfile); + return false; + } + + thumbnail.loadFromData((const uchar*) data, (uint) size); + + gp_file_unref(cfile); + return true; +} + +bool GPCamera::getExif(const TQString& folder, const TQString& itemName, + char **edata, int& esize) +{ + int errorCode; + CameraFile *cfile; + const char *data; + unsigned long int size; + + gp_file_new(&cfile); + + if (m_status) + { + delete m_status; + m_status = 0; + } + + m_status = new GPStatus; + + errorCode = gp_camera_file_get(d->camera, TQFile::encodeName(folder), + TQFile::encodeName(itemName), + GP_FILE_TYPE_EXIF, + cfile, m_status->context); + if (errorCode != GP_OK) + { + DDebug() << "Failed to get camera item!" << endl; + printGphotoErrorDescription(errorCode); + gp_file_unref(cfile); + delete m_status; + m_status = 0; + return false; + } + + delete m_status; + m_status = 0; + + errorCode = gp_file_get_data_and_size(cfile, &data, &size); + if (errorCode != GP_OK) + { + DDebug() << "Failed to get Exif data from camera item!" << endl; + printGphotoErrorDescription(errorCode); + gp_file_unref(cfile); + return false; + } + + *edata = new char[size]; + esize = size; + memcpy(*edata, data, size); + + gp_file_unref(cfile); + return true; +} + +bool GPCamera::downloadItem(const TQString& folder, const TQString& itemName, + const TQString& saveFile) +{ + int errorCode; + CameraFile *cfile; + + gp_file_new(&cfile); + + if (m_status) + { + delete m_status; + m_status = 0; + } + + m_status = new GPStatus; + + errorCode = gp_camera_file_get(d->camera, TQFile::encodeName(folder), + TQFile::encodeName(itemName), + GP_FILE_TYPE_NORMAL, cfile, + m_status->context); + if ( errorCode != GP_OK) + { + DDebug() << "Failed to get camera item!" << endl; + printGphotoErrorDescription(errorCode); + gp_file_unref(cfile); + delete m_status; + m_status = 0; + return false; + } + + delete m_status; + m_status = 0; + + errorCode = gp_file_save(cfile, TQFile::encodeName(saveFile)); + if (errorCode != GP_OK) + { + DDebug() << "Failed to save camera item!" << endl; + printGphotoErrorDescription(errorCode); + gp_file_unref(cfile); + return false; + } + + gp_file_unref(cfile); + return true; +} + +bool GPCamera::setLockItem(const TQString& folder, const TQString& itemName, bool lock) +{ + int errorCode; + if (m_status) + { + delete m_status; + m_status = 0; + } + + m_status = new GPStatus; + + CameraFileInfo info; + errorCode = gp_camera_file_get_info(d->camera, TQFile::encodeName(folder), + TQFile::encodeName(itemName), &info, m_status->context); + if (errorCode != GP_OK) + { + DDebug() << "Failed to get camera item properties!" << endl; + printGphotoErrorDescription(errorCode); + delete m_status; + m_status = 0; + return false; + } + + if (info.file.fields & GP_FILE_INFO_PERMISSIONS) + { + if (lock) + { + // Lock the file to set read only flag + info.file.permissions = (CameraFilePermissions)GP_FILE_PERM_READ; + } + else + { + // Unlock the file to set read/write flag + info.file.permissions = (CameraFilePermissions)(GP_FILE_PERM_READ | GP_FILE_PERM_DELETE); + } + } + + // Some gphoto2 drivers need to have only the right flag at on to process properties update in camera. + info.file.fields = GP_FILE_INFO_PERMISSIONS; + info.preview.fields = GP_FILE_INFO_NONE; + info.audio.fields = GP_FILE_INFO_NONE; + + errorCode = gp_camera_file_set_info(d->camera, TQFile::encodeName(folder), + TQFile::encodeName(itemName), info, m_status->context); + if (errorCode != GP_OK) + { + DDebug() << "Failed to set camera item lock properties!" << endl; + printGphotoErrorDescription(errorCode); + delete m_status; + m_status = 0; + return false; + } + + delete m_status; + m_status = 0; + return true; +} + +bool GPCamera::deleteItem(const TQString& folder, const TQString& itemName) +{ + int errorCode; + if (m_status) + { + delete m_status; + m_status = 0; + } + + m_status = new GPStatus; + + errorCode = gp_camera_file_delete(d->camera, TQFile::encodeName(folder), + TQFile::encodeName(itemName), + m_status->context); + if (errorCode != GP_OK) + { + DDebug() << "Failed to delete camera item!" << endl; + printGphotoErrorDescription(errorCode); + delete m_status; + m_status = 0; + return false; + } + + delete m_status; + m_status = 0; + + return true; +} + +// recursively delete all items +bool GPCamera::deleteAllItems(const TQString& folder) +{ + int errorCode; + TQStringList folderList; + + // Get all subfolders in this folder + getSubFolders(folder, folderList); + + if (folderList.count() > 0) + { + for (unsigned int i = 0 ; i < folderList.count() ; i++) + { + TQString subFolder(folder); + + if (!subFolder.endsWith("/")) + subFolder += '/'; + + subFolder += folderList[i]; + deleteAllItems(subFolder); + } + } + + if (m_status) + { + delete m_status; + m_status = 0; + } + + m_status = new GPStatus; + + errorCode = gp_camera_folder_delete_all(d->camera, TQFile::encodeName(folder), + m_status->context); + if (errorCode != GP_OK) + { + DDebug() << "Failed to delete camera folder!" << endl; + printGphotoErrorDescription(errorCode); + delete m_status; + m_status = 0; + return false; + } + + delete m_status; + m_status = 0; + + return true; +} + +bool GPCamera::uploadItem(const TQString& folder, const TQString& itemName, const TQString& localFile, + GPItemInfo& itemInfo, bool /*getImageDimensions*/) +{ + int errorCode; + CameraFile *cfile; + + errorCode = gp_file_new(&cfile); + if (errorCode != GP_OK) + { + DDebug() << "Failed to init new camera file instance!" << endl; + printGphotoErrorDescription(errorCode); + return false; + } + + errorCode = gp_file_open(cfile, TQFile::encodeName(localFile)); + if (errorCode != GP_OK) + { + DDebug() << "Failed to open file!" << endl; + printGphotoErrorDescription(errorCode); + gp_file_unref(cfile); + return false; + } + + errorCode = gp_file_set_name(cfile, TQFile::encodeName(itemName)); + if (errorCode != GP_OK) + { + DDebug() << "Failed to rename item from camera!" << endl; + printGphotoErrorDescription(errorCode); + gp_file_unref(cfile); + return false; + } + + if (m_status) + { + delete m_status; + m_status = 0; + } + + m_status = new GPStatus; + +#ifdef HAVE_GPHOTO25 + errorCode = gp_camera_folder_put_file(d->camera, + TQFile::encodeName(folder), + TQFile::encodeName(itemName), + GP_FILE_TYPE_NORMAL, + cfile, + m_status->context); +#else + errorCode = gp_camera_folder_put_file(d->camera, + TQFile::encodeName(folder), + cfile, + m_status->context); +#endif + if (errorCode != GP_OK) + { + DDebug() << "Failed to upload item to camera!" << endl; + printGphotoErrorDescription(errorCode); + gp_file_unref(cfile); + delete m_status; + m_status = 0; + return false; + } + + // Get new camera item information. + + itemInfo.name = itemName; + itemInfo.folder = folder; + + CameraFileInfo info; + errorCode = gp_camera_file_get_info(d->camera, TQFile::encodeName(folder), + TQFile::encodeName(itemName), &info, m_status->context); + if (errorCode != GP_OK) + { + DDebug() << "Failed to get camera item information!" << endl; + printGphotoErrorDescription(errorCode); + gp_file_unref(cfile); + delete m_status; + m_status = 0; + return false; + } + + itemInfo.mtime = -1; + itemInfo.mime = ""; + itemInfo.size = -1; + itemInfo.width = -1; + itemInfo.height = -1; + itemInfo.downloaded = GPItemInfo::DownloadUnknow; + itemInfo.readPermissions = -1; + itemInfo.writePermissions = -1; + + /* The mime type returned by Gphoto2 is dummy with all RAW files. + if (info.file.fields & GP_FILE_INFO_TYPE) + itemInfo.mime = info.file.type;*/ + + itemInfo.mime = mimeType(TQString(itemInfo.name.section('.', -1)).lower()); + + if (info.file.fields & GP_FILE_INFO_MTIME) + itemInfo.mtime = info.file.mtime; + + if (info.file.fields & GP_FILE_INFO_SIZE) + itemInfo.size = info.file.size; + + if (info.file.fields & GP_FILE_INFO_WIDTH) + itemInfo.width = info.file.width; + + if (info.file.fields & GP_FILE_INFO_HEIGHT) + itemInfo.height = info.file.height; + + if (info.file.fields & GP_FILE_INFO_STATUS) + { + if (info.file.status == GP_FILE_STATUS_DOWNLOADED) + itemInfo.downloaded = GPItemInfo::DownloadedYes; + else + itemInfo.downloaded = GPItemInfo::DownloadedNo; + } + + if (info.file.fields & GP_FILE_INFO_PERMISSIONS) + { + if (info.file.permissions & GP_FILE_PERM_READ) + itemInfo.readPermissions = 1; + else + itemInfo.readPermissions = 0; + if (info.file.permissions & GP_FILE_PERM_DELETE) + itemInfo.writePermissions = 1; + else + itemInfo.writePermissions = 0; + } + + gp_file_unref(cfile); + delete m_status; + m_status = 0; + return true; +} + +bool GPCamera::cameraSummary(TQString& summary) +{ + int errorCode; + CameraText sum; + + if (m_status) + { + delete m_status; + m_status = 0; + } + + m_status = new GPStatus; + + errorCode = gp_camera_get_summary(d->camera, &sum, m_status->context); + if (errorCode != GP_OK) + { + DDebug() << "Failed to get camera summary!" << endl; + printGphotoErrorDescription(errorCode); + delete m_status; + m_status = 0; + return false; + } + + summary = i18n("Title: %1\n" + "Model: %2\n" + "Port: %3\n" + "Path: %4\n\n" + "Thumbnails: %5\n" + "Delete items: %6\n" + "Upload items: %7\n" + "Create directories: %8\n" + "Delete directories: %9\n\n") + .arg(title()) + .arg(model()) + .arg(port()) + .arg(path()) + .arg(thumbnailSupport() ? i18n("yes") : i18n("no")) + .arg(deleteSupport() ? i18n("yes") : i18n("no")) + .arg(uploadSupport() ? i18n("yes") : i18n("no")) + .arg(mkDirSupport() ? i18n("yes") : i18n("no")) + .arg(delDirSupport() ? i18n("yes") : i18n("no")); + + summary.append(TQString(sum.text)); + + delete m_status; + m_status = 0; + return true; +} + +bool GPCamera::cameraManual(TQString& manual) +{ + int errorCode; + CameraText man; + + if (m_status) + { + delete m_status; + m_status = 0; + } + + m_status = new GPStatus; + + errorCode = gp_camera_get_manual(d->camera, &man, m_status->context); + if (errorCode != GP_OK) + { + DDebug() << "Failed to get camera manual!" << endl; + printGphotoErrorDescription(errorCode); + delete m_status; + m_status = 0; + return false; + } + + manual = TQString(man.text); + + delete m_status; + m_status = 0; + return true; +} + +bool GPCamera::cameraAbout(TQString& about) +{ + int errorCode; + CameraText abt; + + if (m_status) + { + delete m_status; + m_status = 0; + } + + m_status = new GPStatus; + + errorCode = gp_camera_get_about(d->camera, &abt, m_status->context); + if (errorCode != GP_OK) + { + DDebug() << "Failed to get information about camera!" << endl; + printGphotoErrorDescription(errorCode); + delete m_status; + m_status = 0; + return false; + } + + about = TQString(abt.text); + about.append(i18n("\n\nTo report problems about this driver, please contact " + "the gphoto2 team at:\n\nhttp://gphoto.org/bugs")); + + delete m_status; + m_status = 0; + return true; +} + +// -- Static methods --------------------------------------------------------------------- + +void GPCamera::printGphotoErrorDescription(int errorCode) +{ + DDebug() << "Libgphoto2 error: " << gp_result_as_string(errorCode) + << " (" << errorCode << ")" << endl; +} + +void GPCamera::getSupportedCameras(int& count, TQStringList& clist) +{ + clist.clear(); + count = 0; + + CameraAbilitiesList *abilList; + CameraAbilities abil; + GPContext *context; + + context = gp_context_new(); + + gp_abilities_list_new( &abilList ); + gp_abilities_list_load( abilList, context ); + + count = gp_abilities_list_count( abilList ); + if ( count < 0 ) + { + DDebug() << "Failed to get list of cameras!" << endl; + printGphotoErrorDescription(count); + gp_context_unref( context ); + return; + } + else + { + for (int i = 0 ; i < count ; i++) + { + gp_abilities_list_get_abilities( abilList, i, &abil ); + const char *cname = abil.model; + clist.append( TQString( cname ) ); + } + } + + gp_abilities_list_free( abilList ); + gp_context_unref( context ); +} + +void GPCamera::getSupportedPorts(TQStringList& plist) +{ + GPPortInfoList *list; + GPPortInfo info; + + plist.clear(); + + gp_port_info_list_new( &list ); + gp_port_info_list_load( list ); + + int numPorts = gp_port_info_list_count( list ); + if ( numPorts < 0) + { + DDebug() << "Failed to get list of port!" << endl; + printGphotoErrorDescription(numPorts); + gp_port_info_list_free( list ); + return; + } + else + { + for (int i = 0 ; i < numPorts ; i++) + { + gp_port_info_list_get_info( list, i, &info ); +#ifdef HAVE_GPHOTO25 + char *xpath; + gp_port_info_get_name( info, &xpath ); + plist.append( xpath ); +#else + plist.append( info.path ); +#endif + } + } + + gp_port_info_list_free( list ); +} + +void GPCamera::getCameraSupportedPorts(const TQString& model, TQStringList& plist) +{ + int i = 0; + plist.clear(); + + CameraAbilities abilities; + CameraAbilitiesList *abilList; + GPContext *context; + + context = gp_context_new(); + + gp_abilities_list_new (&abilList); + gp_abilities_list_load (abilList, context); + i = gp_abilities_list_lookup_model (abilList, model.local8Bit().data()); + gp_abilities_list_get_abilities (abilList, i, &abilities); + gp_abilities_list_free (abilList); + + if (abilities.port & GP_PORT_SERIAL) + plist.append("serial"); + + if (abilities.port & GP_PORT_USB) + plist.append("usb"); + + gp_context_unref( context ); +} + +int GPCamera::autoDetect(TQString& model, TQString& port) +{ + CameraList *camList; + CameraAbilitiesList *abilList; + GPPortInfoList *infoList; + const char *camModel_, *camPort_; + GPContext *context; + + context = gp_context_new(); + gp_list_new(&camList); + + gp_abilities_list_new(&abilList); + gp_abilities_list_load(abilList, context); + gp_port_info_list_new(&infoList); + gp_port_info_list_load(infoList); + gp_abilities_list_detect(abilList, infoList, camList, context); + gp_abilities_list_free(abilList); + gp_port_info_list_free(infoList); + + gp_context_unref(context); + + int count = gp_list_count(camList); + + if (count <= 0) + { + DDebug() << "Failed to autodetect camera!" << endl; + printGphotoErrorDescription(count); + gp_list_free(camList); + return -1; + } + + camModel_ = 0; + camPort_ = 0; + + for (int i = 0; i < count; i++) + { + if (gp_list_get_name(camList, i, &camModel_) != GP_OK) + { + DDebug() << "Failed to autodetect camera!" << endl; + gp_list_free(camList); + return -1; + } + + if (gp_list_get_value(camList, i, &camPort_) != GP_OK) + { + DDebug() << "Failed to autodetect camera!" << endl; + gp_list_free(camList); + return -1; + } + + if (camModel_ && camPort_) + { + model = TQString::fromLatin1(camModel_); + port = TQString::fromLatin1(camPort_); + gp_list_free(camList); + return 0; + } + } + + DDebug() << "Failed to autodetect camera!" << endl; + gp_list_free(camList); + return -1; +} + +} // namespace Digikam diff --git a/src/utilities/cameragui/gpcamera.h b/src/utilities/cameragui/gpcamera.h new file mode 100644 index 00000000..7071e972 --- /dev/null +++ b/src/utilities/cameragui/gpcamera.h @@ -0,0 +1,107 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-21 + * Description : Gphoto2 camera interface + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef GPCAMERA_H +#define GPCAMERA_H + +// Local includes. + +#include "dkcamera.h" + +class TQImage; + +namespace Digikam +{ + +class GPCameraPrivate; +class GPStatus; + +// Gphoto2 camera Implementation of abstract type DKCamera + +class GPCamera : public DKCamera +{ + +public: + + GPCamera(const TQString& title, const TQString& model, + const TQString& port, const TQString& path); + ~GPCamera(); + + bool thumbnailSupport(); + bool deleteSupport(); + bool uploadSupport(); + bool mkDirSupport(); + bool delDirSupport(); + + bool doConnect(); + + void cancel(); + + void getAllFolders(const TQString& folder, TQStringList& subFolderList); + bool getSubFolders(const TQString& folder, TQStringList& subFolderList); + bool getItemsList(const TQString& folder, TQStringList& itemsList); + bool getItemsInfoList(const TQString& folder, GPItemInfoList& items, bool getImageDimensions = true); + bool getThumbnail(const TQString& folder, const TQString& itemName, TQImage& thumbnail); + bool getExif(const TQString& folder, const TQString& itemName, char **edata, int& esize); + + bool setLockItem(const TQString& folder, const TQString& itemName, bool lock); + + bool downloadItem(const TQString& folder, const TQString& itemName, const TQString& saveFile); + bool deleteItem(const TQString& folder, const TQString& itemName); + + // recursively delete all items + bool deleteAllItems(const TQString& folder); + + bool uploadItem(const TQString& folder, const TQString& itemName, const TQString& localFile, + GPItemInfo& itemInfo, bool getImageDimensions=true); + + bool cameraSummary(TQString& summary); + bool cameraManual(TQString& manual); + bool cameraAbout(TQString& about); + + TQString model() const; + TQString port() const; + TQString path() const; + + // Public static methods shared with Camera Setup + + static int autoDetect(TQString& model, TQString& port); + static void getSupportedCameras(int& count, TQStringList& clist); + static void getSupportedPorts(TQStringList& plist); + static void getCameraSupportedPorts(const TQString& model, TQStringList& plist); + +private: + + int setup(); + static void printGphotoErrorDescription(int errorCode); + +private: + + GPCameraPrivate *d; + GPStatus *m_status; +}; + +} // namespace Digikam + +#endif /* GPCAMERA_H */ diff --git a/src/utilities/cameragui/gpiteminfo.cpp b/src/utilities/cameragui/gpiteminfo.cpp new file mode 100644 index 00000000..3a1d53e1 --- /dev/null +++ b/src/utilities/cameragui/gpiteminfo.cpp @@ -0,0 +1,68 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-19 + * Description : camera item info container + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqdatastream.h> + +// Local includes. + +#include "gpiteminfo.h" + +namespace Digikam +{ + +TQDataStream& operator<<( TQDataStream& ds, const GPItemInfo& info) +{ + ds << info.name; + ds << info.folder; + ds << info.mtime; + ds << info.mime; + ds << info.size; + ds << info.width; + ds << info.height; + ds << info.downloaded; + ds << info.readPermissions; + ds << info.writePermissions; + + return ds; +} + +TQDataStream& operator>>(TQDataStream& ds, GPItemInfo& info) +{ + ds >> info.name; + ds >> info.folder; + ds >> info.mtime; + ds >> info.mime; + ds >> info.size; + ds >> info.width; + ds >> info.height; + ds >> info.downloaded; + ds >> info.readPermissions; + ds >> info.writePermissions; + + return ds; +} + +} // namespace Digikam diff --git a/src/utilities/cameragui/gpiteminfo.h b/src/utilities/cameragui/gpiteminfo.h new file mode 100644 index 00000000..02b28693 --- /dev/null +++ b/src/utilities/cameragui/gpiteminfo.h @@ -0,0 +1,80 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-18 + * Description : camera item info container + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef GPITEMINFO_H +#define GPITEMINFO_H + +// C++ includes. + +#include <ctime> + +// TQt includes. + +#include <tqvaluelist.h> +#include <tqcstring.h> + +class TQDataStream; + +namespace Digikam +{ + +class GPItemInfo +{ + +public: + + enum DownloadStatus + { + DownloadUnknow = -1, + DownloadedNo = 0, + DownloadedYes = 1, + DownloadFailed = 2, + DownloadStarted = 3, + NewPicture = 4 + }; + +public: + + long size; + int width; + int height; + int downloaded; // See DownloadStatus enum. + int readPermissions; + int writePermissions; + + TQString name; + TQString folder; + TQString mime; + + time_t mtime; +}; + +TQDataStream& operator<<( TQDataStream &, const GPItemInfo & ); +TQDataStream& operator>>( TQDataStream &, GPItemInfo & ); + +typedef TQValueList<GPItemInfo> GPItemInfoList; + +} // namespace Digikam + +#endif /* GPITEMINFO_H */ diff --git a/src/utilities/cameragui/mtqueue.h b/src/utilities/cameragui/mtqueue.h new file mode 100644 index 00000000..0fb80e5c --- /dev/null +++ b/src/utilities/cameragui/mtqueue.h @@ -0,0 +1,116 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-30 + * Description : camera download multi-threading handler. + * + * Copyright (C) 2004 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + + * 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, 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. + * + * ============================================================ */ + +#ifndef COMMAND_QUEUE_H +#define COMMAND_QUEUE_H + +// TQt includes. + +#include <tqptrqueue.h> +#include <tqmutex.h> + +namespace Digikam +{ + +template<class Type> class MTQueue +{ + +public: + + MTQueue() + { + queue_.setAutoDelete(true); + } + + ~MTQueue() + { + flush(); + } + + bool isEmpty() + { + mutex_.lock(); + bool empty = queue_.isEmpty(); + mutex_.unlock(); + return empty; + } + + void flush() + { + mutex_.lock(); + queue_.clear(); + mutex_.unlock(); + } + + void enqueue(Type * t) + { + mutex_.lock(); + queue_.enqueue(t); + mutex_.unlock(); + } + + Type * dequeue() + { + mutex_.lock(); + Type * i = queue_.dequeue(); + mutex_.unlock(); + return i; + } + + Type * head(bool lock=true) + { + if (lock) + mutex_.lock(); + Type * i = queue_.head(); + if (lock) + mutex_.unlock(); + return i; + } + + int count() + { + mutex_.lock(); + int c = queue_.count(); + mutex_.unlock(); + return c; + } + + void lock() + { + mutex_.lock(); + } + + void unlock() + { + mutex_.unlock(); + } + +private: + + TQPtrQueue<Type> queue_; + TQMutex mutex_; +}; + +} // namespace Digikam + +#endif // COMMAND_QUEUE_H diff --git a/src/utilities/cameragui/renamecustomizer.cpp b/src/utilities/cameragui/renamecustomizer.cpp new file mode 100644 index 00000000..a906ccb1 --- /dev/null +++ b/src/utilities/cameragui/renamecustomizer.cpp @@ -0,0 +1,532 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-19 + * Description : a options group to set renaming files + * operations during camera downloading + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqdatetime.h> +#include <tqlayout.h> +#include <tqradiobutton.h> +#include <tqcheckbox.h> +#include <tqcombobox.h> +#include <tqhbox.h> +#include <tqlabel.h> +#include <tqpushbutton.h> +#include <tqtimer.h> +#include <tqwhatsthis.h> + +// KDE includes. + +#include <tdelocale.h> +#include <tdeconfig.h> +#include <tdeapplication.h> +#include <kiconloader.h> +#include <klineedit.h> +#include <knuminput.h> +#include <kdialogbase.h> +#if KDE_IS_VERSION(3,2,0) +#include <kinputdialog.h> +#else +#include <klineeditdlg.h> +#endif + +// Local includes. + +#include "renamecustomizer.h" +#include "renamecustomizer.moc" + +namespace Digikam +{ + +class RenameCustomizerPriv +{ +public: + + enum DateFormatOptions + { + DigikamStandard = 0, + IsoDateFormat, + TextDateFormat, + LocalDateFormat, + Advanced + }; + + RenameCustomizerPriv() + { + renameDefault = 0; + renameCustom = 0; + renameDefaultBox = 0; + renameCustomBox = 0; + renameDefaultCase = 0; + renameDefaultCaseType = 0; + addDateTimeBox = 0; + addCameraNameBox = 0; + addSeqNumberBox = 0; + changedTimer = 0; + renameCustomPrefix = 0; + renameCustomSuffix = 0; + startIndexLabel = 0; + startIndexInput = 0; + focusedWidget = 0; + dateTimeButton = 0; + dateTimeLabel = 0; + dateTimeFormat = 0; +} + + TQWidget *focusedWidget; + + TQString cameraTitle; + + TQRadioButton *renameDefault; + TQRadioButton *renameCustom; + + TQGroupBox *renameDefaultBox; + TQGroupBox *renameCustomBox; + + TQLabel *renameDefaultCase; + TQLabel *startIndexLabel; + TQLabel *dateTimeLabel; + + TQComboBox *renameDefaultCaseType; + TQComboBox *dateTimeFormat; + + TQCheckBox *addDateTimeBox; + TQCheckBox *addCameraNameBox; + TQCheckBox *addSeqNumberBox; + + TQPushButton *dateTimeButton; + TQString dateTimeFormatString; + + TQTimer *changedTimer; + + KLineEdit *renameCustomPrefix; + KLineEdit *renameCustomSuffix; + + KIntNumInput *startIndexInput; +}; + +RenameCustomizer::RenameCustomizer(TQWidget* parent, const TQString& cameraTitle) + : TQButtonGroup(parent) +{ + d = new RenameCustomizerPriv; + d->changedTimer = new TQTimer(this); + d->cameraTitle = cameraTitle; + + setFrameStyle( TQFrame::NoFrame ); + setRadioButtonExclusive(true); + setColumnLayout(0, TQt::Vertical); + TQGridLayout* mainLayout = new TQGridLayout(layout(), 4, 1); + + // ---------------------------------------------------------------- + + d->renameDefault = new TQRadioButton(i18n("Camera filenames"), this); + TQWhatsThis::add( d->renameDefault, i18n("<p>Turn on this option to use camera " + "provided image filenames without modifications.")); + mainLayout->addMultiCellWidget(d->renameDefault, 0, 0, 0, 1); + + d->renameDefaultBox = new TQGroupBox( this ); + d->renameDefaultBox->setFrameStyle(TQFrame::NoFrame|TQFrame::Plain); + d->renameDefaultBox->setInsideMargin(0); + d->renameDefaultBox->setColumnLayout(0, TQt::Vertical); + + d->renameDefaultCase = new TQLabel( i18n("Change case to:"), d->renameDefaultBox ); + d->renameDefaultCase->setSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Preferred ); + + d->renameDefaultCaseType = new TQComboBox( d->renameDefaultBox ); + d->renameDefaultCaseType->insertItem(i18n("Leave as Is"), 0); + d->renameDefaultCaseType->insertItem(i18n("Upper"), 1); + d->renameDefaultCaseType->insertItem(i18n("Lower"), 2); + d->renameDefaultCaseType->setSizePolicy(TQSizePolicy::Minimum, TQSizePolicy::Preferred); + TQWhatsThis::add( d->renameDefaultCaseType, i18n("<p>Set the method to use to change the case " + "of image filenames.")); + + TQHBoxLayout* boxLayout1 = new TQHBoxLayout( d->renameDefaultBox->layout() ); + boxLayout1->addSpacing( 10 ); + boxLayout1->addWidget( d->renameDefaultCase ); + boxLayout1->addWidget( d->renameDefaultCaseType ); + + mainLayout->addMultiCellWidget(d->renameDefaultBox, 1, 1, 0, 1); + + // ------------------------------------------------------------- + + d->renameCustom = new TQRadioButton(i18n("Customize"), this); + mainLayout->addMultiCellWidget(d->renameCustom, 2, 2, 0, 1); + TQWhatsThis::add( d->renameCustom, i18n("<p>Turn on this option to customize image filenames " + "during download.")); + + d->renameCustomBox = new TQGroupBox(this); + d->renameCustomBox->setFrameStyle(TQFrame::NoFrame|TQFrame::Plain); + d->renameCustomBox->setInsideMargin(0); + d->renameCustomBox->setColumnLayout(0, TQt::Vertical); + + TQGridLayout* renameCustomBoxLayout = new TQGridLayout(d->renameCustomBox->layout(), + 6, 2, KDialogBase::spacingHint()); + renameCustomBoxLayout->setColSpacing( 0, 10 ); + + TQLabel* prefixLabel = new TQLabel(i18n("Prefix:"), d->renameCustomBox); + renameCustomBoxLayout->addMultiCellWidget(prefixLabel, 0, 0, 1, 1); + d->renameCustomPrefix = new KLineEdit(d->renameCustomBox); + d->focusedWidget = d->renameCustomPrefix; + renameCustomBoxLayout->addMultiCellWidget(d->renameCustomPrefix, 0, 0, 2, 2); + TQWhatsThis::add( d->renameCustomPrefix, i18n("<p>Set the prefix which will be added to " + "image filenames.")); + + TQLabel* suffixLabel = new TQLabel(i18n("Suffix:"), d->renameCustomBox); + renameCustomBoxLayout->addMultiCellWidget(suffixLabel, 1, 1, 1, 1); + d->renameCustomSuffix = new KLineEdit(d->renameCustomBox); + renameCustomBoxLayout->addMultiCellWidget(d->renameCustomSuffix, 1, 1, 2, 2); + TQWhatsThis::add( d->renameCustomSuffix, i18n("<p>Set the suffix which will be added to " + "image filenames.")); + + d->addDateTimeBox = new TQCheckBox( i18n("Add Date && Time"), d->renameCustomBox ); + renameCustomBoxLayout->addMultiCellWidget(d->addDateTimeBox, 2, 2, 1, 2); + TQWhatsThis::add( d->addDateTimeBox, i18n("<p>Set this option to add the camera provided date and time.")); + + TQWidget *dateTimeWidget = new TQWidget(d->renameCustomBox); + d->dateTimeLabel = new TQLabel(i18n("Date format:"), dateTimeWidget); + d->dateTimeFormat = new TQComboBox(dateTimeWidget); + d->dateTimeFormat->insertItem(i18n("Standard"), RenameCustomizerPriv::DigikamStandard); + d->dateTimeFormat->insertItem(i18n("ISO"), RenameCustomizerPriv::IsoDateFormat); + d->dateTimeFormat->insertItem(i18n("Full Text"), RenameCustomizerPriv::TextDateFormat); + d->dateTimeFormat->insertItem(i18n("Local Settings"), RenameCustomizerPriv::LocalDateFormat); + d->dateTimeFormat->insertItem(i18n("Advanced..."), RenameCustomizerPriv::Advanced); + TQWhatsThis::add( d->dateTimeFormat, i18n("<p>Select your preferred date format for " + "creating new albums. The options available are:</p>" + "<p><b>Standard</b>: the date format that has been used as a standard by digiKam. " + "E.g.: <i>20060824T142618</i></p>" + "<p/><b>ISO</b>: the date format according to ISO 8601 " + "(YYYY-MM-DD). E.g.: <i>2006-08-24T14:26:18</i></p>" + "<p><b>Full Text</b>: the date format is a user-readable string. " + "E.g.: <i>Thu Aug 24 14:26:18 2006</i></p>" + "<p><b>Local Settings</b>: the date format depending on TDE control panel settings.</p>" + "<p><b>Advanced:</b> allows the user to specify a custom date format.</p>")); + d->dateTimeButton = new TQPushButton(SmallIcon("configure"), TQString(), dateTimeWidget); + TQSizePolicy policy = d->dateTimeButton->sizePolicy(); + policy.setHorData(TQSizePolicy::Maximum); + d->dateTimeButton->setSizePolicy(policy); + TQHBoxLayout *boxLayout2 = new TQHBoxLayout(dateTimeWidget); + boxLayout2->addWidget(d->dateTimeLabel); + boxLayout2->addWidget(d->dateTimeFormat); + boxLayout2->addWidget(d->dateTimeButton); + renameCustomBoxLayout->addMultiCellWidget(dateTimeWidget, 3, 3, 1, 2); + + d->addCameraNameBox = new TQCheckBox( i18n("Add Camera Name"), d->renameCustomBox ); + renameCustomBoxLayout->addMultiCellWidget(d->addCameraNameBox, 4, 4, 1, 2); + TQWhatsThis::add( d->addCameraNameBox, i18n("<p>Set this option to add the camera name.")); + + d->addSeqNumberBox = new TQCheckBox( i18n("Add Sequence Number"), d->renameCustomBox ); + renameCustomBoxLayout->addMultiCellWidget(d->addSeqNumberBox, 5, 5, 1, 2); + TQWhatsThis::add( d->addSeqNumberBox, i18n("<p>Set this option to add a sequence number " + "starting with the index set below.")); + + d->startIndexLabel = new TQLabel( i18n("Start Index:"), d->renameCustomBox ); + d->startIndexInput = new KIntNumInput(1, d->renameCustomBox); + d->startIndexInput->setRange(1, 900000, 1, false); + TQWhatsThis::add( d->startIndexInput, i18n("<p>Set the starting index value used to rename " + "files with a sequence number.")); + + renameCustomBoxLayout->addMultiCellWidget(d->startIndexLabel, 6, 6, 1, 1); + renameCustomBoxLayout->addMultiCellWidget(d->startIndexInput, 6, 6, 2, 2); + + mainLayout->addMultiCellWidget(d->renameCustomBox, 3, 3, 0, 1); + mainLayout->setRowStretch(4, 10); + + // -- setup connections ------------------------------------------------- + + connect(this, TQ_SIGNAL(clicked(int)), + this, TQ_SLOT(slotRadioButtonClicked(int))); + + connect(d->renameCustomPrefix, TQ_SIGNAL(textChanged(const TQString&)), + this, TQ_SLOT(slotRenameOptionsChanged())); + + connect(d->renameCustomSuffix, TQ_SIGNAL(textChanged(const TQString&)), + this, TQ_SLOT(slotRenameOptionsChanged())); + + connect(d->addDateTimeBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotRenameOptionsChanged())); + + connect(d->addCameraNameBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotRenameOptionsChanged())); + + connect(d->addSeqNumberBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotRenameOptionsChanged())); + + connect(d->renameDefaultCaseType, TQ_SIGNAL(activated(const TQString&)), + this, TQ_SLOT(slotRenameOptionsChanged())); + + connect(d->startIndexInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotRenameOptionsChanged())); + + connect(d->changedTimer, TQ_SIGNAL(timeout()), + this, TQ_SIGNAL(signalChanged())); + + connect(d->dateTimeButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotDateTimeButtonClicked())); + + connect(d->dateTimeFormat, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotDateTimeFormatChanged(int))); + + connect(d->addDateTimeBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotDateTimeBoxToggled(bool))); + + // -- initial values --------------------------------------------------- + + readSettings(); + + // signal to this not yet connected when readSettings is called? Don't know + slotDateTimeBoxToggled(d->addDateTimeBox->isChecked()); +} + +RenameCustomizer::~RenameCustomizer() +{ + delete d->changedTimer; + saveSettings(); + delete d; +} + +bool RenameCustomizer::useDefault() const +{ + return d->renameDefault->isChecked(); +} + +int RenameCustomizer::startIndex() const +{ + return d->startIndexInput->value(); +} + +TQString RenameCustomizer::newName(const TQDateTime &dateTime, int index, const TQString &extension) const +{ + if (d->renameDefault->isChecked()) + return TQString(); + else + { + TQString name(d->renameCustomPrefix->text()); + + // use the "T" as a delimiter between date and time + TQString date; + switch (d->dateTimeFormat->currentItem()) + { + case RenameCustomizerPriv::DigikamStandard: + date = dateTime.toString("yyyyMMddThhmmss"); + break; + case RenameCustomizerPriv::TextDateFormat: + date = dateTime.toString(TQt::TextDate); + break; + case RenameCustomizerPriv::LocalDateFormat: + date = dateTime.toString(TQt::LocalDate); + break; + case RenameCustomizerPriv::IsoDateFormat: + date = dateTime.toString(TQt::ISODate); + break; + case RenameCustomizerPriv::Advanced: + date = dateTime.toString(d->dateTimeFormatString); + break; + } + + // it seems that TQString::number does not support padding with zeros + TQString seq; + seq.sprintf("-%06d", index); + + if (d->addDateTimeBox->isChecked()) + name += date; + + if (d->addSeqNumberBox->isChecked()) + name += seq; + + if (d->addCameraNameBox->isChecked()) + name += TQString("-%1").arg(d->cameraTitle.simplifyWhiteSpace().replace(" ", "")); + + name += d->renameCustomSuffix->text(); + name += extension; + + return name; + } +} + +RenameCustomizer::Case RenameCustomizer::changeCase() const +{ + RenameCustomizer::Case type = NONE; + + if (d->renameDefaultCaseType->currentItem() == 1) + type=UPPER; + if (d->renameDefaultCaseType->currentItem() == 2) + type=LOWER; + + return type; +} + +void RenameCustomizer::slotRadioButtonClicked(int) +{ + TQRadioButton* btn = dynamic_cast<TQRadioButton*>(selected()); + if (!btn) + return; + + d->renameCustomBox->setEnabled( btn != d->renameDefault ); + d->renameDefaultBox->setEnabled( btn == d->renameDefault ); + slotRenameOptionsChanged(); +} + +void RenameCustomizer::slotRenameOptionsChanged() +{ + d->focusedWidget = focusWidget(); + + if (d->addSeqNumberBox->isChecked()) + { + d->startIndexInput->setEnabled(true); + d->startIndexLabel->setEnabled(true); + } + else + { + d->startIndexInput->setEnabled(false); + d->startIndexLabel->setEnabled(false); + } + + d->changedTimer->start(500, true); +} + +void RenameCustomizer::slotDateTimeBoxToggled(bool on) +{ + d->dateTimeLabel->setEnabled(on); + d->dateTimeFormat->setEnabled(on); + d->dateTimeButton->setEnabled(on + && d->dateTimeFormat->currentItem() == RenameCustomizerPriv::Advanced); + slotRenameOptionsChanged(); +} + +void RenameCustomizer::slotDateTimeFormatChanged(int index) +{ + if (index == RenameCustomizerPriv::Advanced) + { + d->dateTimeButton->setEnabled(true); + //d->dateTimeButton->show(); + //slotDateTimeButtonClicked(); + } + else + { + d->dateTimeButton->setEnabled(false); + //d->dateTimeButton->hide(); + } + slotRenameOptionsChanged(); +} + +void RenameCustomizer::slotDateTimeButtonClicked() +{ + bool ok; + TQString message = i18n("<qt><p>Enter the format for date and time.</p>" + "<p>Use <i>dd</i> for the day, " + "<i>MM</i> for the month, " + "<i>yyyy</i> for the year, " + "<i>hh</i> for the hour, " + "<i>mm</i> for the minute, " + "<i>ss</i> for the second.</p>" + "<p>Examples: <i>yyyyMMddThhmmss</i> " + "for 20060824T142418,<br>" + "<i>yyyy-MM-dd hh:mm:ss</i> " + "for 2006-08-24 14:24:18.</p></qt>"); + +#if KDE_IS_VERSION(3,2,0) + TQString newFormat = KInputDialog::getText(i18n("Change Date and Time Format"), + message, + d->dateTimeFormatString, &ok, this); +#else + TQString newFormat = KLineEditDlg::getText(i18n("Change Date and Time Format"), + message, + d->dateTimeFormatString, &ok, this); +#endif + + if (!ok) + return; + + d->dateTimeFormatString = newFormat; + slotRenameOptionsChanged(); +} + +void RenameCustomizer::readSettings() +{ + TDEConfig* config = kapp->config(); + + config->setGroup("Camera Settings"); + bool def = config->readBoolEntry("Rename Use Default", true); + bool addSeqNumb = config->readBoolEntry("Add Sequence Number", true); + bool adddateTime = config->readBoolEntry("Add Date Time", false); + bool addCamName = config->readBoolEntry("Add Camera Name", false); + int chcaseT = config->readNumEntry("Case Type", NONE); + TQString prefix = config->readEntry("Rename Prefix", i18n("photo")); + TQString suffix = config->readEntry("Rename Postfix", TQString()); + int startIndex = config->readNumEntry("Rename Start Index", 1); + int dateTime = config->readNumEntry("Date Time Format", RenameCustomizerPriv::IsoDateFormat); + TQString format = config->readEntry("Date Time Format String", "yyyyMMddThhmmss"); + + if (def) + { + d->renameDefault->setChecked(true); + d->renameCustom->setChecked(false); + d->renameCustomBox->setEnabled(false); + d->renameDefaultBox->setEnabled(true); + } + else + { + d->renameDefault->setChecked(false); + d->renameCustom->setChecked(true); + d->renameCustomBox->setEnabled(true); + d->renameDefaultBox->setEnabled(false); + } + + d->addDateTimeBox->setChecked(adddateTime); + d->addCameraNameBox->setChecked(addCamName); + d->addSeqNumberBox->setChecked(addSeqNumb); + d->renameDefaultCaseType->setCurrentItem(chcaseT); + d->renameCustomPrefix->setText(prefix); + d->renameCustomSuffix->setText(suffix); + d->startIndexInput->setValue(startIndex); + d->dateTimeFormat->setCurrentItem(dateTime); + d->dateTimeFormatString = format; + slotRenameOptionsChanged(); +} + +void RenameCustomizer::saveSettings() +{ + TDEConfig* config = kapp->config(); + + config->setGroup("Camera Settings"); + config->writeEntry("Rename Use Default", d->renameDefault->isChecked()); + config->writeEntry("Add Camera Name", d->addCameraNameBox->isChecked()); + config->writeEntry("Add Date Time", d->addDateTimeBox->isChecked()); + config->writeEntry("Add Sequence Number", d->addSeqNumberBox->isChecked()); + config->writeEntry("Case Type", d->renameDefaultCaseType->currentItem()); + config->writeEntry("Rename Prefix", d->renameCustomPrefix->text()); + config->writeEntry("Rename Suffix", d->renameCustomSuffix->text()); + config->writeEntry("Rename Start Index", d->startIndexInput->value()); + config->writeEntry("Date Time Format", d->dateTimeFormat->currentItem()); + config->writeEntry("Date Time Format String", d->dateTimeFormatString); + config->sync(); +} + +void RenameCustomizer::restoreFocus() +{ + d->focusedWidget->setFocus(); +} + +} // namespace Digikam + diff --git a/src/utilities/cameragui/renamecustomizer.h b/src/utilities/cameragui/renamecustomizer.h new file mode 100644 index 00000000..47ad925b --- /dev/null +++ b/src/utilities/cameragui/renamecustomizer.h @@ -0,0 +1,91 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-19 + * Description : a options group to set renaming files + * operations during camera downloading + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef RENAMECUSTOMIZER_H +#define RENAMECUSTOMIZER_H + +// TQt includes. + +#include <tqbuttongroup.h> + +class TQDateTime; + +namespace Digikam +{ + +class RenameCustomizerPriv; + +class RenameCustomizer : public TQButtonGroup +{ + TQ_OBJECT + + +public: + + enum Case + { + NONE = 0, + UPPER, + LOWER + }; + + RenameCustomizer(TQWidget* parent, const TQString& cameraTitle); + ~RenameCustomizer(); + + void setUseDefault(bool val); + bool useDefault() const; + TQString newName(const TQDateTime &date, int index, const TQString &extension) const; + Case changeCase() const; + int startIndex() const; + +signals: + + void signalChanged(); + +public slots: + + void restoreFocus(); + +private: + + void readSettings(); + void saveSettings(); + +private slots: + + void slotRadioButtonClicked(int); + void slotRenameOptionsChanged(); + void slotDateTimeBoxToggled(bool); + void slotDateTimeFormatChanged(int); + void slotDateTimeButtonClicked(); + +private: + + RenameCustomizerPriv *d; +}; + +} // namespace Digikam + +#endif /* RENAMECUSTOMIZER_H */ diff --git a/src/utilities/cameragui/umscamera.cpp b/src/utilities/cameragui/umscamera.cpp new file mode 100644 index 00000000..537c29ba --- /dev/null +++ b/src/utilities/cameragui/umscamera.cpp @@ -0,0 +1,519 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-21 + * Description : USB Mass Storage camera interface + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <utime.h> +} + +// TQt includes. + +#include <tqdir.h> +#include <tqfileinfo.h> +#include <tqfile.h> +#include <tqstringlist.h> +#include <tqdeepcopy.h> +#include <tqwmatrix.h> + +// KDE includes. + +#include <tdelocale.h> +#include <tdefilemetainfo.h> + +// LibKDcraw includes. + +#include <libkdcraw/kdcraw.h> + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "dmetadata.h" +#include "umscamera.h" + +namespace Digikam +{ + +UMSCamera::UMSCamera(const TQString& title, const TQString& model, const TQString& port, const TQString& path) + : DKCamera(title, model, port, path) +{ + m_cancel = false; +} + +UMSCamera::~UMSCamera() +{ +} + +bool UMSCamera::doConnect() +{ + return true; +} + +void UMSCamera::cancel() +{ + // set the cancel flag + m_cancel = true; +} + +void UMSCamera::getAllFolders(const TQString& folder, TQStringList& subFolderList) +{ + m_cancel = false; + subFolderList.clear(); + subFolderList.append(folder); + listFolders(folder, subFolderList); +} + +bool UMSCamera::getItemsInfoList(const TQString& folder, GPItemInfoList& infoList, bool getImageDimensions) +{ + m_cancel = false; + infoList.clear(); + + TQDir dir(folder); + dir.setFilter(TQDir::Files); + + const TQFileInfoList *list = dir.entryInfoList(); + if (!list) + return false; + + TQFileInfoListIterator it(*list); + TQFileInfo *fi; + TQFileInfo thmlo, thmup; + DMetadata meta; + + while ((fi = it.current()) != 0 && !m_cancel) + { + ++it; + + TQString mime = mimeType(fi->extension(false).lower()); + + if (!mime.isEmpty()) + { + TQSize dims; + TQDateTime dt; + GPItemInfo info; + thmlo.setFile(folder + TQString("/") + fi->baseName() + TQString(".thm")); + thmup.setFile(folder + TQString("/") + fi->baseName() + TQString(".THM")); + + if (thmlo.exists()) + { + // Try thumbnail sidecar files with lowercase extension. + meta.load(thmlo.filePath()); + dt = meta.getImageDateTime(); + dims = meta.getImageDimensions(); + } + else if (thmup.exists()) + { + // Try thumbnail sidecar files with uppercase extension. + meta.load(thmup.filePath()); + dt = meta.getImageDateTime(); + dims = meta.getImageDimensions(); + } + else if (mime == TQString("image/x-raw")) + { + // If no thumbnail sidecar file available , try to load image metadata with Raw files. + meta.load(fi->filePath()); + dt = meta.getImageDateTime(); + dims = meta.getImageDimensions(); + } + else + { + meta.load(fi->filePath()); + dt = meta.getImageDateTime(); + dims = meta.getImageDimensions(); + + if (dims.isNull()) + { + // In all others case, try KFileMetaInfo. + KFileMetaInfo kmeta(fi->filePath()); + if (kmeta.isValid()) + { + if (kmeta.containsGroup("Jpeg EXIF Data")) + dims = kmeta.group("Jpeg EXIF Data").item("Dimensions").value().toSize(); + else if (kmeta.containsGroup("General")) + dims = kmeta.group("General").item("Dimensions").value().toSize(); + else if (kmeta.containsGroup("Technical")) + dims = kmeta.group("Technical").item("Dimensions").value().toSize(); + } + } + } + + if (dt.isNull()) + { + // If date is not found in metadata, use file time stamp + dt = fi->created(); + } + + info.name = fi->fileName(); + info.folder = !folder.endsWith("/") ? folder + TQString("/") : folder; + info.mime = mime; + info.mtime = dt.toTime_t(); + info.size = fi->size(); + info.width = getImageDimensions ? dims.width() : -1; + info.height = getImageDimensions ? dims.height() : -1; + info.downloaded = GPItemInfo::DownloadUnknow; + info.readPermissions = fi->isReadable(); + info.writePermissions = fi->isWritable(); + + infoList.append(info); + } + } + + return true; +} + +bool UMSCamera::getThumbnail(const TQString& folder, const TQString& itemName, TQImage& thumbnail) +{ + m_cancel = false; + + // JPEG files: try to get thumbnail from Exif data. + + DMetadata metadata(TQFile::encodeName(folder + TQString("/") + itemName)); + thumbnail = metadata.getExifThumbnail(true); + if (!thumbnail.isNull()) + return true; + + // RAW files : try to extract embedded thumbnail using dcraw + + KDcrawIface::KDcraw::loadDcrawPreview(thumbnail, TQString(folder + TQString("/") + itemName)); + if (!thumbnail.isNull()) + return true; + + // THM files: try to get thumbnail from '.thm' files if we didn't manage to get + // thumbnail from Exif. Any cameras provides *.thm files like JPEG files with RAW/video files. + // Using this way speed up thumbnailization and limit data transfered between camera and computer. + // NOTE: the thumbnail extracted with this method can provide a poor quality preview. + + TQFileInfo fi(folder + TQString("/") + itemName); + + if (thumbnail.load(folder + TQString("/") + fi.baseName() + TQString(".thm"))) // Lowercase + { + if (!thumbnail.isNull()) + return true; + } + else if (thumbnail.load(folder + TQString("/") + fi.baseName() + TQString(".THM"))) // Uppercase + { + if (!thumbnail.isNull()) + return true; + } + + // Finaly, we trying to get thumbnail using DImg API (slow). + + DImg dimgThumb(TQCString(TQFile::encodeName(folder + TQString("/") + itemName))); + + if (!dimgThumb.isNull()) + { + thumbnail = dimgThumb.copyTQImage(); + return true; + } + + return false; +} + +bool UMSCamera::getExif(const TQString&, const TQString&, char **, int&) +{ + // not necessary to implement this. read it directly from the file + // (done in camera controller) + DWarning() << "exif implemented yet in camera controller" << endl; + return false; +} + +bool UMSCamera::downloadItem(const TQString& folder, const TQString& itemName, const TQString& saveFile) +{ + m_cancel = false; + TQString src = folder + TQString("/") + itemName; + TQString dest = saveFile; + + TQFile sFile(src); + TQFile dFile(dest); + + if ( !sFile.open(IO_ReadOnly) ) + { + DWarning() << "Failed to open source file for reading: " + << src << endl; + return false; + } + + if ( !dFile.open(IO_WriteOnly) ) + { + sFile.close(); + DWarning() << "Failed to open dest file for writing: " + << dest << endl; + return false; + } + + const int MAX_IPC_SIZE = (1024*32); + char buffer[MAX_IPC_SIZE]; + + TQ_LONG len; + while ((len = sFile.readBlock(buffer, MAX_IPC_SIZE)) != 0 && !m_cancel) + { + if (len == -1 || dFile.writeBlock(buffer, (TQ_ULONG)len) != len) + { + sFile.close(); + dFile.close(); + return false; + } + } + + sFile.close(); + dFile.close(); + + // set the file modification time of the downloaded file to that + // of the original file + struct stat st; + ::stat(TQFile::encodeName(src), &st); + + struct utimbuf ut; + ut.modtime = st.st_mtime; + ut.actime = st.st_atime; + + ::utime(TQFile::encodeName(dest), &ut); + + return true; +} + +bool UMSCamera::setLockItem(const TQString& folder, const TQString& itemName, bool lock) +{ + TQString src = folder + TQString("/") + itemName; + + if (lock) + { + // Lock the file to set read only flag + if (::chmod(TQFile::encodeName(src), S_IREAD) == -1) + return false; + } + else + { + // Unlock the file to set read/write flag + if (::chmod(TQFile::encodeName(src), S_IREAD | S_IWRITE) == -1) + return false; + } + + return true; +} + +bool UMSCamera::deleteItem(const TQString& folder, const TQString& itemName) +{ + m_cancel = false; + + // Any camera provide THM (thumbnail) file with real image. We need to remove it also. + + TQFileInfo fi(folder + TQString("/") + itemName); + + TQFileInfo thmLo(folder + TQString("/") + fi.baseName() + ".thm"); // Lowercase + + if (thmLo.exists()) + ::unlink(TQFile::encodeName(thmLo.filePath())); + + TQFileInfo thmUp(folder + TQString("/") + fi.baseName() + ".THM"); // Uppercase + + if (thmUp.exists()) + ::unlink(TQFile::encodeName(thmUp.filePath())); + + // Remove the real image. + return (::unlink(TQFile::encodeName(folder + TQString("/") + itemName)) == 0); +} + +bool UMSCamera::uploadItem(const TQString& folder, const TQString& itemName, const TQString& localFile, + GPItemInfo& info, bool getImageDimensions) +{ + m_cancel = false; + TQString dest = folder + TQString("/") + itemName; + TQString src = localFile; + + TQFile sFile(src); + TQFile dFile(dest); + + if ( !sFile.open(IO_ReadOnly) ) + { + DWarning() << "Failed to open source file for reading: " + << src << endl; + return false; + } + + if ( !dFile.open(IO_WriteOnly) ) + { + sFile.close(); + DWarning() << "Failed to open dest file for writing: " + << dest << endl; + return false; + } + + const int MAX_IPC_SIZE = (1024*32); + char buffer[MAX_IPC_SIZE]; + + TQ_LONG len; + while ((len = sFile.readBlock(buffer, MAX_IPC_SIZE)) != 0 && !m_cancel) + { + if (len == -1 || dFile.writeBlock(buffer, (TQ_ULONG)len) == -1) + { + sFile.close(); + dFile.close(); + return false; + } + } + + sFile.close(); + dFile.close(); + + // set the file modification time of the uploaded file to that + // of the original file + struct stat st; + ::stat(TQFile::encodeName(src), &st); + + struct utimbuf ut; + ut.modtime = st.st_mtime; + ut.actime = st.st_atime; + + ::utime(TQFile::encodeName(dest), &ut); + + // Get new camera item information. + + DMetadata meta; + TQFileInfo fi(dest); + TQString mime = mimeType(fi.extension(false).lower()); + + if (!mime.isEmpty()) + { + TQSize dims; + TQDateTime dt; + + if (mime == TQString("image/x-raw")) + { + // Try to load image metadata with Raw files. + meta.load(fi.filePath()); + dt = meta.getImageDateTime(); + dims = meta.getImageDimensions(); + } + else + { + meta.load(fi.filePath()); + dt = meta.getImageDateTime(); + dims = meta.getImageDimensions(); + + if (dims.isNull()) + { + // In all others case, try KFileMetaInfo. + KFileMetaInfo kmeta(fi.filePath()); + if (kmeta.isValid()) + { + if (kmeta.containsGroup("Jpeg EXIF Data")) + dims = kmeta.group("Jpeg EXIF Data").item("Dimensions").value().toSize(); + else if (kmeta.containsGroup("General")) + dims = kmeta.group("General").item("Dimensions").value().toSize(); + else if (kmeta.containsGroup("Technical")) + dims = kmeta.group("Technical").item("Dimensions").value().toSize(); + } + } + } + + if (dt.isNull()) + { + // If date is not found in metadata, use file time stamp + dt = fi.created(); + } + + info.name = fi.fileName(); + info.folder = !folder.endsWith("/") ? folder + TQString("/") : folder; + info.mime = mime; + info.mtime = dt.toTime_t(); + info.size = fi.size(); + info.width = getImageDimensions ? dims.width() : -1; + info.height = getImageDimensions ? dims.height() : -1; + info.downloaded = GPItemInfo::DownloadUnknow; + info.readPermissions = fi.isReadable(); + info.writePermissions = fi.isWritable(); + } + + return true; +} + +void UMSCamera::listFolders(const TQString& folder, TQStringList& subFolderList) +{ + if (m_cancel) + return; + + TQDir dir(folder); + dir.setFilter(TQDir::Dirs|TQDir::Executable); + + const TQFileInfoList *list = dir.entryInfoList(); + if (!list) + return; + + TQFileInfoListIterator it( *list ); + TQFileInfo *fi; + + while ((fi = it.current()) != 0 && !m_cancel) + { + ++it; + + if (fi->fileName() == "." || fi->fileName() == "..") + continue; + + TQString subfolder = folder + TQString(folder.endsWith("/") ? "" : "/") + fi->fileName(); + subFolderList.append(subfolder); + listFolders(subfolder, subFolderList); + } +} + +bool UMSCamera::cameraSummary(TQString& summary) +{ + summary = TQString(i18n("<b>Mounted Camera</b> driver for USB/IEEE1394 mass storage cameras and " + "Flash disk card readers.<br><br>")); + + summary.append(i18n("Title: %1<br>" + "Model: %2<br>" + "Port: %3<br>" + "Path: %4<br>") + .arg(title()) + .arg(model()) + .arg(port()) + .arg(path())); + return true; +} + +bool UMSCamera::cameraManual(TQString& manual) +{ + manual = TQString(i18n("For more information about the <b>Mounted Camera</b> driver, " + "please read <b>Supported Digital Still " + "Cameras</b> section in the digiKam manual.")); + return true; +} + +bool UMSCamera::cameraAbout(TQString& about) +{ + about = TQString(i18n("The <b>Mounted Camera</b> driver is a simple interface to a camera disk " + "mounted locally on your system.<br><br>" + "It doesn't use libgphoto2 drivers.<br><br>" + "To report any problems with this driver, please contact the digiKam team at:<br><br>" + "http://www.digikam.org/?q=contact")); + return true; +} + +} // namespace Digikam diff --git a/src/utilities/cameragui/umscamera.h b/src/utilities/cameragui/umscamera.h new file mode 100644 index 00000000..697ef615 --- /dev/null +++ b/src/utilities/cameragui/umscamera.h @@ -0,0 +1,78 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-21 + * Description : USB Mass Storage camera interface + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef UMSCAMERA_H +#define UMSCAMERA_H + +// TQt includes. + +#include <tqstringlist.h> + +// Local includes. + +#include "dkcamera.h" + +namespace Digikam +{ + +class UMSCameraPriv; + +class UMSCamera : public DKCamera +{ +public: + + UMSCamera(const TQString& title, const TQString& model, const TQString& port, const TQString& path); + ~UMSCamera(); + + bool doConnect(); + void cancel(); + + void getAllFolders(const TQString& folder, TQStringList& subFolderList); + bool getItemsInfoList(const TQString& folder, GPItemInfoList& infoList, bool getImageDimensions=true); + bool getThumbnail(const TQString& folder, const TQString& itemName, TQImage& thumbnail); + bool getExif(const TQString& folder, const TQString& itemName, char **edata, int& esize); + + bool setLockItem(const TQString& folder, const TQString& itemName, bool lock); + + bool downloadItem(const TQString& folder, const TQString& itemName, const TQString& saveFile); + bool deleteItem(const TQString& folder, const TQString& itemName); + bool uploadItem(const TQString& folder, const TQString& itemName, const TQString& localFile, + GPItemInfo& info, bool getImageDimensions=true); + + bool cameraSummary(TQString& summary); + bool cameraManual(TQString& manual); + bool cameraAbout(TQString& about); + +private: + + void listFolders(const TQString& folder, TQStringList& subFolderList); + +private: + + bool m_cancel; +}; + +} // namespace Digikam + +#endif /* UMSCAMERA_H */ diff --git a/src/utilities/hotplug/Makefile.am b/src/utilities/hotplug/Makefile.am new file mode 100644 index 00000000..b0b9e23a --- /dev/null +++ b/src/utilities/hotplug/Makefile.am @@ -0,0 +1,7 @@ +konqservicemenudir = $(kde_datadir)/konqueror/servicemenus +konqservicemenu_DATA = digikam-download.desktop digikam-gphoto2-camera.desktop digikam-mount-and-download.desktop + +helperdir = $(digikamhelper_dir) +helper_SCRIPTS = digikam-camera + +#EXTRA_DIST = $(servicemenu_DATA) $(helper_SCRIPTS) diff --git a/src/utilities/hotplug/configure.in.in b/src/utilities/hotplug/configure.in.in new file mode 100644 index 00000000..b36be1e8 --- /dev/null +++ b/src/utilities/hotplug/configure.in.in @@ -0,0 +1,7 @@ +KDE_EXPAND_MAKEVAR(digikamhelper_dir, kde_datadir/digikam/utils) +AC_SUBST(digikamhelper_dir) + +AC_OUTPUT(src/utilities/hotplug/digikam-download.desktop) +AC_OUTPUT(src/utilities/hotplug/digikam-gphoto2-camera.desktop) +AC_OUTPUT(src/utilities/hotplug/digikam-mount-and-download.desktop) + diff --git a/src/utilities/hotplug/digikam-camera b/src/utilities/hotplug/digikam-camera new file mode 100755 index 00000000..10b2fe3f --- /dev/null +++ b/src/utilities/hotplug/digikam-camera @@ -0,0 +1,40 @@ +#!/bin/sh + +action="$1"; shift; + +case "$action" in +detect) + cmdoption=--detect-camera + dcopcall=detectCamera + ;; +storage) + cmdoption=--download-from + dcopcall=downloadFrom + args="$@" + ;; +*) + echo "${0##*/}: wrong action. Usage" + echo " ${0##*/} detect # for gphoto2 supported cameras" + echo " ${0##*/} storage <url> # for usbdisk or directries with images" + exit 1 + ;; +esac + +for app in `dcop`; do + case "$app" in + digikam-*) + echo "recycling running $app: $dcopcall $@" + if test -z "$args"; then + exec dcop "$app" camera "$dcopcall" + else + exec dcop "$app" camera "$dcopcall" "$args" + fi + ;; + esac +done; +echo "starting digikam with $cmdoption $args" +if test -z "$args"; then + exec digikam "$cmdoption" +else + exec digikam "$cmdoption" "$args" +fi diff --git a/src/utilities/hotplug/digikam-download.desktop.in b/src/utilities/hotplug/digikam-download.desktop.in new file mode 100644 index 00000000..55a29fdb --- /dev/null +++ b/src/utilities/hotplug/digikam-download.desktop.in @@ -0,0 +1,27 @@ +[Desktop Action digiKam Download] +Exec=@digikamhelper_dir@/digikam-camera storage %u +Icon=digikam +Name=Download Photos with digiKam +Name[ca]=Descàrrega de fotos amb el digiKam +Name[de]=Fotos mit digiKam herunterladen +Name[es]=Descargar fotos con digiKam +Name[et]=Fotode allalaadimine digiKamiga +Name[fi]=Lataa valokuvat digiKamilla +Name[fr]=Télécharger les photos avec digiKam +Name[is]=Hala niður myndum með digiKam +Name[it]=Scarica foto con digiKam +Name[ja]=digiKam で写真をダウンロード +Name[nds]=Fotos mit digiKam daalladen +Name[nl]=Foto's downloaden met digiKam +Name[pl]=Pobierz zdjęcia programem digiKam +Name[pt]=Obter Fotografias com o digiKam +Name[pt_BR]=Obter Fotografias com o digiKam +Name[sk]=Stiahnuť fotky pomocou digiKam +Name[sr]=Преузми слике помоћу digiKam-а +Name[sr@Latn]=Преузми слике помоћу digiKam-а +Name[sv]=Ladda ner foton med Digikam +Name[xx]=xxDownload Photos with digiKamxx + +[Desktop Entry] +Actions=digiKam Download +X-TDE-ServiceTypes=media/removable_mounted,media/camera_mounted diff --git a/src/utilities/hotplug/digikam-gphoto2-camera.desktop.in b/src/utilities/hotplug/digikam-gphoto2-camera.desktop.in new file mode 100644 index 00000000..f2c4a42a --- /dev/null +++ b/src/utilities/hotplug/digikam-gphoto2-camera.desktop.in @@ -0,0 +1,27 @@ +[Desktop Action digiKam Detect and Download] +Exec=@digikamhelper_dir@/digikam-camera detect %u +Icon=digikam +Name=digiKam Detect and Download +Name[ca]=Detecta i descarrega amb el digiKam +Name[de]=Finden und Herunterladen mit digiKam +Name[es]=Detectar y descargar con digiKam +Name[et]=*Fotode tuvastamine ja allalaadimine digiKamiga +Name[fi]=Tunnista kamera ja lataa kuvat digiKamilla +Name[fr]=Détecter et télécharger avec digiKam +Name[is]=digiKam Finna og Niðurhala +Name[it]=Rileva e scarica con digiKam +Name[ja]=digiKam 検出とダウンロード +Name[nds]=digiKam - Opdecken un daalladen +Name[nl]=digiKam-detectie en download +Name[pl]=Wykrycie i pobieranie digiKamem +Name[pt]=Detectar e Transferir com o digiKam +Name[pt_BR]=Detectar e Transferir com o digiKam +Name[sk]=digiKam Nájsť a stiahnuť +Name[sr]=digiKam-ово Препознај и преузми +Name[sr@Latn]=digiKam-ово Препознај и преузми +Name[sv]=Digikam detektering och nerladdning +Name[xx]=xxdigiKam Detect and Downloadxx + +[Desktop Entry] +Actions=digiKam Detect and Download +X-TDE-ServiceTypes=media/gphoto2camera diff --git a/src/utilities/hotplug/digikam-mount-and-download.desktop.in b/src/utilities/hotplug/digikam-mount-and-download.desktop.in new file mode 100644 index 00000000..2b773a15 --- /dev/null +++ b/src/utilities/hotplug/digikam-mount-and-download.desktop.in @@ -0,0 +1,27 @@ +[Desktop Action digiKam Mount and Download] +Exec=@digikamhelper_dir@/digikam-camera storage %u +Icon=digikam +Name=Download Photos with digiKam +Name[ca]=Descàrrega de fotos amb el digiKam +Name[de]=Fotos mit digiKam herunterladen +Name[es]=Descargar fotos con digiKam +Name[et]=Fotode allalaadimine digiKamiga +Name[fi]=Lataa valokuvat digiKamilla +Name[fr]=Télécharger les photos avec digiKam +Name[is]=Hala niður myndum með digiKam +Name[it]=Scarica foto con digiKam +Name[ja]=digiKam で写真をダウンロード +Name[nds]=Fotos mit digiKam daalladen +Name[nl]=Foto's downloaden met digiKam +Name[pl]=Pobierz zdjęcia programem digiKam +Name[pt]=Obter Fotografias com o digiKam +Name[pt_BR]=Obter Fotografias com o digiKam +Name[sk]=Stiahnuť fotky pomocou digiKam +Name[sr]=Преузми слике помоћу digiKam-а +Name[sr@Latn]=Преузми слике помоћу digiKam-а +Name[sv]=Ladda ner foton med Digikam +Name[xx]=xxDownload Photos with digiKamxx + +[Desktop Entry] +Actions=digiKam Mount and Download +X-TDE-ServiceTypes=media/removable_unmounted,media/camera_unmounted diff --git a/src/utilities/imageeditor/Makefile.am b/src/utilities/imageeditor/Makefile.am new file mode 100644 index 00000000..c324c683 --- /dev/null +++ b/src/utilities/imageeditor/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = canvas tools rawimport editor diff --git a/src/utilities/imageeditor/canvas/Makefile.am b/src/utilities/imageeditor/canvas/Makefile.am new file mode 100644 index 00000000..740f854e --- /dev/null +++ b/src/utilities/imageeditor/canvas/Makefile.am @@ -0,0 +1,28 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libdimgcanvas.la + +libdimgcanvas_la_SOURCES = dimginterface.cpp colorcorrectiondlg.cpp \ + canvas.cpp undocache.cpp \ + undoaction.cpp undomanager.cpp \ + imagepluginloader.cpp imageplugin.cpp + +libdimgcanvas_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TIFF) + +INCLUDES = -I$(top_srcdir)/src/digikam \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/threadimageio \ + -I$(top_srcdir)/src/utilities/splashscreen \ + -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/rawimport \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/widgets/common \ + $(LIBKEXIV2_CFLAGS) $(LIBKDCRAW_CFLAGS) $(all_includes) + +digikaminclude_HEADERS = imageplugin.h + +digikamincludedir = $(includedir)/digikam diff --git a/src/utilities/imageeditor/canvas/canvas.cpp b/src/utilities/imageeditor/canvas/canvas.cpp new file mode 100644 index 00000000..89758c3d --- /dev/null +++ b/src/utilities/imageeditor/canvas/canvas.cpp @@ -0,0 +1,1421 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-09 + * Description : image editor canvas management class + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// C++ includes. + +#include <cstdio> +#include <cmath> + +// TQt includes. + +#include <tqtooltip.h> +#include <tqfile.h> +#include <tqstring.h> +#include <tqevent.h> +#include <tqpoint.h> +#include <tqpainter.h> +#include <tqpen.h> +#include <tqpixmap.h> +#include <tqstyle.h> +#include <tqapplication.h> +#include <tqcursor.h> +#include <tqimage.h> +#include <tqregion.h> +#include <tqtimer.h> +#include <tqcache.h> +#include <tqcolor.h> +#include <tqdragobject.h> +#include <tqclipboard.h> +#include <tqtoolbutton.h> + +// KDE includes. + +#include <kcursor.h> +#include <tdelocale.h> +#include <kiconloader.h> +#include <kdatetbl.h> +#include <tdeglobalsettings.h> + +// Local includes. + +#include "ddebug.h" +#include "imagehistogram.h" +#include "imagepaniconwidget.h" +#include "dimginterface.h" +#include "iccsettingscontainer.h" +#include "exposurecontainer.h" +#include "iofilesettingscontainer.h" +#include "loadingcacheinterface.h" +#include "canvas.h" +#include "canvas.moc" + +namespace Digikam +{ + +class CanvasPrivate +{ + +public: + + CanvasPrivate() : + tileSize(128), minZoom(0.1), maxZoom(12.0), zoomMultiplier(1.2) + { + rubber = 0; + pressedMoved = false; + pressedMoving = false; + ltActive = false; + rtActive = false; + lbActive = false; + rbActive = false; + midButtonPressed = false; + midButtonX = 0; + midButtonY = 0; + panIconPopup = 0; + panIconWidget = 0; + cornerButton = 0; + parent = 0; + im = 0; + rubber = 0; + autoZoom = false; + fullScreen = false; + zoom = 1.0; + tileTmpPix = new TQPixmap(tileSize, tileSize); + + tileCache.setMaxCost((10*1024*1024)/(tileSize*tileSize*4)); + tileCache.setAutoDelete(true); + } + + bool autoZoom; + bool fullScreen; + bool pressedMoved; + bool pressedMoving; + bool ltActive; + bool rtActive; + bool lbActive; + bool rbActive; + bool midButtonPressed; + + const int tileSize; + int midButtonX; + int midButtonY; + + double zoom; + const double minZoom; + const double maxZoom; + const double zoomMultiplier; + + TQToolButton *cornerButton; + + TQRect *rubber; + TQRect pixmapRect; + + TQCache<TQPixmap> tileCache; + + TQPixmap* tileTmpPix; + TQPixmap qcheck; + + TQColor bgColor; + + TQWidget *parent; + + TDEPopupFrame *panIconPopup; + + DImgInterface *im; + + ImagePanIconWidget *panIconWidget; +}; + +Canvas::Canvas(TQWidget *parent) + : TQScrollView(parent) +{ + d = new CanvasPrivate; + d->im = new DImgInterface(); + d->parent = parent; + d->bgColor.setRgb(0, 0, 0); + + d->qcheck.resize(16, 16); + TQPainter p(&d->qcheck); + p.fillRect(0, 0, 8, 8, TQColor(144, 144, 144)); + p.fillRect(8, 8, 8, 8, TQColor(144, 144, 144)); + p.fillRect(0, 8, 8, 8, TQColor(100, 100, 100)); + p.fillRect(8, 0, 8, 8, TQColor(100, 100, 100)); + p.end(); + + d->cornerButton = new TQToolButton(this); + d->cornerButton->setIconSet(SmallIcon("move")); + d->cornerButton->hide(); + TQToolTip::add(d->cornerButton, i18n("Pan the image to a region")); + setCornerWidget(d->cornerButton); + + viewport()->setBackgroundMode(TQt::NoBackground); + viewport()->setMouseTracking(false); + setFrameStyle( TQFrame::NoFrame ); + + // ------------------------------------------------------------ + + connect(this, TQ_SIGNAL(signalZoomChanged(double)), + this, TQ_SLOT(slotZoomChanged(double))); + + connect(d->cornerButton, TQ_SIGNAL(pressed()), + this, TQ_SLOT(slotCornerButtonPressed())); + + connect(d->im, TQ_SIGNAL(signalModified()), + this, TQ_SLOT(slotModified())); + + connect(d->im, TQ_SIGNAL(signalUndoStateChanged(bool, bool, bool)), + this, TQ_SIGNAL(signalUndoStateChanged(bool, bool, bool))); + + connect(d->im, TQ_SIGNAL(signalLoadingStarted(const TQString&)), + this, TQ_SIGNAL(signalLoadingStarted(const TQString&))); + + connect(d->im, TQ_SIGNAL(signalImageLoaded(const TQString&, bool)), + this, TQ_SLOT(slotImageLoaded(const TQString&, bool))); + + connect(d->im, TQ_SIGNAL(signalImageSaved(const TQString&, bool)), + this, TQ_SLOT(slotImageSaved(const TQString&, bool))); + + connect(d->im, TQ_SIGNAL(signalLoadingProgress(const TQString&, float)), + this, TQ_SIGNAL(signalLoadingProgress(const TQString&, float))); + + connect(d->im, TQ_SIGNAL(signalSavingProgress(const TQString&, float)), + this, TQ_SIGNAL(signalSavingProgress(const TQString&, float))); + + connect(this, TQ_SIGNAL(signalSelected(bool)), + this, TQ_SLOT(slotSelected())); +} + +Canvas::~Canvas() +{ + delete d->tileTmpPix; + delete d->im; + + if (d->rubber) + delete d->rubber; + + delete d; +} + +void Canvas::resetImage() +{ + reset(); + viewport()->setUpdatesEnabled(false); + d->im->resetImage(); +} + +void Canvas::reset() +{ + if (d->rubber) + { + delete d->rubber; + d->rubber = 0; + if (d->im->imageValid()) + emit signalSelected(false); + } + + d->tileCache.clear(); +} + +void Canvas::load(const TQString& filename, IOFileSettingsContainer *IOFileSettings) +{ + reset(); + + viewport()->setUpdatesEnabled(false); + + d->im->load( filename, IOFileSettings, d->parent ); + + emit signalPrepareToLoad(); +} + +void Canvas::slotImageLoaded(const TQString& filePath, bool success) +{ + d->zoom = 1.0; + d->im->zoom(d->zoom); + + if (d->autoZoom) + updateAutoZoom(); + + updateContentsSize(true); + + viewport()->setUpdatesEnabled(true); + viewport()->update(); + + emit signalZoomChanged(d->zoom); + + emit signalLoadingFinished(filePath, success); +} + +void Canvas::preload(const TQString& /*filename*/) +{ +// d->im->preload(filename); +} + +/* +These code part are unused and untested +void Canvas::save(const TQString& filename, IOFileSettingsContainer *IOFileSettings) +{ + d->im->save(filename, IOFileSettings); + emit signalSavingStarted(filename); +} + +void Canvas::saveAs(const TQString& filename,IOFileSettingsContainer *IOFileSettings, + const TQString& mimeType) +{ + d->im->saveAs(filename, IOFileSettings, mimeType); + emit signalSavingStarted(filename); +} +*/ + +void Canvas::saveAs(const TQString& filename, IOFileSettingsContainer *IOFileSettings, + bool setExifOrientationTag, const TQString& mimeType) +{ + d->im->saveAs(filename, IOFileSettings, setExifOrientationTag, mimeType); + emit signalSavingStarted(filename); +} + +void Canvas::slotImageSaved(const TQString& filePath, bool success) +{ + emit signalSavingFinished(filePath, success); +} + +void Canvas::switchToLastSaved(const TQString& newFilename) +{ + d->im->switchToLastSaved(newFilename); +} + +void Canvas::abortSaving() +{ + d->im->abortSaving(); +} + +void Canvas::setModified() +{ + d->im->setModified(); +} + +void Canvas::readMetadataFromFile(const TQString &file) +{ + d->im->readMetadataFromFile(file); +} + +void Canvas::clearUndoHistory() +{ + d->im->clearUndoManager(); +} + +void Canvas::setUndoHistoryOrigin() +{ + d->im->setUndoManagerOrigin(); +} + +void Canvas::updateUndoState() +{ + d->im->updateUndoState(); +} + +DImg Canvas::currentImage() +{ + return DImg(*d->im->getImg()); +} + +TQString Canvas::currentImageFileFormat() +{ + return d->im->getImageFormat(); +} + +TQString Canvas::currentImageFilePath() +{ + return d->im->getImageFilePath(); +} + +int Canvas::imageWidth() +{ + return d->im->origWidth(); +} + +int Canvas::imageHeight() +{ + return d->im->origHeight(); +} + +bool Canvas::isReadOnly() +{ + return d->im->isReadOnly(); +} + +TQRect Canvas::getSelectedArea() +{ + int x, y, w, h; + d->im->getSelectedArea(x, y, w, h); + return ( TQRect(x, y, w, h) ); +} + +DImgInterface *Canvas::interface() const +{ + return d->im; +} + +void Canvas::makeDefaultEditingCanvas() +{ + DImgInterface::setDefaultInterface(d->im); +} + +double Canvas::calcAutoZoomFactor() +{ + if (!d->im->imageValid()) return d->zoom; + + double srcWidth = d->im->origWidth(); + double srcHeight = d->im->origHeight(); + double dstWidth = contentsRect().width(); + double dstHeight = contentsRect().height(); + return TQMIN(dstWidth/srcWidth, dstHeight/srcHeight); +} + +void Canvas::updateAutoZoom() +{ + d->zoom = calcAutoZoomFactor(); + d->im->zoom(d->zoom); + emit signalZoomChanged(d->zoom); +} + +void Canvas::updateContentsSize(bool deleteRubber) +{ + viewport()->setUpdatesEnabled(false); + + if (deleteRubber && d->rubber) + { + delete d->rubber; + d->rubber = 0; + d->ltActive = false; + d->rtActive = false; + d->lbActive = false; + d->rbActive = false; + d->pressedMoved = false; + viewport()->unsetCursor(); + viewport()->setMouseTracking(false); + if (d->im->imageValid()) + emit signalSelected(false); + } + + int wZ = d->im->width(); + int hZ = d->im->height(); + + if (visibleWidth() > wZ || visibleHeight() > hZ) + { + // Center the image + int centerx = contentsRect().width()/2; + int centery = contentsRect().height()/2; + int xoffset = int(centerx - wZ/2); + int yoffset = int(centery - hZ/2); + xoffset = TQMAX(xoffset, 0); + yoffset = TQMAX(yoffset, 0); + + d->pixmapRect = TQRect(xoffset, yoffset, wZ, hZ); + } + else + { + d->pixmapRect = TQRect(0, 0, wZ, hZ); + } + + if (!deleteRubber && d->rubber) + { + int xSel, ySel, wSel, hSel; + d->im->getSelectedArea(xSel, ySel, wSel, hSel); + xSel = (int)((xSel * d->tileSize) / floor(d->tileSize / d->zoom)); + ySel = (int)((ySel * d->tileSize) / floor(d->tileSize / d->zoom)); + wSel = (int)((wSel * d->tileSize) / floor(d->tileSize / d->zoom)); + hSel = (int)((hSel * d->tileSize) / floor(d->tileSize / d->zoom)); + d->rubber->setX(xSel); + d->rubber->setY(ySel); + d->rubber->setWidth(wSel); + d->rubber->setHeight(hSel); + d->rubber->moveBy(d->pixmapRect.x(), d->pixmapRect.y()); + } + + d->tileCache.clear(); + resizeContents(wZ, hZ); + viewport()->setUpdatesEnabled(true); +} + +void Canvas::resizeEvent(TQResizeEvent* e) +{ + if (!e) + return; + + TQScrollView::resizeEvent(e); + + if (d->autoZoom) + updateAutoZoom(); + + updateContentsSize(false); + + // No need to repaint. its called + // automatically after resize + + // To be sure than corner widget used to pan image will be hide/show + // accordinly with resize event. + slotZoomChanged(d->zoom); +} + +void Canvas::viewportPaintEvent(TQPaintEvent *e) +{ + TQRect er(e->rect()); + er = TQRect(TQMAX(er.x() - 1, 0), + TQMAX(er.y() - 1, 0), + TQMIN(er.width() + 2, contentsRect().width()), + TQMIN(er.height() + 2, contentsRect().height())); + + paintViewport(er, (d->zoom <= 1.0) ? true : false); +} + +void Canvas::paintViewport(const TQRect& er, bool antialias) +{ + TQRect o_cr(viewportToContents(er.topLeft()), viewportToContents(er.bottomRight())); + TQRect cr = o_cr; + + TQRegion clipRegion(er); + cr = d->pixmapRect.intersect(cr); + + if (!cr.isEmpty() && d->im->imageValid()) + { + clipRegion -= TQRect(contentsToViewport(cr.topLeft()), cr.size()); + + TQRect pr = TQRect(cr.x() - d->pixmapRect.x(), cr.y() - d->pixmapRect.y(), + cr.width(), cr.height()); + + int x1 = (int)floor((double)pr.x() / (double)d->tileSize) * d->tileSize; + int y1 = (int)floor((double)pr.y() / (double)d->tileSize) * d->tileSize; + int x2 = (int)ceilf((double)pr.right() / (double)d->tileSize) * d->tileSize; + int y2 = (int)ceilf((double)pr.bottom() / (double)d->tileSize) * d->tileSize; + + TQPixmap pix(d->tileSize, d->tileSize); + int sx, sy, sw, sh; + int step = (int)floor(d->tileSize / d->zoom); + + bool hasRubber = (d->rubber && d->pressedMoved && d->pressedMoving && d->rubber->intersects(pr)); + if (hasRubber) + { + // remove rubber + drawRubber(); + } + + for (int j = y1 ; j < y2 ; j += d->tileSize) + { + for (int i = x1 ; i < x2 ; i += d->tileSize) + { + TQString key = TQString("%1,%2").arg(i).arg(j); + TQPixmap *pix = d->tileCache.find(key); + + if (!pix) + { + if (antialias) + { + pix = new TQPixmap(d->tileSize, d->tileSize); + d->tileCache.insert(key, pix); + } + else + { + pix = d->tileTmpPix; + } + + if (d->im->hasAlpha()) + { + TQPainter p(pix); + p.drawTiledPixmap(0, 0, d->tileSize, d->tileSize, + d->qcheck, 0, 0); + p.end(); + } + else + { + pix->fill(d->bgColor); + } + + // NOTE : with implementations <= 0.9.1, the canvas doesn't work properly using high zoom level (> 500). + // The sx, sy, sw, sh values haven't be computed properly and "tile" artefacts been appears + // over the image. Look the example here: + // http://digikam3rdparty.free.fr/Screenshots/editorhighzoomartefact.png + // Note than these "tile" artifacts are not the real tiles of canvas. + // The new implementation below fix this problem to handle properly the areas to + // use from the source image to generate the canvas pixmap tiles. + + sx = (int)floor((double)i / d->tileSize) * step; + sy = (int)floor((double)j / d->tileSize) * step; + sw = step; + sh = step; + + if (d->rubber && d->pressedMoved && !d->pressedMoving) + { + TQRect rr(d->rubber->normalize()); + TQRect r(i, j, d->tileSize, d->tileSize); + + d->im->paintOnDevice(pix, sx, sy, sw, sh, + 0, 0, d->tileSize, d->tileSize, + rr.x() - i - d->pixmapRect.x(), + rr.y() - j - d->pixmapRect.y(), + rr.width(), rr.height(), + antialias); + + rr.moveBy(-i -d->pixmapRect.x(), -j -d->pixmapRect.y()); + + TQPainter p(pix); + p.setPen(TQPen(TQColor(250, 250, 255), 1)); + p.drawRect(rr); + if (rr.width() >= 10 && rr.height() >= 10) + { + p.drawRect(rr.x(), rr.y(), 5, 5); + p.drawRect(rr.x(), rr.y()+rr.height()-5, 5, 5); + p.drawRect(rr.x()+rr.width()-5, rr.y()+rr.height()-5, 5, 5); + p.drawRect(rr.x()+rr.width()-5, rr.y(), 5, 5); + } + p.end(); + } + else + { + d->im->paintOnDevice(pix, sx, sy, sw, sh, + 0, 0, d->tileSize, d->tileSize, + antialias); + } + } + + TQRect r(i, j, d->tileSize, d->tileSize); + TQRect ir = pr.intersect(r); + TQPoint pt(contentsToViewport(TQPoint(ir.x() + d->pixmapRect.x(), + ir.y() + d->pixmapRect.y()))); + + bitBlt(viewport(), pt.x(), pt.y(), + pix, + ir.x()-r.x(), ir.y()-r.y(), + ir.width(), ir.height()); + } + } + + if (hasRubber) + { + // restore rubber + drawRubber(); + } + } + + TQPainter painter(viewport()); + painter.setClipRegion(clipRegion); + painter.fillRect(er, d->bgColor); + painter.end(); +} + +void Canvas::drawRubber() +{ + if (!d->rubber || !d->im->imageValid()) + return; + + TQPainter p(viewport()); + p.setRasterOp(TQt::NotROP ); + p.setPen(TQPen(TQt::color0, 1)); + p.setBrush(NoBrush); + + TQRect r(d->rubber->normalize()); + r = TQRect(contentsToViewport(TQPoint(r.x(), r.y())), r.size()); + + TQPoint pnt(r.x(), r.y()); + + style().drawPrimitive(TQStyle::PE_FocusRect, &p, + TQRect(pnt.x(), pnt.y(), r.width(), r.height()), + colorGroup(), TQStyle::Style_Default, + TQStyleOption(colorGroup().base())); + p.end(); +} + +void Canvas::contentsMousePressEvent(TQMouseEvent *e) +{ + if (!e || e->button() == TQt::RightButton) + return; + + d->midButtonPressed = false; + + if (e->button() == TQt::LeftButton) + { + if (d->ltActive || d->rtActive || + d->lbActive || d->rbActive) + { + Q_ASSERT( d->rubber ); + if (!d->rubber) + return; + + // Set diagonally opposite corner as anchor + + TQRect r(d->rubber->normalize()); + + if (d->ltActive) + { + d->rubber->setTopLeft(r.bottomRight()); + d->rubber->setBottomRight(r.topLeft()); + } + else if (d->rtActive) + { + d->rubber->setTopLeft(r.bottomLeft()); + d->rubber->setBottomRight(r.topRight()); + } + else if (d->lbActive) + { + d->rubber->setTopLeft(r.topRight()); + d->rubber->setBottomRight(r.bottomLeft()); + } + else if (d->rbActive) + { + d->rubber->setTopLeft(r.topLeft()); + d->rubber->setBottomRight(r.bottomLeft()); + } + + viewport()->setMouseTracking(false); + d->pressedMoved = false; + d->pressedMoving = true; + + d->tileCache.clear(); + viewport()->repaint(false); + + return; + } + } + else if (e->button() == TQt::MidButton) + { + if (visibleWidth() < d->im->width() || + visibleHeight() < d->im->height()) + { + viewport()->setCursor(TQt::SizeAllCursor); + d->midButtonPressed = true; + d->midButtonX = e->x(); + d->midButtonY = e->y(); + } + return; + } + + if (d->rubber) + { + delete d->rubber; + d->rubber = 0; + } + + d->rubber = new TQRect(e->x(), e->y(), 0, 0); + + if (d->pressedMoved) + { + d->tileCache.clear(); + viewport()->update(); + } + + d->pressedMoved = false; + d->pressedMoving = true; + + viewport()->setMouseTracking(false); +} + +void Canvas::contentsMouseMoveEvent(TQMouseEvent *e) +{ + if (!e) + return; + + if (e->state() & TQt::MidButton) + { + if (d->midButtonPressed) + { + scrollBy(d->midButtonX - e->x(), + d->midButtonY - e->y()); + } + } + else if (!viewport()->hasMouseTracking()) + { + if (!d->rubber) + return; + + if (e->state() != TQt::LeftButton && + !(d->ltActive || d->rtActive || + d->lbActive || d->rbActive)) + return; + + // Clear old rubber. + if (d->pressedMoved) + drawRubber(); + + // Move content if necessary. + blockSignals(true); + setUpdatesEnabled(false); + ensureVisible(e->x(), e->y(), 10, 10); + setUpdatesEnabled(true); + blockSignals(false); + + // draw the new rubber position. + int r, b; + r = (e->x() > d->pixmapRect.left()) ? e->x() : d->pixmapRect.left(); + r = (r < d->pixmapRect.right()) ? r : d->pixmapRect.right(); + b = (e->y() > d->pixmapRect.top()) ? e->y() : d->pixmapRect.top(); + b = (b < d->pixmapRect.bottom()) ? b : d->pixmapRect.bottom(); + d->rubber->setRight(r); + d->rubber->setBottom(b); + drawRubber(); + + d->pressedMoved = true; + d->pressedMoving = true; + + // To refresh editor status bar with current selection. + emit signalSelectionChanged(calcSeletedArea()); + } + else + { + if (!d->rubber) + return; + + TQRect r(d->rubber->normalize()); + + TQRect lt(r.x()-5, r.y()-5, 10, 10); + TQRect rt(r.x()+r.width()-5, r.y()-5, 10, 10); + TQRect lb(r.x()-5, r.y()+r.height()-5, 10, 10); + TQRect rb(r.x()+r.width()-5, r.y()+r.height()-5, 10, 10); + + d->ltActive = false; + d->rtActive = false; + d->lbActive = false; + d->rbActive = false; + + if (lt.contains(e->x(), e->y())) + { + viewport()->setCursor(TQt::SizeFDiagCursor); + d->ltActive = true; + } + else if (rb.contains(e->x(), e->y())) + { + viewport()->setCursor(TQt::SizeFDiagCursor); + d->rbActive = true; + } + else if (lb.contains(e->x(), e->y())) + { + viewport()->setCursor(TQt::SizeBDiagCursor); + d->lbActive = true; + } + else if (rt.contains(e->x(), e->y())) + { + viewport()->setCursor(TQt::SizeBDiagCursor); + d->rtActive = true; + } + else + viewport()->unsetCursor(); + } +} + +void Canvas::contentsMouseReleaseEvent(TQMouseEvent *e) +{ + if (!e) + return; + + d->midButtonPressed = false; + + if (d->pressedMoving) + { + d->pressedMoving = false; + viewport()->update(); + } + + if (d->pressedMoved && d->rubber) + { + // Normalize rubber rectangle to always have the selection into the image + TQRect rec = d->rubber->normalize(); + + if (rec.left() < d->pixmapRect.left()) rec.setLeft(d->pixmapRect.left()); + if (rec.right() > d->pixmapRect.right()) rec.setRight(d->pixmapRect.right()); + if (rec.top() < d->pixmapRect.top()) rec.setTop(d->pixmapRect.top()); + if (rec.bottom() > d->pixmapRect.bottom()) rec.setBottom(d->pixmapRect.bottom()); + + d->rubber->setLeft(rec.left()); + d->rubber->setRight(rec.right()); + d->rubber->setTop(rec.top()); + d->rubber->setBottom(rec.bottom()); + + d->tileCache.clear(); + viewport()->setMouseTracking(true); + if (d->im->imageValid()) + emit signalSelected(true); + } + else + { + d->ltActive = false; + d->rtActive = false; + d->lbActive = false; + d->rbActive = false; + viewport()->setMouseTracking(false); + viewport()->unsetCursor(); + if (d->im->imageValid()) + emit signalSelected(false); + } + + if (e->button() != TQt::LeftButton) + { + viewport()->unsetCursor(); + } + + if (e->button() == TQt::RightButton) + { + emit signalRightButtonClicked(); + } +} + +void Canvas::contentsWheelEvent(TQWheelEvent *e) +{ + e->accept(); + + if (e->state() & TQt::ShiftButton) + { + if (e->delta() < 0) + emit signalShowNextImage(); + else if (e->delta() > 0) + emit signalShowPrevImage(); + return; + } + else if (e->state() & TQt::ControlButton) + { + if (e->delta() < 0) + slotDecreaseZoom(); + else if (e->delta() > 0) + slotIncreaseZoom(); + return; + } + + TQScrollView::contentsWheelEvent(e); +} + +bool Canvas::maxZoom() +{ + return ((d->zoom * d->zoomMultiplier) >= d->maxZoom); +} + +bool Canvas::minZoom() +{ + return ((d->zoom / d->zoomMultiplier) <= d->minZoom); +} + +bool Canvas::exifRotated() +{ + return d->im->exifRotated(); +} + +double Canvas::snapZoom(double zoom) +{ + // If the zoom value gets changed from d->zoom to zoom + // across 50%, 100% or fit-to-window, then return the + // the corresponding special value. Otherwise zoom is returned unchanged. + double fit = calcAutoZoomFactor(); + TQValueList<double> snapValues; + snapValues.append(0.5); + snapValues.append(1.0); + snapValues.append(fit); + + qHeapSort(snapValues); + TQValueList<double>::const_iterator it; + + if (d->zoom < zoom) + { + for(it = snapValues.constBegin(); it != snapValues.constEnd(); ++it) + { + double z = *it; + if ((d->zoom < z) && (zoom > z)) + { + zoom = z; + break; + } + } + } + else + { + // We need to go through the list in reverse order, + // however, tqCopyBackward does not seem to work here, so + // a simple for loop over integers is used instead. + for(int i=snapValues.size()-1; i>=0; i--) + { + double z = snapValues[i]; + if ((d->zoom > z) && (zoom < z)) + { + zoom = z; + break; + } + } + } + + return zoom; +} + +void Canvas::slotIncreaseZoom() +{ + if (maxZoom()) + return; + + double zoom = d->zoom * d->zoomMultiplier; + zoom = snapZoom(zoom); + setZoomFactor(zoom); +} + +void Canvas::slotDecreaseZoom() +{ + if (minZoom()) + return; + + double zoom = d->zoom / d->zoomMultiplier; + zoom = snapZoom(zoom); + setZoomFactor(zoom); +} + +void Canvas::setZoomFactorSnapped(double zoom) +{ + double fit = calcAutoZoomFactor(); + + if (fabs(zoom-fit) < 0.05) + { + // If 1.0 or 0.5 are even closer to zoom than fit, then choose these. + if (fabs(zoom-fit) > fabs(zoom-1.0) ) + { + zoom = 1.0; + } + else if (fabs(zoom-fit) > fabs(zoom-0.5) ) + { + zoom = 0.5; + } + else + { + zoom = fit; + } + } + else + { + if (fabs(zoom-1.0) < 0.05) + { + zoom = 1.0; + } + if (fabs(zoom-0.5) < 0.05) + { + zoom = 0.5; + } + } + setZoomFactor(zoom); +} + +double Canvas::zoomFactor() +{ + return d->zoom; +} + +void Canvas::setZoomFactor(double zoom) +{ + if (d->autoZoom) + { + d->autoZoom = false; + emit signalToggleOffFitToWindow(); + } + + // Zoom using center of canvas and given zoom factor. + + double cpx = contentsX() + visibleWidth() / 2.0; + double cpy = contentsY() + visibleHeight() / 2.0; + + cpx = (cpx / d->tileSize) * floor(d->tileSize / d->zoom); + cpy = (cpy / d->tileSize) * floor(d->tileSize / d->zoom); + + d->zoom = zoom; + + d->im->zoom(d->zoom); + updateContentsSize(false); + + viewport()->setUpdatesEnabled(false); + center((int)((cpx * d->tileSize) / floor(d->tileSize / d->zoom)), + (int)((cpy * d->tileSize) / floor(d->tileSize / d->zoom))); + viewport()->setUpdatesEnabled(true); + viewport()->update(); + + emit signalZoomChanged(d->zoom); +} + +void Canvas::fitToSelect() +{ + int xSel, ySel, wSel, hSel; + d->im->getSelectedArea(xSel, ySel, wSel, hSel); + + if (wSel && hSel ) + { + // If selected area, use center of selection + // and recompute zoom factor accordinly. + double cpx = xSel + wSel / 2.0; + double cpy = ySel + hSel / 2.0; + + double srcWidth = wSel; + double srcHeight = hSel; + double dstWidth = contentsRect().width(); + double dstHeight = contentsRect().height(); + + d->zoom = TQMIN(dstWidth/srcWidth, dstHeight/srcHeight); + + d->autoZoom = false; + emit signalToggleOffFitToWindow(); + d->im->zoom(d->zoom); + updateContentsSize(true); + + viewport()->setUpdatesEnabled(false); + center((int)((cpx * d->tileSize) / floor(d->tileSize / d->zoom)), + (int)((cpy * d->tileSize) / floor(d->tileSize / d->zoom))); + viewport()->setUpdatesEnabled(true); + viewport()->update(); + + emit signalZoomChanged(d->zoom); + } +} + +bool Canvas::fitToWindow() +{ + return d->autoZoom; +} + +void Canvas::toggleFitToWindow() +{ + d->autoZoom = !d->autoZoom; + + if (d->autoZoom) + updateAutoZoom(); + else + { + d->zoom = 1.0; + emit signalZoomChanged(d->zoom); + } + + d->im->zoom(d->zoom); + updateContentsSize(false); + slotZoomChanged(d->zoom); + viewport()->update(); +} + +void Canvas::slotRotate90() +{ + d->im->rotate90(); +} + +void Canvas::slotRotate180() +{ + d->im->rotate180(); +} + +void Canvas::slotRotate270() +{ + d->im->rotate270(); +} + +void Canvas::slotFlipHoriz() +{ + d->im->flipHoriz(); +} + +void Canvas::slotFlipVert() +{ + d->im->flipVert(); +} + +void Canvas::slotCrop() +{ + int x, y, w, h; + d->im->getSelectedArea(x, y, w, h); + + if (!w && !h ) // No current selection. + return; + + d->im->crop(x, y, w, h); +} + +void Canvas::resizeImage(int w, int h) +{ + d->im->resize(w, h); +} + +void Canvas::setBackgroundColor(const TQColor& color) +{ + if (d->bgColor == color) + return; + + d->bgColor = color; + viewport()->update(); +} + +void Canvas::setICCSettings(ICCSettingsContainer *cmSettings) +{ + d->im->setICCSettings(cmSettings); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::setExposureSettings(ExposureSettingsContainer *expoSettings) +{ + d->im->setExposureSettings(expoSettings); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::setExifOrient(bool exifOrient) +{ + d->im->setExifOrient(exifOrient); + viewport()->update(); +} + +void Canvas::increaseGamma() +{ + d->im->changeGamma(1); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::decreaseGamma() +{ + d->im->changeGamma(-1); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::increaseBrightness() +{ + d->im->changeBrightness(1); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::decreaseBrightness() +{ + d->im->changeBrightness(-1); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::increaseContrast() +{ + d->im->changeContrast(5); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::decreaseContrast() +{ + d->im->changeContrast(-5); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::slotRestore() +{ + d->im->restore(); +} + +void Canvas::slotUndo(int steps) +{ + while(steps > 0) + { + d->im->undo(); + --steps; + } +} + +void Canvas::getUndoHistory(TQStringList &titles) +{ + d->im->getUndoHistory(titles); +} + +void Canvas::getRedoHistory(TQStringList &titles) +{ + d->im->getRedoHistory(titles); +} + +void Canvas::slotRedo(int steps) +{ + while(steps > 0) + { + d->im->redo(); + --steps; + } +} + +void Canvas::slotCopy() +{ + int x, y, w, h; + d->im->getSelectedArea(x, y, w, h); + + if (!w && !h ) // No current selection. + return; + + TQApplication::setOverrideCursor (TQt::waitCursor); + uchar* data = d->im->getImageSelection(); + DImg selDImg = DImg(w, h, d->im->sixteenBit(), d->im->hasAlpha(), data); + delete [] data; + + TQImage selImg = selDImg.copyTQImage(); + TQApplication::clipboard()->setData(new TQImageDrag(selImg), TQClipboard::Clipboard); + TQApplication::restoreOverrideCursor (); +} + +void Canvas::slotSelected() +{ + int x=0, y=0, w=0, h=0; + + if (d->rubber && d->pressedMoved) + { + TQRect sel = calcSeletedArea(); + x = sel.x(); + y = sel.y(); + w = sel.width(); + h = sel.height(); + } + + d->im->setSelectedArea(x, y, w, h); +} + +TQRect Canvas::calcSeletedArea() +{ + int x=0, y=0, w=0, h=0; + TQRect r(d->rubber->normalize()); + + if (r.isValid()) + { + r.moveBy(- d->pixmapRect.x(), - d->pixmapRect.y()); + + x = (int)(((double)r.x() / d->tileSize) * floor(d->tileSize / d->zoom)); + y = (int)(((double)r.y() / d->tileSize) * floor(d->tileSize / d->zoom)); + w = (int)(((double)r.width() / d->tileSize) * floor(d->tileSize / d->zoom)); + h = (int)(((double)r.height() / d->tileSize) * floor(d->tileSize / d->zoom)); + + x = TQMIN(imageWidth(), TQMAX(x, 0)); + y = TQMIN(imageHeight(), TQMAX(y, 0)); + w = TQMIN(imageWidth(), TQMAX(w, 0)); + h = TQMIN(imageHeight(), TQMAX(h, 0)); + + // Avoid empty selection by rubberband - at least mark one pixel + // At high zoom factors, the rubberband may operate at subpixel level! + if (w == 0) + w = 1; + if (h == 0) + h = 1; + } + + return TQRect(x, y, w, h); +} + +void Canvas::slotModified() +{ + if (d->autoZoom) + updateAutoZoom(); + d->im->zoom(d->zoom); + + updateContentsSize(true); + viewport()->update(); + + // To be sure than corner widget used to pan image will be hide/show + // accordinly with new image size (if changed). + slotZoomChanged(d->zoom); + + emit signalChanged(); +} + +void Canvas::slotCornerButtonPressed() +{ + if (d->panIconPopup) + { + d->panIconPopup->hide(); + delete d->panIconPopup; + d->panIconPopup = 0; + } + + d->panIconPopup = new TDEPopupFrame(this); + ImagePanIconWidget *pan = new ImagePanIconWidget(180, 120, d->panIconPopup); + d->panIconPopup->setMainWidget(pan); + + TQRect r((int)(contentsX() / d->zoom), (int)(contentsY() / d->zoom), + (int)(visibleWidth() / d->zoom), (int)(visibleHeight() / d->zoom)); + pan->setRegionSelection(r); + pan->setMouseFocus(); + + connect(pan, TQ_SIGNAL(signalSelectionMoved(const TQRect&, bool)), + this, TQ_SLOT(slotPanIconSelectionMoved(const TQRect&, bool))); + + connect(pan, TQ_SIGNAL(signalHiden()), + this, TQ_SLOT(slotPanIconHiden())); + + TQPoint g = mapToGlobal(viewport()->pos()); + g.setX(g.x()+ viewport()->size().width()); + g.setY(g.y()+ viewport()->size().height()); + d->panIconPopup->popup(TQPoint(g.x() - d->panIconPopup->width(), + g.y() - d->panIconPopup->height())); + + pan->setCursorToLocalRegionSelectionCenter(); +} + +void Canvas::slotPanIconHiden() +{ + d->cornerButton->blockSignals(true); + d->cornerButton->animateClick(); + d->cornerButton->blockSignals(false); +} + +void Canvas::slotPanIconSelectionMoved(const TQRect& r, bool b) +{ + setContentsPos((int)(r.x()*d->zoom), (int)(r.y()*d->zoom)); + + if (b) + { + d->panIconPopup->hide(); + delete d->panIconPopup; + d->panIconPopup = 0; + slotPanIconHiden(); + } +} + +void Canvas::slotZoomChanged(double /*zoom*/) +{ + updateScrollBars(); + + if (horizontalScrollBar()->isVisible() || verticalScrollBar()->isVisible()) + d->cornerButton->show(); + else + d->cornerButton->hide(); +} + +void Canvas::slotSelectAll() +{ + if (d->rubber) + { + delete d->rubber; + d->rubber = 0; + } + + d->rubber = new TQRect(d->pixmapRect); + d->pressedMoved = true; + d->tileCache.clear(); + viewport()->setMouseTracking(true); + viewport()->update(); + + if (d->im->imageValid()) + emit signalSelected(true); +} + +void Canvas::slotSelectNone() +{ + reset(); + viewport()->update(); +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/canvas/canvas.h b/src/utilities/imageeditor/canvas/canvas.h new file mode 100644 index 00000000..c772098d --- /dev/null +++ b/src/utilities/imageeditor/canvas/canvas.h @@ -0,0 +1,209 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-09 + * Description : image editor canvas management class + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef CANVAS_H +#define CANVAS_H + +// TQt includes. + +#include <tqscrollview.h> +#include <tqrect.h> + +// Local includes. + +#include "digikam_export.h" +#include "dimg.h" + +class TQString; +class TQStringList; +class TQPixmap; +class TQPaintEvent; +class TQResizeEvent; +class TQWheelEvent; +class TQKeyEvent; +class TQColor; + +namespace Digikam +{ + +class CanvasPrivate; +class DImgInterface; +class ExposureSettingsContainer; +class ICCSettingsContainer; +class IOFileSettingsContainer; + +class DIGIKAM_EXPORT Canvas : public TQScrollView +{ + TQ_OBJECT + + +public: + + Canvas(TQWidget *parent=0); + ~Canvas(); + + void load(const TQString& filename, IOFileSettingsContainer *IOFileSettings); + void preload(const TQString& filename); + + void saveAs(const TQString& filename, IOFileSettingsContainer *IOFileSettings, + bool setExifOrientationTag, const TQString& mimeType=TQString()); + void resetImage(); + void switchToLastSaved(const TQString& newFilename); + void abortSaving(); + void setModified(); + void readMetadataFromFile(const TQString &file); + void clearUndoHistory(); + void setUndoHistoryOrigin(); + void updateUndoState(); + DImg currentImage(); + TQString currentImageFileFormat(); + TQString currentImageFilePath(); + + DImgInterface *interface() const; + void makeDefaultEditingCanvas(); + + double snapZoom(double z); + void setZoomFactorSnapped(double zoom); + + double zoomFactor(); + void setZoomFactor(double z); + bool fitToWindow(); + bool maxZoom(); + bool minZoom(); + bool exifRotated(); + int imageWidth(); + int imageHeight(); + TQRect getSelectedArea(); + + // If current image file format is only available in read only, + // typicially all RAW image file formats. + bool isReadOnly(); + + void resizeImage(int w, int h); + + void setBackgroundColor(const TQColor& color); + void setICCSettings(ICCSettingsContainer *cmSettings); + void setExposureSettings(ExposureSettingsContainer *expoSettings); + + void setExifOrient(bool exifOrient); + + void increaseGamma(); + void decreaseGamma(); + void increaseBrightness(); + void decreaseBrightness(); + void increaseContrast(); + void decreaseContrast(); + + void getUndoHistory(TQStringList &titles); + void getRedoHistory(TQStringList &titles); + + void toggleFitToWindow(); + void fitToSelect(); + +signals: + + void signalZoomChanged(double zoom); + void signalMaxZoom(); + void signalMinZoom(); + void signalChanged(); + void signalUndoStateChanged(bool, bool, bool); + void signalSelected(bool); + void signalRightButtonClicked(); + void signalShowNextImage(); + void signalShowPrevImage(); + void signalPrepareToLoad(); + void signalLoadingStarted(const TQString &filename); + void signalLoadingFinished(const TQString &filename, bool success); + void signalLoadingProgress(const TQString& filePath, float progress); + void signalSavingStarted(const TQString &filename); + void signalSavingFinished(const TQString &filename, bool success); + void signalSavingProgress(const TQString& filePath, float progress); + void signalSelectionChanged(const TQRect&); + void signalToggleOffFitToWindow(); + +public slots: + + void slotIncreaseZoom(); + void slotDecreaseZoom(); + + // image modifiers + void slotRotate90(); + void slotRotate180(); + void slotRotate270(); + + void slotFlipHoriz(); + void slotFlipVert(); + + void slotCrop(); + + void slotRestore(); + void slotUndo(int steps=1); + void slotRedo(int steps=1); + + void slotCopy(); + + void slotSelectAll(); + void slotSelectNone(); + +protected: + + void resizeEvent(TQResizeEvent* e); + void viewportPaintEvent(TQPaintEvent *e); + void contentsMousePressEvent(TQMouseEvent *e); + void contentsMouseMoveEvent(TQMouseEvent *e); + void contentsMouseReleaseEvent(TQMouseEvent *e); + void contentsWheelEvent(TQWheelEvent *e); + +private: + + TQRect calcSeletedArea(); + double calcAutoZoomFactor(); + void updateAutoZoom(); + void updateContentsSize(bool deleteRubber); + + void drawRubber(); + void paintViewport(const TQRect& er, bool antialias); + + void reset(); + +private slots: + + void slotSelected(); + void slotModified(); + void slotImageLoaded(const TQString& filePath, bool success); + void slotImageSaved(const TQString& filePath, bool success); + void slotCornerButtonPressed(); + void slotZoomChanged(double); + void slotPanIconSelectionMoved(const TQRect&, bool); + void slotPanIconHiden(); + +private: + + CanvasPrivate *d; +}; + +} // namespace Digikam + +#endif /* CANVAS_H */ + diff --git a/src/utilities/imageeditor/canvas/colorcorrectiondlg.cpp b/src/utilities/imageeditor/canvas/colorcorrectiondlg.cpp new file mode 100644 index 00000000..1403773f --- /dev/null +++ b/src/utilities/imageeditor/canvas/colorcorrectiondlg.cpp @@ -0,0 +1,186 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-05-15 + * Description : a dialog to see preview ICC color correction + * before to apply color profile. + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlabel.h> +#include <tqwhatsthis.h> +#include <tqlayout.h> +#include <tqframe.h> +#include <tqstring.h> +#include <tqfileinfo.h> +#include <tqpushbutton.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kiconloader.h> +#include <tdeapplication.h> +#include <kseparator.h> + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "icctransform.h" +#include "iccprofileinfodlg.h" +#include "colorcorrectiondlg.h" +#include "colorcorrectiondlg.moc" + +namespace Digikam +{ + +ColorCorrectionDlg::ColorCorrectionDlg(TQWidget* parent, DImg *preview, + IccTransform *iccTrans, const TQString& file) + : KDialogBase(parent, "", true, TQString(), + Help|Ok|Apply|Cancel, Ok, true) +{ + m_iccTrans = iccTrans; + m_parent = parent; + setHelp("iccprofile.anchor", "digikam"); + setButtonText(Ok, i18n("Convert")); + setButtonTip(Ok, i18n("Apply the default color workspace profile to the image")); + setButtonText(Cancel, i18n("Do Nothing")); + setButtonTip(Cancel, i18n("Do not change the image")); + setButtonText(Apply, i18n("Assign")); + setButtonTip(Apply, i18n("Only embed the color workspace profile in the image, don't change the image")); + + TQFileInfo fi(file); + setCaption(fi.fileName()); + + TQWidget *page = new TQWidget(this); + TQGridLayout* grid = new TQGridLayout(page, 3, 2, 0, KDialog::spacingHint()); + + TQLabel *originalTitle = new TQLabel(i18n("Original Image:"), page); + TQLabel *previewOriginal = new TQLabel(page); + TQLabel *targetTitle = new TQLabel(i18n("Corrected Image:"), page); + TQLabel *previewTarget = new TQLabel(page); + TQLabel *logo = new TQLabel(page); + TQLabel *message = new TQLabel(page); + TQLabel *currentProfileTitle = new TQLabel(i18n("Current workspace color profile:"), page); + TQLabel *currentProfileDesc = new TQLabel(TQString("<b>%1</b>").arg(m_iccTrans->getOutpoutProfileDescriptor()), page); + TQPushButton *currentProfInfo = new TQPushButton(i18n("Info..."), page); + TQLabel *embeddedProfileTitle = new TQLabel(i18n("Embedded color profile:"), page); + TQLabel *embeddedProfileDesc = new TQLabel(TQString("<b>%1</b>").arg(m_iccTrans->getEmbeddedProfileDescriptor()), page); + TQPushButton *embeddedProfInfo = new TQPushButton(i18n("Info..."), page); + KSeparator *line = new KSeparator(TQt::Horizontal, page); + + if (m_iccTrans->embeddedProfile().isEmpty()) + { + message->setText(i18n("<p>This image has not been assigned a color profile.</p>" + "<p>Do you want to convert it to your workspace color profile?</p>")); + + line->hide(); + embeddedProfileTitle->hide(); + embeddedProfileDesc->hide(); + embeddedProfInfo->hide(); + } + else + { + message->setText(i18n("<p>This image has been assigned to a color profile that does not " + "match your default workspace color profile.</p>" + "<p>Do you want to convert it to your workspace color profile?</p>")); + } + + previewOriginal->setPixmap(preview->convertToPixmap()); + previewTarget->setPixmap(preview->convertToPixmap(m_iccTrans)); + TDEIconLoader* iconLoader = TDEApplication::kApplication()->iconLoader(); + logo->setPixmap(iconLoader->loadIcon("digikam", TDEIcon::NoGroup, 128, TDEIcon::DefaultState, 0, true)); + + grid->addMultiCellWidget(originalTitle, 0, 0, 0, 0); + grid->addMultiCellWidget(previewOriginal, 1, 1, 0, 0); + grid->addMultiCellWidget(targetTitle, 2, 2, 0, 0); + grid->addMultiCellWidget(previewTarget, 3, 3, 0, 0); + + TQVBoxLayout *vlay = new TQVBoxLayout( KDialog::spacingHint() ); + vlay->addWidget(logo); + vlay->addWidget(message); + + vlay->addWidget(new KSeparator(TQt::Horizontal, page)); + vlay->addWidget(currentProfileTitle); + vlay->addWidget(currentProfileDesc); + + TQHBoxLayout *hlay1 = new TQHBoxLayout( KDialog::spacingHint() ); + hlay1->addWidget(currentProfInfo); + hlay1->addStretch(); + vlay->addLayout(hlay1); + + vlay->addWidget(line); + vlay->addWidget(embeddedProfileTitle); + vlay->addWidget(embeddedProfileDesc); + + TQHBoxLayout *hlay2 = new TQHBoxLayout( KDialog::spacingHint() ); + hlay2->addWidget(embeddedProfInfo); + hlay2->addStretch(); + vlay->addLayout(hlay2); + vlay->addStretch(); + + grid->addMultiCell(new TQSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(), + TQSizePolicy::Minimum, TQSizePolicy::Expanding), 0, 3, 1, 1); + grid->addMultiCellLayout(vlay, 0, 3, 2, 2); + + setMainWidget(page); + + // -------------------------------------------------------------------- + + connect(currentProfInfo, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotCurrentProfInfo()) ); + + connect(embeddedProfInfo, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotEmbeddedProfInfo()) ); + + connect(this, TQ_SIGNAL(applyClicked()), + this, TQ_SLOT(slotApplyClicked())); +} + +ColorCorrectionDlg::~ColorCorrectionDlg() +{ +} + +void ColorCorrectionDlg::slotCurrentProfInfo() +{ + if (m_iccTrans->outputProfile().isEmpty()) + return; + + ICCProfileInfoDlg infoDlg(m_parent, TQString(), m_iccTrans->outputProfile()); + infoDlg.exec(); +} + +void ColorCorrectionDlg::slotEmbeddedProfInfo() +{ + if (m_iccTrans->embeddedProfile().isEmpty()) + return; + + ICCProfileInfoDlg infoDlg(m_parent, TQString(), m_iccTrans->embeddedProfile()); + infoDlg.exec(); +} + +void ColorCorrectionDlg::slotApplyClicked() +{ + DDebug() << "colorcorrectiondlg: Apply pressed" << endl; + done(-1); +} + +} // NameSpace Digikam + diff --git a/src/utilities/imageeditor/canvas/colorcorrectiondlg.h b/src/utilities/imageeditor/canvas/colorcorrectiondlg.h new file mode 100644 index 00000000..7cc4c19d --- /dev/null +++ b/src/utilities/imageeditor/canvas/colorcorrectiondlg.h @@ -0,0 +1,72 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-05-15 + * Description : a dialog to see preview ICC color correction + * before to apply color profile. + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef COLORCORRECTIONDLG_H +#define COLORCORRECTIONDLG_H + +// TQt includes. + +#include <tqstring.h> + +// KDE includes. + +#include <kdialogbase.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class IccTransform; +class DImg; + +class DIGIKAM_EXPORT ColorCorrectionDlg : public KDialogBase +{ + TQ_OBJECT + + +public: + + ColorCorrectionDlg(TQWidget *parent, DImg *preview, + IccTransform *iccTrans, const TQString& file); + ~ColorCorrectionDlg(); + +private slots: + + void slotCurrentProfInfo(); + void slotEmbeddedProfInfo(); + void slotApplyClicked(); + +private: + + TQWidget *m_parent; + + IccTransform *m_iccTrans; +}; + +} // Namespace Digikam + +#endif /* COLORCORRECTIONDLG_H */ diff --git a/src/utilities/imageeditor/canvas/dimginterface.cpp b/src/utilities/imageeditor/canvas/dimginterface.cpp new file mode 100644 index 00000000..8ce1c114 --- /dev/null +++ b/src/utilities/imageeditor/canvas/dimginterface.cpp @@ -0,0 +1,1270 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-15 + * Description : DImg interface for image editor + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#define OPACITY 0.7 +#define RCOL 0xAA +#define GCOL 0xAA +#define BCOL 0xAA + +// C++ includes. + +#include <cmath> +#include <cstdio> +#include <cstdlib> +#include <iostream> + +// TQt includes. + +#include <tqwidget.h> +#include <tqimage.h> +#include <tqpixmap.h> +#include <tqbitmap.h> +#include <tqcolor.h> +#include <tqfile.h> +#include <tqvariant.h> + +// KDE includes. + +#include <kcursor.h> +#include <tdemessagebox.h> + +// Local includes. + +#include "ddebug.h" +#include "bcgmodifier.h" +#include "colorcorrectiondlg.h" +#include "undomanager.h" +#include "undoaction.h" +#include "iccsettingscontainer.h" +#include "icctransform.h" +#include "exposurecontainer.h" +#include "iofilesettingscontainer.h" +#include "rawimport.h" +#include "editortooliface.h" +#include "sharedloadsavethread.h" +#include "dmetadata.h" +#include "dimginterface.h" +#include "dimginterface.moc" + +namespace Digikam +{ + +class UndoManager; + +class DImgInterfacePrivate +{ + +public: + + DImgInterfacePrivate() + { + parent = 0; + undoMan = 0; + cmSettings = 0; + expoSettings = 0; + iofileSettings = 0; + thread = 0; + width = 0; + height = 0; + origWidth = 0; + origHeight = 0; + selX = 0; + selY = 0; + selW = 0; + selH = 0; + zoom = 1.0; + exifOrient = false; + valid = false; + rotatedOrFlipped = false; + } + + bool valid; + bool rotatedOrFlipped; + bool exifOrient; + bool changedBCG; + + int width; + int height; + int origWidth; + int origHeight; + int selX; + int selY; + int selW; + int selH; + + float gamma; + float brightness; + float contrast; + + double zoom; + + // Used by ICC color profile dialog. + TQWidget *parent; + + TQString filename; + TQString savingFilename; + + DImg image; + + UndoManager *undoMan; + + BCGModifier cmod; + + ICCSettingsContainer *cmSettings; + + ExposureSettingsContainer *expoSettings; + + IOFileSettingsContainer *iofileSettings; + + SharedLoadSaveThread *thread; + + IccTransform monitorICCtrans; +}; + +DImgInterface* DImgInterface::m_defaultInterface = 0; + +DImgInterface* DImgInterface::defaultInterface() +{ + return m_defaultInterface; +} + +void DImgInterface::setDefaultInterface(DImgInterface *defaultInterface) +{ + m_defaultInterface = defaultInterface; +} + +DImgInterface::DImgInterface() + : TQObject() +{ + d = new DImgInterfacePrivate; + + d->undoMan = new UndoManager(this); + d->thread = new SharedLoadSaveThread; + + connect( d->thread, TQ_SIGNAL(signalImageLoaded(const LoadingDescription &, const DImg&)), + this, TQ_SLOT(slotImageLoaded(const LoadingDescription &, const DImg&)) ); + + connect( d->thread, TQ_SIGNAL(signalImageSaved(const TQString&, bool)), + this, TQ_SLOT(slotImageSaved(const TQString&, bool)) ); + + connect( d->thread, TQ_SIGNAL(signalLoadingProgress(const LoadingDescription &, float)), + this, TQ_SLOT(slotLoadingProgress(const LoadingDescription &, float)) ); + + connect( d->thread, TQ_SIGNAL(signalSavingProgress(const TQString&, float)), + this, TQ_SLOT(slotSavingProgress(const TQString&, float)) ); +} + +DImgInterface::~DImgInterface() +{ + delete d->undoMan; + delete d->thread; + delete d; + if (m_defaultInterface == this) + m_defaultInterface = 0; +} + +void DImgInterface::load(const TQString& filename, IOFileSettingsContainer *iofileSettings, + TQWidget *parent) +{ + // store here in case filename == d->fileName, and is then reset by resetValues + TQString newFileName = filename; + + resetValues(); + + d->filename = newFileName; + d->iofileSettings = iofileSettings; + d->parent = parent; + + if (d->iofileSettings->useRAWImport && DImg::fileFormat(d->filename) == DImg::RAW) + { + RawImport *rawImport = new RawImport(KURL(d->filename), this); + EditorToolIface::editorToolIface()->loadTool(rawImport); + + connect(rawImport, TQ_SIGNAL(okClicked()), + this, TQ_SLOT(slotUseRawImportSettings())); + + connect(rawImport, TQ_SIGNAL(cancelClicked()), + this, TQ_SLOT(slotUseDefaultSettings())); + } + else + { + slotUseDefaultSettings(); + } +} + +void DImgInterface::slotUseRawImportSettings() +{ + RawImport *rawImport = dynamic_cast<RawImport*>(EditorToolIface::editorToolIface()->currentTool()); + + d->thread->load(LoadingDescription(d->filename, + rawImport->rawDecodingSettings()), + SharedLoadSaveThread::AccessModeReadWrite, + SharedLoadSaveThread::LoadingPolicyFirstRemovePrevious); + emit signalLoadingStarted(d->filename); + + EditorToolIface::editorToolIface()->unLoadTool(); +} + +void DImgInterface::slotUseDefaultSettings() +{ + d->thread->load(LoadingDescription(d->filename, + d->iofileSettings->rawDecodingSettings), + SharedLoadSaveThread::AccessModeReadWrite, + SharedLoadSaveThread::LoadingPolicyFirstRemovePrevious); + emit signalLoadingStarted(d->filename); + + EditorToolIface::editorToolIface()->unLoadTool(); +} + +void DImgInterface::resetImage() +{ + EditorToolIface::editorToolIface()->unLoadTool(); + resetValues(); + d->image.reset(); +} + +void DImgInterface::resetValues() +{ + d->valid = false; + d->filename = TQString(); + d->width = 0; + d->height = 0; + d->origWidth = 0; + d->origHeight = 0; + d->selX = 0; + d->selY = 0; + d->selW = 0; + d->selH = 0; + d->gamma = 1.0; + d->contrast = 1.0; + d->brightness = 0.0; + d->changedBCG = false; + d->iofileSettings = 0; + d->parent = 0; + + d->cmod.reset(); + d->undoMan->clear(); +} + +void DImgInterface::setICCSettings(ICCSettingsContainer *cmSettings) +{ + d->cmSettings = cmSettings; + d->monitorICCtrans.setProfiles(d->cmSettings->workspaceSetting, d->cmSettings->monitorSetting); +} + +void DImgInterface::setExposureSettings(ExposureSettingsContainer *expoSettings) +{ + d->expoSettings = expoSettings; +} + +void DImgInterface::slotImageLoaded(const LoadingDescription &loadingDescription, const DImg& img) +{ + const TQString &fileName = loadingDescription.filePath; + + if (fileName != d->filename) + return; + + bool valRet = false; + d->image = img; + + if (!d->image.isNull()) + { + d->origWidth = d->image.width(); + d->origHeight = d->image.height(); + d->valid = true; + d->width = d->origWidth; + d->height = d->origHeight; + valRet = true; + + // Raw files are already rotated properlly by dcraw. Only perform auto-rotation with JPEG/PNG/TIFF file. + // We don't have a feedback from dcraw about auto-rotated RAW file during decoding. Well set transformed + // flag as well. + + if (d->image.attribute("format").toString() == TQString("RAW")) + d->rotatedOrFlipped = true; + + if (d->exifOrient && + (d->image.attribute("format").toString() == TQString("JPEG") || + d->image.attribute("format").toString() == TQString("PNG") || + d->image.attribute("format").toString() == TQString("TIFF"))) + exifRotate(d->filename); + + if (d->cmSettings->enableCMSetting) + { + if (TQFile::exists(d->cmSettings->workspaceSetting)) + { + IccTransform trans; + TQByteArray fakeProfile; + + // First possibility: image has no embedded profile + if(d->image.getICCProfil().isNull()) + { + // Ask or apply? + if (d->cmSettings->askOrApplySetting) + { + if (d->parent) d->parent->setCursor( KCursor::waitCursor() ); + trans.setProfiles(TQFile::encodeName(d->cmSettings->inputSetting), + TQFile::encodeName(d->cmSettings->workspaceSetting)); + + // NOTE: If Input color profile do not exist, using built-in sRGB intead. + trans.apply(d->image, fakeProfile, d->cmSettings->renderingSetting, + d->cmSettings->BPCSetting, false, + TQFile::exists(d->cmSettings->inputSetting)); + + d->image.getICCProfilFromFile(TQFile::encodeName(d->cmSettings->workspaceSetting)); + if (d->parent) d->parent->unsetCursor(); + } + else + { + // To repaint image in canvas before to ask about to apply ICC profile. + emit signalImageLoaded(d->filename, valRet); + + DImg preview = d->image.smoothScale(240, 180, TQSize::ScaleMin); + trans.setProfiles(TQFile::encodeName(d->cmSettings->inputSetting), + TQFile::encodeName(d->cmSettings->workspaceSetting)); + ColorCorrectionDlg dlg(d->parent, &preview, &trans, fileName); + + switch (dlg.exec()) + { + case TQDialog::Accepted: + if (d->parent) d->parent->setCursor( KCursor::waitCursor() ); + + // NOTE: If Input color profile do not exist, using built-in sRGB intead. + trans.apply(d->image, fakeProfile, d->cmSettings->renderingSetting, + d->cmSettings->BPCSetting, false, + TQFile::exists(d->cmSettings->inputSetting)); + + d->image.getICCProfilFromFile(TQFile::encodeName(d->cmSettings->workspaceSetting)); + if (d->parent) d->parent->unsetCursor(); + break; + case -1: + if (d->parent) d->parent->setCursor( KCursor::waitCursor() ); + d->image.getICCProfilFromFile(TQFile::encodeName(d->cmSettings->workspaceSetting)); + if (d->parent) d->parent->unsetCursor(); + DDebug() << "dimginterface.cpp: Apply pressed" << endl; + break; + } + } + } + // Second possibility: image has an embedded profile + else + { + trans.getEmbeddedProfile( d->image ); + + // Ask or apply? + if (d->cmSettings->askOrApplySetting) + { + if (d->parent) d->parent->setCursor( KCursor::waitCursor() ); + trans.setProfiles(TQFile::encodeName(d->cmSettings->workspaceSetting)); + trans.apply(d->image, fakeProfile, d->cmSettings->renderingSetting, + d->cmSettings->BPCSetting, false, false); + if (d->parent) d->parent->unsetCursor(); + } + else + { + if (trans.getEmbeddedProfileDescriptor() + != trans.getProfileDescription( d->cmSettings->workspaceSetting )) + { + // Embedded profile and default workspace profile are different: ask to user! + + DDebug() << "Embedded profile: " << trans.getEmbeddedProfileDescriptor() << endl; + + // To repaint image in canvas before to ask about to apply ICC profile. + emit signalImageLoaded(d->filename, valRet); + + DImg preview = d->image.smoothScale(240, 180, TQSize::ScaleMin); + trans.setProfiles(TQFile::encodeName(d->cmSettings->workspaceSetting)); + ColorCorrectionDlg dlg(d->parent, &preview, &trans, fileName); + + switch (dlg.exec()) + { + case TQDialog::Accepted: + if (d->parent) d->parent->setCursor( KCursor::waitCursor() ); + trans.apply(d->image, fakeProfile, d->cmSettings->renderingSetting, + d->cmSettings->BPCSetting, false, false); + d->image.getICCProfilFromFile(TQFile::encodeName(d->cmSettings->workspaceSetting)); + if (d->parent) d->parent->unsetCursor(); + break; + case -1: + if (d->parent) d->parent->setCursor( KCursor::waitCursor() ); + d->image.getICCProfilFromFile(TQFile::encodeName(d->cmSettings->workspaceSetting)); + if (d->parent) d->parent->unsetCursor(); + DDebug() << "dimginterface.cpp: Apply pressed" << endl; + break; + } + } + } + } + } + else + { + TQString message = i18n("Cannot find the ICC color-space profile file. " + "The ICC profiles path seems to be invalid. " + "No color transform will be applied. " + "Please check the \"Color Management\" " + "configuration in digiKam's setup to verify the ICC path."); + KMessageBox::information(d->parent, message); + } + } + } + else + { + valRet = false; + } + + emit signalImageLoaded(d->filename, valRet); + setModified(); +} + +void DImgInterface::slotLoadingProgress(const LoadingDescription &loadingDescription, float progress) +{ + if (loadingDescription.filePath == d->filename) + emit signalLoadingProgress(loadingDescription.filePath, progress); +} + +bool DImgInterface::exifRotated() +{ + return d->rotatedOrFlipped; +} + +void DImgInterface::exifRotate(const TQString& filename) +{ + // Rotate image based on EXIF rotate tag + + DMetadata metadata(filename); + DMetadata::ImageOrientation orientation = metadata.getImageOrientation(); + + if(orientation != DMetadata::ORIENTATION_NORMAL) + { + switch (orientation) + { + case DMetadata::ORIENTATION_NORMAL: + case DMetadata::ORIENTATION_UNSPECIFIED: + break; + + case DMetadata::ORIENTATION_HFLIP: + flipHoriz(false); + break; + + case DMetadata::ORIENTATION_ROT_180: + rotate180(false); + break; + + case DMetadata::ORIENTATION_VFLIP: + flipVert(false); + break; + + case DMetadata::ORIENTATION_ROT_90_HFLIP: + rotate90(false); + flipHoriz(false); + break; + + case DMetadata::ORIENTATION_ROT_90: + rotate90(false); + break; + + case DMetadata::ORIENTATION_ROT_90_VFLIP: + rotate90(false); + flipVert(false); + break; + + case DMetadata::ORIENTATION_ROT_270: + rotate270(false); + break; + } + + d->rotatedOrFlipped = true; + } +} + +void DImgInterface::setExifOrient(bool exifOrient) +{ + d->exifOrient = exifOrient; +} + +void DImgInterface::undo() +{ + if (!d->undoMan->anyMoreUndo()) + { + emit signalUndoStateChanged(false, d->undoMan->anyMoreRedo(), !d->undoMan->isAtOrigin()); + return; + } + + d->undoMan->undo(); + emit signalUndoStateChanged(d->undoMan->anyMoreUndo(), d->undoMan->anyMoreRedo(), !d->undoMan->isAtOrigin()); +} + +void DImgInterface::redo() +{ + if (!d->undoMan->anyMoreRedo()) + { + emit signalUndoStateChanged(d->undoMan->anyMoreUndo(), false, !d->undoMan->isAtOrigin()); + return; + } + + d->undoMan->redo(); + emit signalUndoStateChanged(d->undoMan->anyMoreUndo(), d->undoMan->anyMoreRedo(), !d->undoMan->isAtOrigin()); +} + +void DImgInterface::restore() +{ + d->undoMan->clear(); + load(d->filename, d->iofileSettings); +} + +/* +This code is unused and untested +void DImgInterface::save(const TQString& file, IOFileSettingsContainer *iofileSettings) +{ + d->cmod.reset(); + d->cmod.setGamma(d->gamma); + d->cmod.setBrightness(d->brightness); + d->cmod.setContrast(d->contrast); + d->cmod.applyBCG(d->image); + + d->cmod.reset(); + d->gamma = 1.0; + d->contrast = 1.0; + d->brightness = 0.0; + + TQString currentMimeType(TQImageIO::imageFormat(d->filename)); + + d->needClearUndoManager = true; + + saveAction(file, iofileSettings, currentMimeType); +} +*/ + +void DImgInterface::saveAs(const TQString& fileName, IOFileSettingsContainer *iofileSettings, + bool setExifOrientationTag, const TQString& givenMimeType) +{ + // No need to toggle off undo, redo or save action during saving using + // signalUndoStateChanged(), this is will done by GUI implementation directly. + + if (d->changedBCG) + { + d->cmod.reset(); + d->cmod.setGamma(d->gamma); + d->cmod.setBrightness(d->brightness); + d->cmod.setContrast(d->contrast); + d->cmod.applyBCG(d->image); + } + + // Try hard to find a mimetype. + TQString mimeType = givenMimeType; + + // This is possibly empty + if (mimeType.isEmpty()) + mimeType = getImageFormat(); + + DDebug() << "Saving to :" << TQFile::encodeName(fileName).data() << " (" + << mimeType << ")" << endl; + + // JPEG file format. + if ( mimeType.upper() == TQString("JPG") || mimeType.upper() == TQString("JPEG") || + mimeType.upper() == TQString("JPE")) + { + d->image.setAttribute("quality", iofileSettings->JPEGCompression); + d->image.setAttribute("subsampling", iofileSettings->JPEGSubSampling); + } + + // PNG file format. + if ( mimeType.upper() == TQString("PNG") ) + d->image.setAttribute("quality", iofileSettings->PNGCompression); + + // TIFF file format. + if ( mimeType.upper() == TQString("TIFF") || mimeType.upper() == TQString("TIF") ) + d->image.setAttribute("compress", iofileSettings->TIFFCompression); + + // JPEG 2000 file format. + if ( mimeType.upper() == TQString("JP2") || mimeType.upper() == TQString("JPX") || + mimeType.upper() == TQString("JPC") || mimeType.upper() == TQString("PGX")) + { + if (iofileSettings->JPEG2000LossLess) + d->image.setAttribute("quality", 100); // LossLess compression + else + d->image.setAttribute("quality", iofileSettings->JPEG2000Compression); + } + + d->savingFilename = fileName; + + // Get image Exif/Iptc data. + DMetadata meta; + meta.setExif(d->image.getExif()); + meta.setIptc(d->image.getIptc()); + + // Update Iptc preview. + // NOTE: see B.K.O #130525. a JPEG segment is limited to 64K. If the IPTC byte array is + // bigger than 64K duing of image preview tag size, the target JPEG image will be + // broken. Note that IPTC image preview tag is limited to 256K!!! + // There is no limitation with TIFF and PNG about IPTC byte array size. + + TQImage preview = d->image.smoothScale(1280, 1024, TQSize::ScaleMin).copyTQImage(); + + if ( mimeType.upper() != TQString("JPG") && mimeType.upper() != TQString("JPEG") && + mimeType.upper() != TQString("JPE")) + { + // Non JPEG file, we update IPTC preview + meta.setImagePreview(preview); + } + else + { + // JPEG file, we remove IPTC preview. + meta.removeIptcTag("Iptc.Application2.Preview"); + meta.removeIptcTag("Iptc.Application2.PreviewFormat"); + meta.removeIptcTag("Iptc.Application2.PreviewVersion"); + } + + // Update Exif thumbnail. + TQImage thumb = preview.smoothScale(160, 120, TQImage::ScaleMin); + meta.setExifThumbnail(thumb); + + // Update Exif Image dimensions. + meta.setImageDimensions(d->image.size()); + + // Update Exif Document Name tag with the original file name. + meta.setExifTagString("Exif.Image.DocumentName", getImageFileName()); + + // Update Exif Orientation tag if necessary. + if( setExifOrientationTag ) + meta.setImageOrientation(DMetadata::ORIENTATION_NORMAL); + + // Store new Exif/Iptc data into image. + d->image.setExif(meta.getExif()); + d->image.setIptc(meta.getIptc()); + + d->thread->save(d->image, fileName, mimeType); +} + +void DImgInterface::slotImageSaved(const TQString& filePath, bool success) +{ + if (filePath != d->savingFilename) + return; + + if (!success) + DWarning() << "error saving image '" << TQFile::encodeName(filePath).data() << endl; + + emit signalImageSaved(filePath, success); + emit signalUndoStateChanged(d->undoMan->anyMoreUndo(), d->undoMan->anyMoreRedo(), !d->undoMan->isAtOrigin()); +} + +void DImgInterface::slotSavingProgress(const TQString& filePath, float progress) +{ + if (filePath == d->savingFilename) + emit signalSavingProgress(filePath, progress); +} + +void DImgInterface::abortSaving() +{ + // failure will be reported by a signal + d->thread->stopSaving(d->savingFilename); +} + +void DImgInterface::switchToLastSaved(const TQString& newFilename) +{ + // Higher level wants to use the current DImg object to represent the file + // it has previously been saved to. + d->filename = newFilename; + + // Currently the only place where a DImg is connected to the file it originates from + // is the format attribute. + TQString savedformat = d->image.attribute("savedformat").toString(); + if (!savedformat.isEmpty()) + d->image.setAttribute("format", savedformat); +} + +void DImgInterface::setModified() +{ + emit signalModified(); + emit signalUndoStateChanged(d->undoMan->anyMoreUndo(), d->undoMan->anyMoreRedo(), !d->undoMan->isAtOrigin()); +} + +void DImgInterface::readMetadataFromFile(const TQString &file) +{ + DMetadata meta(file); + + //TODO: code is essentially the same as DImgLoader::readMetadata, + // put both in a common place. + if (!meta.getComments().isNull()) + d->image.setComments(meta.getComments()); + if (!meta.getExif().isNull()) + d->image.setExif(meta.getExif()); + if (!meta.getIptc().isNull()) + d->image.setIptc(meta.getIptc()); +} + +void DImgInterface::clearUndoManager() +{ + d->undoMan->clear(); + d->undoMan->setOrigin(); + emit signalUndoStateChanged(false, false, false); +} + +void DImgInterface::setUndoManagerOrigin() +{ + d->undoMan->setOrigin(); + emit signalUndoStateChanged(d->undoMan->anyMoreUndo(), d->undoMan->anyMoreRedo(), !d->undoMan->isAtOrigin()); +} + +void DImgInterface::updateUndoState() +{ + emit signalUndoStateChanged(d->undoMan->anyMoreUndo(), d->undoMan->anyMoreRedo(), !d->undoMan->isAtOrigin()); +} + +bool DImgInterface::imageValid() +{ + return !d->image.isNull(); +} + +int DImgInterface::width() +{ + return d->width; +} + +int DImgInterface::height() +{ + return d->height; +} + +int DImgInterface::origWidth() +{ + return d->origWidth; +} + +int DImgInterface::origHeight() +{ + return d->origHeight; +} + +int DImgInterface::bytesDepth() +{ + return d->image.bytesDepth(); +} + +bool DImgInterface::sixteenBit() +{ + return d->image.sixteenBit(); +} + +bool DImgInterface::hasAlpha() +{ + return d->image.hasAlpha(); +} + +bool DImgInterface::isReadOnly() +{ + if (d->image.isNull()) + return true; + else + return d->image.isReadOnly(); +} + +void DImgInterface::setSelectedArea(int x, int y, int w, int h) +{ + d->selX = x; + d->selY = y; + d->selW = w; + d->selH = h; +} + +void DImgInterface::getSelectedArea(int& x, int& y, int& w, int& h) +{ + x = d->selX; + y = d->selY; + w = d->selW; + h = d->selH; +} + +void DImgInterface::paintOnDevice(TQPaintDevice* p, + int sx, int sy, int sw, int sh, + int dx, int dy, int dw, int dh, + int /*antialias*/) +{ + if (d->image.isNull()) + return; + + DImg img = d->image.smoothScaleSection(sx, sy, sw, sh, dw, dh); + d->cmod.applyBCG(img); + img.convertDepth(32); + + if (d->cmSettings->enableCMSetting && d->cmSettings->managedViewSetting) + { + TQPixmap pix(img.convertToPixmap(&d->monitorICCtrans)); + bitBlt(p, dx, dy, &pix, 0, 0); + } + else + { + TQPixmap pix(img.convertToPixmap()); + bitBlt(p, dx, dy, &pix, 0, 0); + } + + // Show the Over/Under exposure pixels indicators + + if (d->expoSettings->underExposureIndicator || d->expoSettings->overExposureIndicator) + { + TQImage pureColorMask = d->image.copy(sx, sy, sw, sh).pureColorMask(d->expoSettings); + TQPixmap pixMask(pureColorMask.scale(dw, dh)); + bitBlt(p, dx, dy, &pixMask, 0, 0); + } +} + +void DImgInterface::paintOnDevice(TQPaintDevice* p, + int sx, int sy, int sw, int sh, + int dx, int dy, int dw, int dh, + int mx, int my, int mw, int mh, + int /*antialias*/) +{ + if (d->image.isNull()) + return; + + DImg img = d->image.smoothScaleSection(sx, sy, sw, sh, dw, dh); + d->cmod.applyBCG(img); + img.convertDepth(32); + + uint* data = (uint*)img.bits(); + uchar r, g, b, a; + + for (int j=0; j < (int)img.height(); j++) + { + for (int i=0; i < (int)img.width(); i++) + { + if (i < (mx-dx) || i > (mx-dx+mw-1) || + j < (my-dy) || j > (my-dy+mh-1)) + { + a = (*data >> 24) & 0xff; + r = (*data >> 16) & 0xff; + g = (*data >> 8) & 0xff; + b = (*data ) & 0xff; + + r += (uchar)((RCOL - r) * OPACITY); + g += (uchar)((GCOL - g) * OPACITY); + b += (uchar)((BCOL - b) * OPACITY); + + *data = (a << 24) | (r << 16) | (g << 8) | b; + } + + data++; + } + } + + if (d->cmSettings->enableCMSetting && d->cmSettings->managedViewSetting) + { + TQPixmap pix(img.convertToPixmap(&d->monitorICCtrans)); + bitBlt(p, dx, dy, &pix, 0, 0); + } + else + { + TQPixmap pix(img.convertToPixmap()); + bitBlt(p, dx, dy, &pix, 0, 0); + } + + // Show the Over/Under exposure pixels indicators + + if (d->expoSettings->underExposureIndicator || d->expoSettings->overExposureIndicator) + { + TQImage pureColorMask = d->image.copy(sx, sy, sw, sh).pureColorMask(d->expoSettings); + TQPixmap pixMask(pureColorMask.scale(dw, dh)); + bitBlt(p, dx, dy, &pixMask, 0, 0); + } +} + +void DImgInterface::zoom(double val) +{ + d->zoom = val; + d->width = (int)(d->origWidth * val); + d->height = (int)(d->origHeight * val); +} + +void DImgInterface::rotate90(bool saveUndo) +{ + if (saveUndo) + { + d->undoMan->addAction(new UndoActionRotate(this, UndoActionRotate::R90)); + } + + d->image.rotate(DImg::ROT90); + d->origWidth = d->image.width(); + d->origHeight = d->image.height(); + + setModified(); +} + +void DImgInterface::rotate180(bool saveUndo) +{ + if (saveUndo) + { + d->undoMan->addAction(new UndoActionRotate(this, UndoActionRotate::R180)); + } + + d->image.rotate(DImg::ROT180); + d->origWidth = d->image.width(); + d->origHeight = d->image.height(); + + setModified(); +} + +void DImgInterface::rotate270(bool saveUndo) +{ + if (saveUndo) + { + d->undoMan->addAction(new UndoActionRotate(this, UndoActionRotate::R270)); + } + + d->image.rotate(DImg::ROT270); + d->origWidth = d->image.width(); + d->origHeight = d->image.height(); + + setModified(); +} + +void DImgInterface::flipHoriz(bool saveUndo) +{ + if (saveUndo) + { + d->undoMan->addAction(new UndoActionFlip(this, UndoActionFlip::Horizontal)); + } + + d->image.flip(DImg::HORIZONTAL); + + setModified(); +} + +void DImgInterface::flipVert(bool saveUndo) +{ + if (saveUndo) + { + d->undoMan->addAction(new UndoActionFlip(this, UndoActionFlip::Vertical)); + } + + d->image.flip(DImg::VERTICAL); + + setModified(); +} + +void DImgInterface::crop(int x, int y, int w, int h) +{ + d->undoMan->addAction(new UndoActionIrreversible(this, "Crop")); + + d->image.crop(x, y, w, h); + + d->origWidth = d->image.width(); + d->origHeight = d->image.height(); + + setModified(); +} + +void DImgInterface::resize(int w, int h) +{ + d->undoMan->addAction(new UndoActionIrreversible(this, "Resize")); + + d->image.resize(w, h); + + d->origWidth = d->image.width(); + d->origHeight = d->image.height(); + + setModified(); +} + +void DImgInterface::changeGamma(double gamma) +{ + d->undoMan->addAction(new UndoActionBCG(this, d->gamma, d->brightness, + d->contrast, gamma, d->brightness, + d->contrast)); + + d->gamma += gamma/10.0; + + d->cmod.reset(); + d->cmod.setGamma(d->gamma); + d->cmod.setBrightness(d->brightness); + d->cmod.setContrast(d->contrast); + d->changedBCG = true; + + setModified(); +} + +void DImgInterface::changeBrightness(double brightness) +{ + d->undoMan->addAction(new UndoActionBCG(this, d->gamma, d->brightness, + d->contrast, d->gamma, brightness, + d->contrast)); + + d->brightness += brightness/100.0; + + d->cmod.reset(); + d->cmod.setGamma(d->gamma); + d->cmod.setBrightness(d->brightness); + d->cmod.setContrast(d->contrast); + d->changedBCG = true; + + setModified(); +} + +void DImgInterface::changeContrast(double contrast) +{ + d->undoMan->addAction(new UndoActionBCG(this, d->gamma, d->brightness, + d->contrast, d->gamma, d->brightness, + contrast)); + + d->contrast += contrast/100.0; + + d->cmod.reset(); + d->cmod.setGamma(d->gamma); + d->cmod.setBrightness(d->brightness); + d->cmod.setContrast(d->contrast); + d->changedBCG = true; + + setModified(); +} + +void DImgInterface::changeBCG(double gamma, double brightness, double contrast) +{ + d->gamma = gamma; + d->brightness = brightness; + d->contrast = contrast; + + d->cmod.reset(); + d->cmod.setGamma(d->gamma); + d->cmod.setBrightness(d->brightness); + d->cmod.setContrast(d->contrast); + d->changedBCG = true; + + setModified(); +} + +void DImgInterface::setBCG(double brightness, double contrast, double gamma) +{ + d->undoMan->addAction(new UndoActionIrreversible(this, "Brightness, Contrast, Gamma")); + + d->cmod.reset(); + d->cmod.setGamma(gamma); + d->cmod.setBrightness(brightness); + d->cmod.setContrast(contrast); + d->cmod.applyBCG(d->image); + + d->cmod.reset(); + d->gamma = 1.0; + d->contrast = 1.0; + d->brightness = 0.0; + d->changedBCG = false; + + setModified(); +} + +void DImgInterface::convertDepth(int depth) +{ + d->undoMan->addAction(new UndoActionIrreversible(this, "Convert Color Depth")); + d->image.convertDepth(depth); + + setModified(); +} + +DImg* DImgInterface::getImg() +{ + if (!d->image.isNull()) + { + return &d->image; + } + else + { + DWarning() << k_funcinfo << "d->image is NULL" << endl; + return 0; + } +} + +uchar* DImgInterface::getImage() +{ + if (!d->image.isNull()) + { + return d->image.bits(); + } + else + { + DWarning() << k_funcinfo << "d->image is NULL" << endl; + return 0; + } +} + +void DImgInterface::putImage(const TQString &caller, uchar* data, int w, int h) +{ + putImage(caller, data, w, h, d->image.sixteenBit()); +} + +void DImgInterface::putImage(const TQString &caller, uchar* data, int w, int h, bool sixteenBit) +{ + d->undoMan->addAction(new UndoActionIrreversible(this, caller)); + putImage(data, w, h, sixteenBit); +} + +void DImgInterface::putImage(uchar* data, int w, int h) +{ + putImage(data, w, h, d->image.sixteenBit()); +} + +void DImgInterface::putImage(uchar* data, int w, int h, bool sixteenBit) +{ + if (d->image.isNull()) + { + DWarning() << k_funcinfo << "d->image is NULL" << endl; + return; + } + + if (!data) + { + DWarning() << k_funcinfo << "New image is NULL" << endl; + return; + } + + if (w == -1 && h == -1) + { + // New image size + w = d->origWidth; + h = d->origHeight; + } + else + { + // New image size == original size + d->origWidth = w; + d->origHeight = h; + } + + //DDebug() << k_funcinfo << data << " " << w << " " << h << endl; + d->image.putImageData(w, h, sixteenBit, d->image.hasAlpha(), data); + + setModified(); +} + +void DImgInterface::setEmbeddedICCToOriginalImage( TQString profilePath) +{ + if (d->image.isNull()) + { + DWarning() << k_funcinfo << "d->image is NULL" << endl; + return; + } + + DDebug() << k_funcinfo << "Embedding profile: " << profilePath << endl; + d->image.getICCProfilFromFile( TQFile::encodeName(profilePath)); + setModified(); +} + +uchar* DImgInterface::getImageSelection() +{ + if (!d->selW || !d->selH) + return 0; + + if (!d->image.isNull()) + { + DImg im = d->image.copy(d->selX, d->selY, d->selW, d->selH); + return im.stripImageData(); + } + + return 0; +} + +void DImgInterface::putImageSelection(const TQString &caller, uchar* data) +{ + if (!data || d->image.isNull()) + return; + + d->undoMan->addAction(new UndoActionIrreversible(this, caller)); + + d->image.bitBltImage(data, 0, 0, d->selW, d->selH, d->selX, d->selY, d->selW, d->selH, d->image.bytesDepth()); + + setModified(); +} + +void DImgInterface::getUndoHistory(TQStringList &titles) +{ + d->undoMan->getUndoHistory(titles); +} + +void DImgInterface::getRedoHistory(TQStringList &titles) +{ + d->undoMan->getRedoHistory(titles); +} + +TQByteArray DImgInterface::getEmbeddedICC() +{ + return d->image.getICCProfil(); +} + +TQByteArray DImgInterface::getExif() +{ + return d->image.getExif(); +} + +TQByteArray DImgInterface::getIptc() +{ + return d->image.getIptc(); +} + +TQString DImgInterface::getImageFilePath() +{ + return d->filename; +} + +TQString DImgInterface::getImageFileName() +{ + return d->filename.section( '/', -1 ); +} + +TQString DImgInterface::getImageFormat() +{ + if (d->image.isNull()) + return TQString(); + + TQString mimeType = d->image.attribute("format").toString(); + // It is a bug in the loader if format attribute is not given + if (mimeType.isEmpty()) + { + DWarning() << "DImg object does not contain attribute \"format\"" << endl; + mimeType = TQImageIO::imageFormat(d->filename); + } + return mimeType; +} + +ICCSettingsContainer* DImgInterface::getICCSettings() +{ + return d->cmSettings; +} + +TQPixmap DImgInterface::convertToPixmap(DImg& img) +{ + if (d->cmSettings->enableCMSetting && d->cmSettings->managedViewSetting) + return img.convertToPixmap(&d->monitorICCtrans); + + return img.convertToPixmap(); +} + +TQColor DImgInterface::underExposureColor() +{ + return d->expoSettings->underExposureColor; +} + +TQColor DImgInterface::overExposureColor() +{ + return d->expoSettings->overExposureColor; +} + +} // namespace Digikam + diff --git a/src/utilities/imageeditor/canvas/dimginterface.h b/src/utilities/imageeditor/canvas/dimginterface.h new file mode 100644 index 00000000..9a41eb05 --- /dev/null +++ b/src/utilities/imageeditor/canvas/dimginterface.h @@ -0,0 +1,200 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-15 + * Description : DImg interface for image editor + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef DIMGINTERFACE_H +#define DIMGINTERFACE_H + +// TQt includes. + +#include <tqobject.h> +#include <tqstring.h> + +// Local includes. + +#include "digikam_export.h" +#include "dimg.h" + +class TQWidget; +class TQPixmap; + +namespace Digikam +{ + +class ICCSettingsContainer; +class ExposureSettingsContainer; +class IOFileSettingsContainer; +class LoadingDescription; +class DImgInterfacePrivate; + +class DIGIKAM_EXPORT DImgInterface : public TQObject +{ + TQ_OBJECT + + +public: + + static DImgInterface* defaultInterface(); + static void setDefaultInterface(DImgInterface *defaultInterface); + + DImgInterface(); + ~DImgInterface(); + + void load(const TQString& filename, IOFileSettingsContainer *iofileSettings, TQWidget *parent=0); + + void setICCSettings(ICCSettingsContainer *cmSettings); + void setExposureSettings(ExposureSettingsContainer *expoSettings); + void setExifOrient(bool exifOrient); + + void undo(); + void redo(); + void restore(); + + void saveAs(const TQString& file, IOFileSettingsContainer *iofileSettings, + bool setExifOrientationTag, const TQString& mimeType=TQString()); + + void switchToLastSaved(const TQString& newFilename); + void abortSaving(); + void setModified(); + void readMetadataFromFile(const TQString &file); + void clearUndoManager(); + void setUndoManagerOrigin(); + void updateUndoState(); + void resetImage(); + + void zoom(double val); + + void paintOnDevice(TQPaintDevice* p, + int sx, int sy, int sw, int sh, + int dx, int dy, int dw, int dh, + int antialias); + void paintOnDevice(TQPaintDevice* p, + int sx, int sy, int sw, int sh, + int dx, int dy, int dw, int dh, + int mx, int my, int mw, int mh, + int antialias); + + bool imageValid(); + int width(); + int height(); + int origWidth(); + int origHeight(); + int bytesDepth(); + bool hasAlpha(); + bool sixteenBit(); + bool exifRotated(); + bool isReadOnly(); + + void setSelectedArea(int x, int y, int w, int h); + void getSelectedArea(int& x, int& y, int& w, int& h); + + void rotate90(bool saveUndo=true); + void rotate180(bool saveUndo=true); + void rotate270(bool saveUndo=true); + + void flipHoriz(bool saveUndo=true); + void flipVert(bool saveUndo=true); + + void crop(int x, int y, int w, int h); + + void resize(int w, int h); + + void changeGamma(double gamma); + void changeBrightness(double brightness); + void changeContrast(double contrast); + void changeBCG(double gamma, double brightness, double contrast); + + void setBCG(double brightness, double contrast, double gamma); + + void convertDepth(int depth); + + void getUndoHistory(TQStringList &titles); + void getRedoHistory(TQStringList &titles); + + DImg* getImg(); + uchar* getImage(); + + void putImage(uchar* data, int w, int h); + void putImage(uchar* data, int w, int h, bool sixteenBit); + void putImage(const TQString &caller, uchar* data, int w, int h); + void putImage(const TQString &caller, uchar* data, int w, int h, bool sixteenBit); + + uchar* getImageSelection(); + void putImageSelection(const TQString &caller, uchar* data); + + void setEmbeddedICCToOriginalImage( TQString profilePath); + + /** Convert a DImg image to a pixmap for screen using color + managemed view if necessary */ + TQPixmap convertToPixmap(DImg& img); + + TQByteArray getEmbeddedICC(); + TQByteArray getExif(); + TQByteArray getIptc(); + + ICCSettingsContainer *getICCSettings(); + + TQString getImageFileName(); + TQString getImageFilePath(); + TQString getImageFormat(); + + TQColor underExposureColor(); + TQColor overExposureColor(); + +protected slots: + + void slotImageLoaded(const LoadingDescription &loadingDescription, const DImg& img); + void slotImageSaved(const TQString& filePath, bool success); + void slotLoadingProgress(const LoadingDescription &loadingDescription, float progress); + void slotSavingProgress(const TQString& filePath, float progress); + +signals: + + void signalModified(); + void signalUndoStateChanged(bool moreUndo, bool moreRedo, bool canSave); + void signalLoadingStarted(const TQString& filename); + void signalLoadingProgress(const TQString& filePath, float progress); + void signalImageLoaded(const TQString& filePath, bool success); + void signalSavingProgress(const TQString& filePath, float progress); + void signalImageSaved(const TQString& filePath, bool success); + +private slots: + + void slotUseRawImportSettings(); + void slotUseDefaultSettings(); + +private: + + void exifRotate(const TQString& filename); + void resetValues(); + +private: + + static DImgInterface *m_defaultInterface; + + DImgInterfacePrivate *d; +}; + +} // namespace Digikam + +#endif /* DIMGINTERFACE_H */ diff --git a/src/utilities/imageeditor/canvas/iccsettingscontainer.h b/src/utilities/imageeditor/canvas/iccsettingscontainer.h new file mode 100644 index 00000000..eadb12fb --- /dev/null +++ b/src/utilities/imageeditor/canvas/iccsettingscontainer.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-12-08 + * Description : ICC Settings Container. + * + * Copyright (C) 2005-2007 by F.J. Cruz <fj.cruz@supercable.es> + * Copyright (C) 2005-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef ICCSETTINGSCONTAINER_H +#define ICCSETTINGSCONTAINER_H + +// TQt includes. + +#include <tqstring.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT ICCSettingsContainer +{ + +public: + + ICCSettingsContainer() + { + enableCMSetting = false; // NOTE: by default, ICC color management is disable. + + askOrApplySetting = false; + BPCSetting = false; + managedViewSetting = false; + + renderingSetting = 0; + + workspaceSetting = TQString(); + monitorSetting = TQString(); + inputSetting = TQString(); + proofSetting = TQString(); + }; + + ~ICCSettingsContainer(){}; + +public: + + bool enableCMSetting; + + // FALSE -> apply + // TRUE -> ask + bool askOrApplySetting; + bool BPCSetting; + bool managedViewSetting; + + int renderingSetting; + + TQString workspaceSetting; + TQString monitorSetting; + TQString inputSetting; + TQString proofSetting; +}; + +} // namespace Digikam + +#endif // ICCSETTINGSCONTAINER_H diff --git a/src/utilities/imageeditor/canvas/imageplugin.cpp b/src/utilities/imageeditor/canvas/imageplugin.cpp new file mode 100644 index 00000000..b330cb5a --- /dev/null +++ b/src/utilities/imageeditor/canvas/imageplugin.cpp @@ -0,0 +1,68 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-04 + * Description : image plugins interface for image editor + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// Local includes. + +#include "editortool.h" +#include "editortooliface.h" +#include "imageplugin.h" +#include "imageplugin.moc" + +namespace Digikam +{ + +ImagePlugin::ImagePlugin(TQObject *parent, const char* name) + : TQObject(parent, name) +{ +} + +ImagePlugin::~ImagePlugin() +{ +} + +void ImagePlugin::setEnabledSelectionActions(bool) +{ +} + +void ImagePlugin::setEnabledActions(bool) +{ +} + +void ImagePlugin::loadTool(EditorTool* tool) +{ + EditorToolIface::editorToolIface()->loadTool(tool); + + connect(tool, TQ_SIGNAL(okClicked()), + this, TQ_SLOT(slotToolDone())); + + connect(tool, TQ_SIGNAL(cancelClicked()), + this, TQ_SLOT(slotToolDone())); +} + +void ImagePlugin::slotToolDone() +{ + EditorToolIface::editorToolIface()->unLoadTool(); +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/canvas/imageplugin.h b/src/utilities/imageeditor/canvas/imageplugin.h new file mode 100644 index 00000000..ef506256 --- /dev/null +++ b/src/utilities/imageeditor/canvas/imageplugin.h @@ -0,0 +1,70 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-04 + * Description : image plugins interface for image editor. + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_H +#define IMAGEPLUGIN_H + +// TQt includes. + +#include <tqobject.h> + +// KDE includes. + +#include <kxmlguiclient.h> + +// Local includes. + +#include "digikam_export.h" + +class TQWidget; + +namespace Digikam +{ + +class EditorTool; + +class DIGIKAM_EXPORT ImagePlugin : public TQObject, public KXMLGUIClient +{ + TQ_OBJECT + + +public: + + ImagePlugin(TQObject *parent, const char* name=0); + virtual ~ImagePlugin(); + + virtual void setEnabledSelectionActions(bool enable); + virtual void setEnabledActions(bool enable); + + void loadTool(EditorTool* tool); + +private slots: + + void slotToolDone(); +}; + +} //namespace Digikam + +#endif /* IMAGEPLUGIN_H */ + diff --git a/src/utilities/imageeditor/canvas/imagepluginloader.cpp b/src/utilities/imageeditor/canvas/imagepluginloader.cpp new file mode 100644 index 00000000..43c8a16c --- /dev/null +++ b/src/utilities/imageeditor/canvas/imagepluginloader.cpp @@ -0,0 +1,278 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-04 + * Description : image plugins loader for image editor. + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// KDE includes. + +#include <ktrader.h> +#include <tdeparts/componentfactory.h> +#include <tdeapplication.h> +#include <tdelocale.h> +#include <kxmlguiclient.h> + +// Local includes. + +#include "ddebug.h" +#include "splashscreen.h" +#include "imagepluginloader.h" + +namespace Digikam +{ + +// List of obsolete image plugins name. + +static const char* ObsoleteImagePluginsList[] = +{ + "digikamimageplugin_blowup", // Merged with "Resize" tool since 0.9.2. + "digikamimageplugin_solarize", // Renamed "ColorFx" since 0.9.2. + "digikamimageplugin_unsharp", // Merged with "Sharpen" tool since 0.9.2. + "digikamimageplugin_refocus", // Merged with "Sharpen" tool since 0.9.2. + "digikamimageplugin_despeckle", // Renamed "Noise Reduction" since 0.9.2. + "-1" +}; + +class ImagePluginLoaderPrivate +{ + +public: + + typedef TQPair<TQString, ImagePlugin*> PluginType; + typedef TQValueList< PluginType > PluginList; + +public: + + ImagePluginLoaderPrivate() + { + splash = 0; + + for (int i=0 ; TQString(ObsoleteImagePluginsList[i]) != TQString("-1") ; i++) + obsoleteImagePluginsList << ObsoleteImagePluginsList[i]; + } + + TQStringList obsoleteImagePluginsList; + + SplashScreen *splash; + + PluginList pluginList; +}; + +ImagePluginLoader* ImagePluginLoader::m_instance=0; + +ImagePluginLoader* ImagePluginLoader::instance() +{ + return m_instance; +} + +ImagePluginLoader::ImagePluginLoader(TQObject *parent, SplashScreen *splash) + : TQObject(parent) +{ + m_instance = this; + d = new ImagePluginLoaderPrivate; + d->splash = splash; + + TQStringList imagePluginsList2Load; + + TDETrader::OfferList offers = TDETrader::self()->query("Digikam/ImagePlugin"); + TDETrader::OfferList::ConstIterator iter; + + for (iter = offers.begin() ; iter != offers.end() ; ++iter) + { + KService::Ptr service = *iter; + if (!d->obsoleteImagePluginsList.contains(service->library())) + imagePluginsList2Load.append(service->library()); + } + + loadPluginsFromList(imagePluginsList2Load); +} + +ImagePluginLoader::~ImagePluginLoader() +{ + delete d; + m_instance = 0; +} + +void ImagePluginLoader::loadPluginsFromList(const TQStringList& list) +{ + if (d->splash) + d->splash->message(i18n("Loading Image Plugins")); + + TDETrader::OfferList offers = TDETrader::self()->query("Digikam/ImagePlugin"); + TDETrader::OfferList::ConstIterator iter; + + int cpt = 0; + + // Load plugin core at the first time. + + for(iter = offers.begin(); iter != offers.end(); ++iter) + { + KService::Ptr service = *iter; + ImagePlugin *plugin; + + if (service->library() == "digikamimageplugin_core") + { + if (!pluginIsLoaded(service->name()) ) + { + int error=-1; + plugin = KParts::ComponentFactory::createInstanceFromService<ImagePlugin>( + service, this, service->name().local8Bit(), 0, &error); + + if (plugin && (dynamic_cast<KXMLGUIClient*>(plugin) != 0)) + { + d->pluginList.append(ImagePluginLoaderPrivate::PluginType(service->name(), plugin)); + + DDebug() << "ImagePluginLoader: Loaded plugin " << service->name() << endl; + + ++cpt; + } + else + { + DWarning() << "ImagePluginLoader:: createInstanceFromLibrary returned 0 for " + << service->name() + << " (" << service->library() << ")" + << " with error code " + << error << endl; + if (error == KParts::ComponentFactory::ErrNoLibrary) + DWarning() << "KLibLoader says: " + << KLibLoader::self()->lastErrorMessage() << endl; + } + } + break; + } + } + + // Load all other image plugins after (make a coherant menu construction in Image Editor). + + for (iter = offers.begin(); iter != offers.end(); ++iter) + { + KService::Ptr service = *iter; + ImagePlugin *plugin; + + if (!list.contains(service->library()) && service->library() != "digikamimageplugin_core") + { + if ((plugin = pluginIsLoaded(service->name())) != NULL) + d->pluginList.remove(ImagePluginLoaderPrivate::PluginType(service->name(),plugin)); + } + else + { + if( pluginIsLoaded(service->name()) ) + continue; + else + { + int error=-1; + plugin = KParts::ComponentFactory::createInstanceFromService<ImagePlugin>( + service, this, service->name().local8Bit(), 0); + + if (plugin && (dynamic_cast<KXMLGUIClient*>(plugin) != 0)) + { + d->pluginList.append(ImagePluginLoaderPrivate::PluginType(service->name(), plugin)); + + DDebug() << "ImagePluginLoader: Loaded plugin " << service->name() << endl; + + ++cpt; + } + else + { + DWarning() << "ImagePluginLoader:: createInstanceFromLibrary returned 0 for " + << service->name() + << " (" << service->library() << ")" + << " with error code " + << error << endl; + if (error == KParts::ComponentFactory::ErrNoLibrary) + DWarning() << "KLibLoader says: " + << KLibLoader::self()->lastErrorMessage() << endl; + } + } + } + } + + d->splash = 0; // Splashcreen is only lanched at the first time. + // If user change plugins list to use in setup, don't try to + // use the old splashscreen instance. +} + +ImagePlugin* ImagePluginLoader::pluginIsLoaded(const TQString& name) +{ + if ( d->pluginList.isEmpty() ) + return 0; + + for (ImagePluginLoaderPrivate::PluginList::iterator it = d->pluginList.begin(); + it != d->pluginList.end(); ++it) + { + if ((*it).first == name ) + return (*it).second; + } + + return 0; +} + +ImagePlugin* ImagePluginLoader::pluginInstance(const TQString& libraryName) +{ + TDETrader::OfferList offers = TDETrader::self()->query("Digikam/ImagePlugin"); + TDETrader::OfferList::ConstIterator iter; + + for(iter = offers.begin(); iter != offers.end(); ++iter) + { + KService::Ptr service = *iter; + + if(service->library() == libraryName) + { + return ( pluginIsLoaded(service->name()) ); + } + } + + return 0; +} + +bool ImagePluginLoader::pluginLibraryIsLoaded(const TQString& libraryName) +{ + TDETrader::OfferList offers = TDETrader::self()->query("Digikam/ImagePlugin"); + TDETrader::OfferList::ConstIterator iter; + + for(iter = offers.begin(); iter != offers.end(); ++iter) + { + KService::Ptr service = *iter; + + if(service->library() == libraryName) + { + if( pluginIsLoaded(service->name()) ) + return true; + } + } + + return false; +} + +TQPtrList<ImagePlugin> ImagePluginLoader::pluginList() +{ + TQPtrList<ImagePlugin> list; + + for (ImagePluginLoaderPrivate::PluginList::iterator it = d->pluginList.begin(); + it != d->pluginList.end(); ++it) + { + list.append((*it).second); + } + + return list; +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/canvas/imagepluginloader.h b/src/utilities/imageeditor/canvas/imagepluginloader.h new file mode 100644 index 00000000..55ffe933 --- /dev/null +++ b/src/utilities/imageeditor/canvas/imagepluginloader.h @@ -0,0 +1,79 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-04 + * Description : image plugins loader for image editor. + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGINLOADER_H +#define IMAGEPLUGINLOADER_H + +// TQt includes. + +#include <tqobject.h> +#include <tqptrlist.h> +#include <tqstring.h> +#include <tqvaluelist.h> +#include <tqpair.h> + +// Local includes. + +#include "digikam_export.h" +#include "imageplugin.h" + +namespace Digikam +{ + +class SplashScreen; +class ImagePluginLoaderPrivate; + +class DIGIKAM_EXPORT ImagePluginLoader : public TQObject +{ + +public: + + ImagePluginLoader(TQObject *parent, SplashScreen *splash=0); + ~ImagePluginLoader(); + + static ImagePluginLoader* instance(); + + TQPtrList<ImagePlugin> pluginList(); + void loadPluginsFromList(const TQStringList& list); + + // Return true if plugin library is loaded in memory. + // 'libraryName' is internal plugin library name not i18n. + bool pluginLibraryIsLoaded(const TQString& libraryName); + + ImagePlugin* pluginInstance(const TQString& libraryName); + +private: + + ImagePlugin* pluginIsLoaded(const TQString& name); + +private: + + static ImagePluginLoader *m_instance; + + ImagePluginLoaderPrivate *d; +}; + +} // namespace Digikam + +#endif /* IMAGEPLUGINLOADER_H */ diff --git a/src/utilities/imageeditor/canvas/iofilesettingscontainer.h b/src/utilities/imageeditor/canvas/iofilesettingscontainer.h new file mode 100644 index 00000000..360299b3 --- /dev/null +++ b/src/utilities/imageeditor/canvas/iofilesettingscontainer.h @@ -0,0 +1,84 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-01-03 + * Description : IO file Settings Container. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef IOFILESETTINGSCONTAINER_H +#define IOFILESETTINGSCONTAINER_H + +// Local includes. + +#include "drawdecoding.h" +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT IOFileSettingsContainer +{ + +public: + + IOFileSettingsContainer() + { + JPEGCompression = 75; + JPEGSubSampling = 1; // Medium subsampling + PNGCompression = 9; + TIFFCompression = false; + JPEG2000Compression = 75; + JPEG2000LossLess = true; + useRAWImport = true; + }; + + ~IOFileSettingsContainer(){}; + +public: + + // JPEG quality value. + int JPEGCompression; + + // JPEG chroma subsampling value. + int JPEGSubSampling; + + // PNG compression value. + int PNGCompression; + + // TIFF deflat compression. + bool TIFFCompression; + + // JPEG2000 quality value. + int JPEG2000Compression; + + // JPEG2000 lossless compression. + bool JPEG2000LossLess; + + // Use Raw Import tool to load a RAW picture. + bool useRAWImport; + + // ------------------------------------------------------ + // RAW File decoding options : + + DRawDecoding rawDecodingSettings; +}; + +} // namespace Digikam + +#endif // IOFILESETTINGSCONTAINER_H diff --git a/src/utilities/imageeditor/canvas/undoaction.cpp b/src/utilities/imageeditor/canvas/undoaction.cpp new file mode 100644 index 00000000..bb4ff756 --- /dev/null +++ b/src/utilities/imageeditor/canvas/undoaction.cpp @@ -0,0 +1,185 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-06 + * Description : undo actions manager for image editor. + * + * Copyright (C) 2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005 by Joern Ahrens <joern.ahrens@kdemail.net> + * + * 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, 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. + * + * ============================================================ */ + +// Local includes. + +#include "ddebug.h" +#include "dimginterface.h" +#include "undoaction.h" + +namespace Digikam +{ + +UndoAction::UndoAction(DImgInterface* iface) + : m_iface(iface) +{ + m_title = i18n("unknown"); +} + +UndoAction::~UndoAction() +{ +} + +TQString UndoAction::getTitle() const +{ + return m_title; +} + +UndoActionRotate::UndoActionRotate(DImgInterface* iface, + UndoActionRotate::Angle angle) + : UndoAction(iface), m_angle(angle) +{ + switch(m_angle) + { + case(R90): + m_title = i18n("Rotate 90 Degrees"); + break; + case(R180): + m_title = i18n("Rotate 180 Degrees"); + break; + case(R270): + m_title = i18n("Rotate 270 Degrees"); + break; + } +} + +UndoActionRotate::~UndoActionRotate() +{ +} + +void UndoActionRotate::rollBack() +{ + switch(m_angle) + { + case(R90): + m_iface->rotate270(false); + return; + case(R180): + m_iface->rotate180(false); + return; + case(R270): + m_iface->rotate90(false); + return; + default: + DWarning() << "Unknown rotate angle specified" << endl; + } +} + +void UndoActionRotate::execute() +{ + switch(m_angle) + { + case R90: + m_iface->rotate90(false); + return; + case R180: + m_iface->rotate180(false); + return; + case R270: + m_iface->rotate270(false); + return; + default: + DWarning() << "Unknown rotate angle specified" << endl; + } +} + +UndoActionFlip::UndoActionFlip(DImgInterface* iface, + UndoActionFlip::Direction dir) + : UndoAction(iface), m_dir(dir) +{ + if(m_dir ==TQt::Horizontal) + m_title = i18n("Flip Horizontal"); + else if(m_dir ==TQt::Vertical) + m_title = i18n("Flip Vertical"); +} + +UndoActionFlip::~UndoActionFlip() +{ +} + +void UndoActionFlip::rollBack() +{ + switch(m_dir) + { + case TQt::Horizontal: + m_iface->flipHoriz(false); + return; + case TQt::Vertical: + m_iface->flipVert(false); + return; + default: + DWarning() << "Unknown flip direction specified" << endl; + } +} + +void UndoActionFlip::execute() +{ + rollBack(); +} + +UndoActionBCG::UndoActionBCG(DImgInterface* iface, + double oldGamma, double oldBrightness, + double oldContrast, double newGamma, + double newBrightness, double newContrast) + : UndoAction(iface), m_oldGamma(oldGamma), m_oldBrightness(oldBrightness), + m_oldContrast(oldContrast), m_newGamma(newGamma), m_newBrightness(newBrightness), + m_newContrast(newContrast) +{ + m_title = i18n("Brightness,Contrast,Gamma"); +} + +UndoActionBCG::~UndoActionBCG() +{ +} + +void UndoActionBCG::rollBack() +{ + m_iface->changeBCG(m_oldGamma, m_oldBrightness, m_oldContrast); +} + +void UndoActionBCG::execute() +{ + m_iface->changeBCG(m_newGamma, m_newBrightness, m_newContrast); +} + +UndoActionIrreversible::UndoActionIrreversible(DImgInterface* iface, + const TQString &title) + : UndoAction(iface) +{ + m_title = title; +} + +UndoActionIrreversible::~UndoActionIrreversible() +{ +} + +void UndoActionIrreversible::rollBack() +{ +} + +void UndoActionIrreversible::execute() +{ +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/canvas/undoaction.h b/src/utilities/imageeditor/canvas/undoaction.h new file mode 100644 index 00000000..5f29486e --- /dev/null +++ b/src/utilities/imageeditor/canvas/undoaction.h @@ -0,0 +1,144 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-06 + * Description : undo actions manager for image editor. + * + * Copyright (C) 2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005 by Joern Ahrens <joern.ahrens@kdemail.net> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef UNDOACTION_H +#define UNDOACTION_H + +// KDE includes. + +#include <tdelocale.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DImgInterface; + +class DIGIKAM_EXPORT UndoAction +{ + +public: + + UndoAction(DImgInterface* iface); + virtual ~UndoAction(); + + virtual void rollBack() = 0; + virtual void execute() = 0; + + TQString getTitle() const; + +protected: + + DImgInterface *m_iface; + TQString m_title; +}; + +class DIGIKAM_EXPORT UndoActionRotate : public UndoAction +{ + +public: + + enum Angle + { + R90, + R180, + R270 + }; + + UndoActionRotate(DImgInterface* iface, Angle angle); + ~UndoActionRotate(); + + void rollBack(); + void execute(); + +private: + + int m_angle; +}; + +class DIGIKAM_EXPORT UndoActionFlip : public UndoAction +{ + +public: + + enum Direction + { + Horizontal, + Vertical + }; + + UndoActionFlip(DImgInterface* iface, Direction dir); + ~UndoActionFlip(); + + void rollBack(); + void execute(); + +private: + + int m_dir; +}; + +class DIGIKAM_EXPORT UndoActionBCG : public UndoAction +{ + +public: + + UndoActionBCG(DImgInterface* iface, + double oldGamma, double oldBrightness, + double oldContrast, double newGamma, + double newBrightness, double newContrast); + ~UndoActionBCG(); + + void rollBack(); + void execute(); + +private: + + double m_oldGamma; + double m_oldBrightness; + double m_oldContrast; + double m_newGamma; + double m_newBrightness; + double m_newContrast; +}; + +class DIGIKAM_EXPORT UndoActionIrreversible : public UndoAction +{ + +public: + + UndoActionIrreversible(DImgInterface* iface, + const TQString &caller=i18n("Unknown")); + ~UndoActionIrreversible(); + + void rollBack(); + void execute(); +}; + +} // namespace Digikam + +#endif /* UNDOACTION_H */ diff --git a/src/utilities/imageeditor/canvas/undocache.cpp b/src/utilities/imageeditor/canvas/undocache.cpp new file mode 100644 index 00000000..5f36ae75 --- /dev/null +++ b/src/utilities/imageeditor/canvas/undocache.cpp @@ -0,0 +1,178 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-05 + * Description : undo cache manager for image editor + * + * Copyright (C) 2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005 by Joern Ahrens <joern.ahrens@kdemail.net> + * + * 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, 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. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include <unistd.h> +} + +// TQt includes. + +#include <tqcstring.h> +#include <tqstring.h> +#include <tqfile.h> +#include <tqdatastream.h> +#include <tqstringlist.h> + +// KDE includes. + +#include <kstandarddirs.h> +#include <tdeaboutdata.h> +#include <kinstance.h> +#include <tdeglobal.h> + +// Local includes. + +#include "ddebug.h" +#include "undocache.h" + +namespace Digikam +{ + +class UndoCachePriv +{ +public: + + TQString cachePrefix; + TQStringList cacheFilenames; +}; + +UndoCache::UndoCache() +{ + d = new UndoCachePriv; + + TQString cacheDir; + cacheDir = locateLocal("cache", + TDEGlobal::instance()->aboutData()->programName() + '/'); + + d->cachePrefix = TQString("%1undocache-%2") + .arg(cacheDir) + .arg(getpid()); +} + +UndoCache::~UndoCache() +{ + clear(); + delete d; +} + +/** + * delete all cache files + */ +void UndoCache::clear() +{ + for (TQStringList::iterator it = d->cacheFilenames.begin(); + it != d->cacheFilenames.end(); ++it) + { + ::unlink(TQFile::encodeName(*it)); + } + + d->cacheFilenames.clear(); +} + +/** + * write the data into a cache file + */ +bool UndoCache::putData(int level, int w, int h, int bytesDepth, uchar* data) +{ + TQString cacheFile = TQString("%1-%2.bin") + .arg(d->cachePrefix) + .arg(level); + + TQFile file(cacheFile); + + if (file.exists() || !file.open(IO_WriteOnly)) + return false; + + TQDataStream ds(&file); + ds << w; + ds << h; + ds << bytesDepth; + + TQByteArray ba(w*h*bytesDepth); + memcpy (ba.data(), data, w*h*bytesDepth); + ds << ba; + + file.close(); + + d->cacheFilenames.append(cacheFile); + + return true; +} + +/** + * get the data from a cache file + */ +uchar* UndoCache::getData(int level, int& w, int& h, int& bytesDepth, bool del) +{ + TQString cacheFile = TQString("%1-%2.bin") + .arg(d->cachePrefix) + .arg(level); + + TQFile file(cacheFile); + if (!file.open(IO_ReadOnly)) + return 0; + + TQDataStream ds(&file); + ds >> w; + ds >> h; + ds >> bytesDepth; + + uchar *data = new uchar[w*h*bytesDepth]; + if (!data) + return 0; + + TQByteArray ba(w*h*bytesDepth); + ds >> ba; + memcpy (data, ba.data(), w*h*bytesDepth); + + file.close(); + + if(del) + { + ::unlink(TQFile::encodeName(cacheFile)); + d->cacheFilenames.remove(cacheFile); + } + + return data; +} + +/** + * delete a cache file + */ +void UndoCache::erase(int level) +{ + TQString cacheFile = TQString("%1-%2.bin") + .arg(d->cachePrefix) + .arg(level); + + if(d->cacheFilenames.find(cacheFile) == d->cacheFilenames.end()) + return; + + ::unlink(TQFile::encodeName(cacheFile)); +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/canvas/undocache.h b/src/utilities/imageeditor/canvas/undocache.h new file mode 100644 index 00000000..732c7c3e --- /dev/null +++ b/src/utilities/imageeditor/canvas/undocache.h @@ -0,0 +1,62 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-05 + * Description : undo cache manager for image editor. + * + * Copyright (C) 2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005 by Joern Ahrens <joern.ahrens@kdemail.net> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef UNDOCACHE_H +#define UNDOCACHE_H + +// TQt includes. + +#include <tqglobal.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class UndoCachePriv; + +class DIGIKAM_EXPORT UndoCache +{ + +public: + + UndoCache(); + ~UndoCache(); + + void clear(); + bool putData(int level, int w, int h, int bytesDepth, uchar* data); + uchar *getData(int level, int& w, int& h, int& bytesDepth, bool del=true); + + void erase(int level); + +private: + + UndoCachePriv *d; +}; + +} // namespace Digikam + +#endif /* UNDOCACHE_H */ diff --git a/src/utilities/imageeditor/canvas/undomanager.cpp b/src/utilities/imageeditor/canvas/undomanager.cpp new file mode 100644 index 00000000..87085d5d --- /dev/null +++ b/src/utilities/imageeditor/canvas/undomanager.cpp @@ -0,0 +1,253 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-06 + * Description : an image editor actions undo/redo manager + * + * Copyright (C) 2005-2006 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005-2006 Joern Ahrens <joern.ahrens@kdemail.net> + * + * 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, 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. + * + * ============================================================ */ + +// C++ includes. + +#include <typeinfo> +#include <climits> + +// Local includes. + +#include "ddebug.h" +#include "dimginterface.h" +#include "undoaction.h" +#include "undocache.h" +#include "undomanager.h" + +namespace Digikam +{ + +class UndoManagerPriv +{ + +public: + + UndoManagerPriv() + { + dimgiface = 0; + undoCache = 0; + origin = 0; + } + + TQValueList<UndoAction*> undoActions; + TQValueList<UndoAction*> redoActions; + int origin; + + UndoCache *undoCache; + + DImgInterface *dimgiface; +}; + +UndoManager::UndoManager(DImgInterface* iface) +{ + d = new UndoManagerPriv; + d->dimgiface = iface; + d->undoCache = new UndoCache; +} + +UndoManager::~UndoManager() +{ + clear(true); + delete d->undoCache; + delete d; +} + +void UndoManager::addAction(UndoAction* action) +{ + if (!action) + return; + + // All redo actions are invalid now + clearRedoActions(); + + d->undoActions.push_back(action); + + if (typeid(*action) == typeid(UndoActionIrreversible)) + { + int w = d->dimgiface->origWidth(); + int h = d->dimgiface->origHeight(); + int bytesDepth = d->dimgiface->bytesDepth(); + uchar* data = d->dimgiface->getImage(); + + d->undoCache->putData(d->undoActions.size(), w, h, bytesDepth, data); + } + + // if origin is at one of the redo action that are now invalid, + // it is no longer reachable + if (d->origin < 0) + d->origin = INT_MAX; + else + d->origin++; +} + +void UndoManager::undo() +{ + if (d->undoActions.isEmpty()) + return; + + UndoAction* action = d->undoActions.back(); + + if (typeid(*action) == typeid(UndoActionIrreversible)) + { + // Save the current state for the redo operation + + int w = d->dimgiface->origWidth(); + int h = d->dimgiface->origHeight(); + int bytesDepth = d->dimgiface->bytesDepth(); + uchar* data = d->dimgiface->getImage(); + + d->undoCache->putData(d->undoActions.size() + 1, w, h, bytesDepth, data); + + // And now, undo the action + + int newW, newH, newBytesDepth; + uchar *newData = d->undoCache->getData(d->undoActions.size(), newW, newH, newBytesDepth, false); + if (newData) + { + d->dimgiface->putImage(newData, newW, newH, newBytesDepth == 8 ? true : false); + delete [] newData; + } + } + else + { + action->rollBack(); + } + + d->undoActions.pop_back(); + d->redoActions.push_back(action); + d->origin--; +} + +void UndoManager::redo() +{ + if(d->redoActions.isEmpty()) + return; + + UndoAction *action = d->redoActions.back(); + + if(typeid(*action) == typeid(UndoActionIrreversible)) + { + int w, h, bytesDepth; + uchar *data = d->undoCache->getData(d->undoActions.size() + 2, w, h, bytesDepth, false); + if (data) + { + d->dimgiface->putImage(data, w, h, bytesDepth == 8 ? true : false); + delete[] data; + } + } + else + { + action->execute(); + } + + d->redoActions.pop_back(); + d->undoActions.push_back(action); + d->origin++; +} + +void UndoManager::clear(bool clearCache) +{ + clearUndoActions(); + clearRedoActions(); + setOrigin(); + + if(clearCache) + d->undoCache->clear(); +} + +void UndoManager::clearUndoActions() +{ + UndoAction *action; + TQValueList<UndoAction*>::iterator it; + + for(it = d->undoActions.begin(); it != d->undoActions.end(); ++it) + { + action = *it; + delete action; + } + d->undoActions.clear(); +} + +void UndoManager::clearRedoActions() +{ + if(!anyMoreRedo()) + return; + + UndoAction *action; + TQValueList<UndoAction*>::iterator it; + + // get the level of the first redo action + int level = d->undoActions.size() + 1; + for(it = d->redoActions.begin(); it != d->redoActions.end(); ++it) + { + action = *it; + d->undoCache->erase(level); + delete action; + level++; + } + d->undoCache->erase(level); + d->redoActions.clear(); +} + +bool UndoManager::anyMoreUndo() +{ + return !d->undoActions.isEmpty(); +} + +bool UndoManager::anyMoreRedo() +{ + return !d->redoActions.isEmpty(); +} + +void UndoManager::getUndoHistory(TQStringList &titles) +{ + TQValueList<UndoAction*>::iterator it; + + for(it = d->undoActions.begin(); it != d->undoActions.end(); ++it) + { + titles.push_front((*it)->getTitle()); + } +} + +void UndoManager::getRedoHistory(TQStringList &titles) +{ + TQValueList<UndoAction*>::iterator it; + + for(it = d->redoActions.begin(); it != d->redoActions.end(); ++it) + { + titles.push_front((*it)->getTitle()); + } +} + +bool UndoManager::isAtOrigin() +{ + return d->origin == 0; +} + +void UndoManager::setOrigin() +{ + d->origin = 0; +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/canvas/undomanager.h b/src/utilities/imageeditor/canvas/undomanager.h new file mode 100644 index 00000000..a36ba12f --- /dev/null +++ b/src/utilities/imageeditor/canvas/undomanager.h @@ -0,0 +1,76 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-06 + * Description : an image editor actions undo/redo manager + * + * Copyright (C) 2005-2006 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005-2006 by Joern Ahrens <joern.ahrens@kdemail.net> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef UNDOMANAGER_H +#define UNDOMANAGER_H + +// TQt includes. + +#include <tqstringlist.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DImgInterface; +class UndoManagerPriv; +class UndoAction; + +class DIGIKAM_EXPORT UndoManager +{ + +public: + + UndoManager(DImgInterface* iface); + ~UndoManager(); + + void undo(); + void redo(); + + void clear(bool clearCache=true); + bool anyMoreUndo(); + bool anyMoreRedo(); + void getUndoHistory(TQStringList &titles); + void getRedoHistory(TQStringList &titles); + bool isAtOrigin(); + void setOrigin(); + + void addAction(UndoAction* action); + +private: + + void clearUndoActions(); + void clearRedoActions(); + +private: + + UndoManagerPriv *d; +}; + +} // namespace Digikam + +#endif /* UNDOMANAGER_H */ diff --git a/src/utilities/imageeditor/editor/Makefile.am b/src/utilities/imageeditor/editor/Makefile.am new file mode 100644 index 00000000..71031991 --- /dev/null +++ b/src/utilities/imageeditor/editor/Makefile.am @@ -0,0 +1,51 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libdimgeditor.la libshowfoto.la + +libdimgeditor_la_SOURCES = editorwindow.cpp imageiface.cpp imagewindow.cpp editorstackview.cpp \ + editortooliface.cpp editortool.cpp editortoolsettings.cpp + +libdimgeditor_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TDEPRINT) + +libdimgeditor_la_LIBADD = $(top_builddir)/src/utilities/imageeditor/tools/libdimgeditortools.la \ + $(top_builddir)/src/utilities/imageeditor/rawimport/librawimport.la + +libshowfoto_la_SOURCES = editorwindow.cpp imageiface.cpp editorstackview.cpp \ + editortooliface.cpp editortool.cpp editortoolsettings.cpp + +libshowfoto_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TDEPRINT) + +libshowfoto_la_LIBADD = $(top_builddir)/src/libs/dimg/libdimg.la \ + $(top_builddir)/src/libs/dialogs/libdialogshowfoto.la \ + $(top_builddir)/src/libs/widgets/libwidgets.la \ + $(top_builddir)/src/libs/greycstoration/libgreycstoration.la \ + $(top_builddir)/src/utilities/imageeditor/canvas/libdimgcanvas.la \ + $(top_builddir)/src/utilities/imageeditor/tools/libdimgeditortools.la \ + $(top_builddir)/src/utilities/imageeditor/rawimport/librawimport.la + +INCLUDES = -I$(top_srcdir)/src/digikam \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/themeengine \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/libs/imageproperties \ + -I$(top_srcdir)/src/libs/threadimageio \ + -I$(top_srcdir)/src/utilities/setup \ + -I$(top_srcdir)/src/utilities/slideshow \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/utilities/imageeditor/tools \ + -I$(top_builddir)/src/libs/dialogs \ + $(LIBKEXIV2_CFLAGS) $(LIBKDCRAW_CFLAGS) $(all_includes) + +digikaminclude_HEADERS = imageiface.h + +digikamincludedir = $(includedir)/digikam + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimagewindowui.rc + +kde_servicetypes_DATA = digikamimageplugin.desktop + diff --git a/src/utilities/imageeditor/editor/digikamimageplugin.desktop b/src/utilities/imageeditor/editor/digikamimageplugin.desktop new file mode 100644 index 00000000..31bef621 --- /dev/null +++ b/src/utilities/imageeditor/editor/digikamimageplugin.desktop @@ -0,0 +1,39 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=ServiceType +X-TDE-ServiceType=Digikam/ImagePlugin +X-TDE-DerivedName=Digikam ImagePlugin +Comment=A digiKam Image Plugin +Comment[bg]=Приставка за снимки в digiKam +Comment[br]=Ul lugent skeudenn digiKam +Comment[ca]=Un connector d'imatges del digiKam +Comment[cs]=Modul pro digiKam +Comment[da]=Et Digikam billed-plugin +Comment[de]=Ein Bild-Modul von digiKam +Comment[el]=Ένα πρόσθετο εικόνας digiKam +Comment[es]=Plugin de digiKam para imágenes +Comment[et]=DigiKami pildiplugin +Comment[fa]=یک وصلۀ تصویر digiKam +Comment[fi]=digiKam-liitännäinen +Comment[fr]=Un module externe pour digiKam +Comment[gl]=Un plugin de Imaxe de digiKam +Comment[hr]=digiKam dodatak za slike +Comment[is]=digiKam ljósmynda-íforrit +Comment[it]=Un plugin per le immagini di digiKam +Comment[ja]=digiKam 画像プラグイン +Comment[ms]=Plugin Imej digiKam +Comment[nds]=En digiKam-Bildmoduul +Comment[nl]=Een plugin voor Digikam +Comment[pa]=ਇੱਕ ਡਿਜ਼ੀਕੈਮ ਚਿੱਤਰ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka obrazu digiKama +Comment[pt]=Um 'Plugin' de Imagem do digiKam +Comment[pt_BR]=Plugin de imagem digiKam +Comment[ru]=Модуль изображений digiKam +Comment[sk]=DigiKam obrázkový plugin +Comment[sr]=digiKam-ов сликовни прикључак +Comment[sr@Latn]=digiKam-ov slikovni priključak +Comment[sv]=Ett bildinsticksprogram för Digikam +Comment[tr]=Bir digiKam Resim Eklentisi +Comment[uk]=Втулок зображень digiKam +Comment[vi]=Một phần bổ sung ảnh digiKam +Comment[xx]=xxA digiKam Image Pluginxx diff --git a/src/utilities/imageeditor/editor/digikamimagewindowui.rc b/src/utilities/imageeditor/editor/digikamimagewindowui.rc new file mode 100644 index 00000000..2c0314ad --- /dev/null +++ b/src/utilities/imageeditor/editor/digikamimagewindowui.rc @@ -0,0 +1,125 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<gui version="24" name="digikamimagewindow" > + +<MenuBar> + + <Menu name="File" ><text>&File</text> + <Action name="editorwindow_backward" /> + <Action name="editorwindow_forward" /> + <Separator/> + <Action name="editorwindow_first" /> + <Action name="editorwindow_last" /> + <Separator/> + <Action name="editorwindow_print" /> + <Separator/> + <Action name="editorwindow_save" /> + <Action name="editorwindow_saveas" /> + <Action name="editorwindow_revert" /> + <Separator/> + <Action name="editorwindow_delete" /> + <Separator/> + <Action name="editorwindow_close" /> + </Menu> + + <Menu name="Edit" ><text>&Edit</text> + <Action name="editorwindow_copy" /> + <Separator/> + <Action name="editorwindow_undo" /> + <Action name="editorwindow_redo" /> + <Separator/> + <Action name="editorwindow_selectAll" /> + <Action name="editorwindow_selectNone" /> + </Menu> + + <Menu name="View" ><text>&View</text> + <Action name="editorwindow_fullscreen" /> + <Action name="editorwindow_slideshow" /> + <Separator/> + <Action name="editorwindow_zoomplus" /> + <Action name="editorwindow_zoomminus" /> + <Action name="editorwindow_zoomto100percents" /> + <Action name="editorwindow_zoomfit2window" /> + <Action name="editorwindow_zoomfit2select" /> + <Separator/> + <Action name="editorwindow_underexposure" /> + <Action name="editorwindow_overexposure" /> + <Action name="editorwindow_cmview" /> + </Menu> + + <Menu name="Color" ><text>&Color</text> + </Menu> + + <Menu name="Enhance" ><text>Enh&ance</text> + </Menu> + + <Menu name="Transform" ><text>Tra&nsform</text> + <Action name="editorwindow_rotate_left" /> + <Action name="editorwindow_rotate_right" /> + <Separator/> + <Action name="editorwindow_flip_horiz" /> + <Action name="editorwindow_flip_vert" /> + <Separator/> + <Action name="editorwindow_crop" /> + <Action name="editorwindow_resize" /> + </Menu> + + <Menu name="Decorate" ><text>&Decorate</text> + </Menu> + + <Menu name="Filters" ><text>F&ilters</text> + </Menu> + + <Menu name="help" ><text>&Help</text> + <Action name="editorwindow_rawcameralist"/> + <Action name="editorwindow_donatemoney" /> + <Action name="editorwindow_contribute" /> + </Menu> + + <Merge/> + + <Menu name="settings" noMerge="1"><Text>&Settings</Text> + <Merge name="StandardToolBarMenuHandler" /> + <Action name="options_show_menubar"/> + <Action name="options_show_statusbar"/> + <Action name="options_show_toolbar"/> + <Separator/> + <Action name="theme_menu" /> + <Action name="options_configure_keybinding"/> + <Action name="options_configure_toolbars"/> + <Action name="options_configure" /> + </Menu> + +</MenuBar> + +<ToolBar name="ToolBar" ><text>Main Toolbar</text> + <Action name="editorwindow_first" /> + <Action name="editorwindow_backward" /> + <Action name="editorwindow_forward" /> + <Action name="editorwindow_last" /> + <Separator/> + <Action name="editorwindow_save" /> + <Action name="editorwindow_saveas" /> + <Action name="editorwindow_undo" /> + <Action name="editorwindow_redo" /> + <Action name="editorwindow_revert" /> + <Separator/> + <Action name="editorwindow_zoomplus" /> + <Action name="editorwindow_zoomto" /> + <Action name="editorwindow_zoomminus" /> + <Action name="editorwindow_zoomfit2window" /> + <Action name="editorwindow_zoomfit2select" /> + <Separator/> + <Action name="editorwindow_rotate_left" /> + <Action name="editorwindow_rotate_right" /> + <Action name="editorwindow_crop" /> + <Separator/> + <Action name="editorwindow_fullscreen" /> + <Action name="editorwindow_slideshow" /> + <Merge /> + <WeakSeparator/> + <Action name="logo_action" /> +</ToolBar> + +<ActionProperties/> + +</gui> diff --git a/src/utilities/imageeditor/editor/editorstackview.cpp b/src/utilities/imageeditor/editor/editorstackview.cpp new file mode 100644 index 00000000..61550300 --- /dev/null +++ b/src/utilities/imageeditor/editor/editorstackview.cpp @@ -0,0 +1,233 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-20 + * Description : A widget stack to embed editor view. + * + * Copyright (C) 2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// Local includes. + +#include "previewwidget.h" +#include "imageregionwidget.h" +#include "imagepanelwidget.h" +#include "canvas.h" +#include "editorstackview.h" +#include "editorstackview.moc" + +namespace Digikam +{ + +class EditorStackViewPriv +{ + +public: + + EditorStackViewPriv() + { + canvas = 0; + toolView = 0; + } + + TQWidget *toolView; + Canvas *canvas; +}; + +EditorStackView::EditorStackView(TQWidget *parent) + : TQWidgetStack(parent, 0, TQt::WDestructiveClose) +{ + d = new EditorStackViewPriv; +} + +EditorStackView::~EditorStackView() +{ + delete d; +} + +void EditorStackView::setCanvas(Canvas* canvas) +{ + if (d->canvas) return; + + d->canvas = canvas; + addWidget(d->canvas, CanvasMode); + + connect(d->canvas, TQ_SIGNAL(signalZoomChanged(double)), + this, TQ_SLOT(slotZoomChanged(double))); +} + +Canvas* EditorStackView::canvas() const +{ + return d->canvas; +} + +void EditorStackView::setToolView(TQWidget* view) +{ + if (d->toolView) + removeWidget(d->toolView); + + d->toolView = view; + + if (d->toolView) + addWidget(d->toolView, ToolViewMode); + + PreviewWidget *preview = previewWidget(); + if (preview) + { + connect(preview, TQ_SIGNAL(signalZoomFactorChanged(double)), + this, TQ_SLOT(slotZoomChanged(double))); + } +} + +TQWidget* EditorStackView::toolView() const +{ + return d->toolView; +} + +int EditorStackView::viewMode() +{ + return id(visibleWidget()); +} + +void EditorStackView::setViewMode(int mode) +{ + if (mode != CanvasMode && mode != ToolViewMode) + return; + + raiseWidget(mode); +} + +void EditorStackView::increaseZoom() +{ + if (viewMode() == CanvasMode) + { + d->canvas->slotIncreaseZoom(); + } + else + { + PreviewWidget *preview = previewWidget(); + if (preview) + preview->slotIncreaseZoom(); + } +} + +void EditorStackView::decreaseZoom() +{ + if (viewMode() == CanvasMode) + { + d->canvas->slotDecreaseZoom(); + } + else + { + PreviewWidget *preview = previewWidget(); + if (preview) + preview->slotDecreaseZoom(); + } +} + +void EditorStackView::toggleFitToWindow() +{ + if (viewMode() == CanvasMode) + { + d->canvas->toggleFitToWindow(); + } + else + { + PreviewWidget *preview = previewWidget(); + if (preview) + preview->toggleFitToWindow(); + } +} + +void EditorStackView::fitToSelect() +{ + if (viewMode() == CanvasMode) + { + d->canvas->fitToSelect(); + } +} + +void EditorStackView::zoomTo100Percents() +{ + if (viewMode() == CanvasMode) + { + if (d->canvas->zoomFactor() == 1.0) + d->canvas->toggleFitToWindow(); + else + d->canvas->setZoomFactor(1.0); + } + else + { + PreviewWidget *preview = previewWidget(); + if (preview) + { + if (preview->zoomFactor() == 1.0) + preview->toggleFitToWindow(); + else + preview->setZoomFactor(1.0); + } + } +} + +void EditorStackView::setZoomFactor(double zoom) +{ + if (viewMode() == CanvasMode) + { + d->canvas->setZoomFactor(zoom); + } + else + { + PreviewWidget *preview = previewWidget(); + if (preview) + preview->setZoomFactor(zoom); + } +} + +void EditorStackView::slotZoomChanged(double zoom) +{ + bool max, min; + + if (viewMode() == CanvasMode) + { + max = d->canvas->maxZoom(); + min = d->canvas->minZoom(); + emit signalZoomChanged(max, min, zoom); + } + else + { + PreviewWidget *preview = previewWidget(); + if (preview) + { + max = preview->maxZoom(); + min = preview->minZoom(); + emit signalZoomChanged(max, min, zoom); + } + } +} + +PreviewWidget* EditorStackView::previewWidget() const +{ + PreviewWidget *preview = dynamic_cast<PreviewWidget*>(d->toolView); + if (preview) return preview; + + ImagePanelWidget *panel = dynamic_cast<ImagePanelWidget*>(d->toolView); + if (panel) return (dynamic_cast<PreviewWidget*>(panel->previewWidget())); + + return 0; +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/editor/editorstackview.h b/src/utilities/imageeditor/editor/editorstackview.h new file mode 100644 index 00000000..e428fcd7 --- /dev/null +++ b/src/utilities/imageeditor/editor/editorstackview.h @@ -0,0 +1,97 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-20 + * Description : A widget stack to embed editor view. + * + * Copyright (C) 2008-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef EDITORSTACKVIEW_H +#define EDITORSTACKVIEW_H + +// KDE includes. + +#include <tqwidgetstack.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class PreviewWidget; +class Canvas; +class EditorStackViewPriv; + +class DIGIKAM_EXPORT EditorStackView : public TQWidgetStack +{ +TQ_OBJECT + + +public: + + enum StackViewMode + { + CanvasMode=0, + ToolViewMode + }; + +public: + + EditorStackView(TQWidget *parent=0); + ~EditorStackView(); + + void setCanvas(Canvas* canvas); + Canvas *canvas() const; + + void setToolView(TQWidget* view); + TQWidget *toolView() const; + + int viewMode(); + void setViewMode(int mode); + + void increaseZoom(); + void decreaseZoom(); + void toggleFitToWindow(); + void fitToSelect(); + void zoomTo100Percents(); + void setZoomFactor(double zoom); + + /** Two widgets are embedded in Editor Tool to perform preview with panning and zooming: + a PreviewWidget derivated class or ImagePanelWidget. + This method try to find the right PreviewWidget instance accordingly else return 0. + */ + PreviewWidget* previewWidget() const; + +signals: + + void signalZoomChanged(bool isMax, bool isMin, double zoom); + +private slots: + + void slotZoomChanged(double); + +private: + + EditorStackViewPriv* d; +}; + +} // namespace Digikam + +#endif /* EDITORSTACKVIEW_H */ diff --git a/src/utilities/imageeditor/editor/editortool.cpp b/src/utilities/imageeditor/editor/editortool.cpp new file mode 100644 index 00000000..b840e673 --- /dev/null +++ b/src/utilities/imageeditor/editor/editortool.cpp @@ -0,0 +1,436 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-20 + * Description : editor tool template class. + * + * Copyright (C) 2008-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqwidget.h> +#include <tqtimer.h> + +// KDE includes. + +#include <kcursor.h> + +// Local includes. + +#include "ddebug.h" +#include "imagewidget.h" +#include "imageguidewidget.h" +#include "imagepanelwidget.h" +#include "dimgthreadedfilter.h" +#include "editortoolsettings.h" +#include "editortooliface.h" +#include "editortool.h" +#include "editortool.moc" + +namespace Digikam +{ + +class EditorToolPriv +{ + +public: + + EditorToolPriv() + { + timer = 0; + view = 0; + settings = 0; + } + + TQString helpAnchor; + TQString name; + + TQWidget *view; + + TQPixmap icon; + + TQTimer *timer; + + EditorToolSettings *settings; +}; + +EditorTool::EditorTool(TQObject *parent) + : TQObject(parent) +{ + d = new EditorToolPriv; + d->timer = new TQTimer(this); + + connect(d->timer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotEffect())); +} + +EditorTool::~EditorTool() +{ + delete d; +} + +void EditorTool::init() +{ + TQTimer::singleShot(0, this, TQ_SLOT(slotInit())); +} + +TQPixmap EditorTool::toolIcon() const +{ + return d->icon; +} + +void EditorTool::setToolIcon(const TQPixmap& icon) +{ + d->icon = icon; +} + +TQString EditorTool::toolName() const +{ + return d->name; +} + +void EditorTool::setToolName(const TQString& name) +{ + d->name = name; +} + +TQWidget* EditorTool::toolView() const +{ + return d->view; +} + +void EditorTool::setToolView(TQWidget *view) +{ + d->view = view; + // Will be unblocked in slotInit() + // This will prevent resize event signals emit during tool init. + d->view->blockSignals(true); +} + +EditorToolSettings* EditorTool::toolSettings() const +{ + return d->settings; +} + +void EditorTool::setToolSettings(EditorToolSettings *settings) +{ + d->settings = settings; + + connect(d->settings, TQ_SIGNAL(signalOkClicked()), + this, TQ_SLOT(slotOk())); + + connect(d->settings, TQ_SIGNAL(signalCancelClicked()), + this, TQ_SLOT(slotCancel())); + + connect(d->settings, TQ_SIGNAL(signalDefaultClicked()), + this, TQ_SLOT(slotResetSettings())); + + connect(d->settings, TQ_SIGNAL(signalSaveAsClicked()), + this, TQ_SLOT(slotSaveAsSettings())); + + connect(d->settings, TQ_SIGNAL(signalLoadClicked()), + this, TQ_SLOT(slotLoadSettings())); + + connect(d->settings, TQ_SIGNAL(signalTryClicked()), + this, TQ_SLOT(slotEffect())); + + // Will be unblocked in slotInit() + // This will prevent signals emit during tool init. + d->settings->blockSignals(true); +} + +void EditorTool::slotInit() +{ + readSettings(); + // Unlock signals from preview and settings widgets when init is done. + d->view->blockSignals(false); + d->settings->blockSignals(false); +} + +void EditorTool::setToolHelp(const TQString& anchor) +{ + d->helpAnchor = anchor; + // TODO: use this anchor with editor Help menu +} + +TQString EditorTool::toolHelp() const +{ + if (d->helpAnchor.isEmpty()) + return (name() + TQString(".anchor")); + + return d->helpAnchor; +} + +void EditorTool::setBusy(bool state) +{ + d->settings->setBusy(state); +} + +void EditorTool::readSettings() +{ + d->settings->readSettings(); +} + +void EditorTool::writeSettings() +{ + d->settings->writeSettings(); +} + +void EditorTool::slotResetSettings() +{ + d->settings->resetSettings(); +} + +void EditorTool::slotTimer() +{ + d->timer->start(500, true); +} + +void EditorTool::slotOk() +{ + writeSettings(); + finalRendering(); + emit okClicked(); +} + +void EditorTool::slotCancel() +{ + writeSettings(); + emit cancelClicked(); +} + +// ---------------------------------------------------------------- + +class EditorToolThreadedPriv +{ + +public: + + EditorToolThreadedPriv() + { + threadedFilter = 0; + currentRenderingMode = EditorToolThreaded::NoneRendering; + } + + EditorToolThreaded::RenderingMode currentRenderingMode; + + TQString progressMess; + + DImgThreadedFilter *threadedFilter; +}; + +EditorToolThreaded::EditorToolThreaded(TQObject *parent) + : EditorTool(parent) +{ + d = new EditorToolThreadedPriv; +} + +EditorToolThreaded::~EditorToolThreaded() +{ + delete d->threadedFilter; + delete d; +} + +EditorToolThreaded::RenderingMode EditorToolThreaded::renderingMode() const +{ + return d->currentRenderingMode; +} + +void EditorToolThreaded::setProgressMessage(const TQString& mess) +{ + d->progressMess = mess; +} + +DImgThreadedFilter* EditorToolThreaded::filter() const +{ + return d->threadedFilter; +} + +void EditorToolThreaded::setFilter(DImgThreadedFilter *filter) +{ + d->threadedFilter = filter; +} + +void EditorToolThreaded::slotResized() +{ + if (d->currentRenderingMode == EditorToolThreaded::FinalRendering) + { + toolView()->update(); + return; + } + else if (d->currentRenderingMode == EditorToolThreaded::PreviewRendering) + { + if (filter()) + filter()->stopComputation(); + } + + TQTimer::singleShot(0, this, TQ_SLOT(slotEffect())); +} + +void EditorToolThreaded::slotAbort() +{ + d->currentRenderingMode = EditorToolThreaded::NoneRendering; + + if (filter()) + filter()->stopComputation(); + + EditorToolIface::editorToolIface()->setToolStopProgress(); + + toolSettings()->enableButton(EditorToolSettings::Ok, true); + toolSettings()->enableButton(EditorToolSettings::Load, true); + toolSettings()->enableButton(EditorToolSettings::SaveAs, true); + toolSettings()->enableButton(EditorToolSettings::Try, true); + toolSettings()->enableButton(EditorToolSettings::Default, true); + + renderingFinished(); +} + +void EditorToolThreaded::customEvent(TQCustomEvent *e) +{ + if (!e) return; + + DImgThreadedFilter::EventData *ed = (DImgThreadedFilter::EventData*)e->data(); + + if (!ed) return; + + if (ed->starting) // Computation in progress ! + { + EditorToolIface::editorToolIface()->setToolProgress(ed->progress); + } + else + { + if (ed->success) // Computation Completed ! + { + switch (d->currentRenderingMode) + { + case EditorToolThreaded::PreviewRendering: + { + DDebug() << "Preview " << toolName() << " completed..." << endl; + putPreviewData(); + slotAbort(); + break; + } + + case EditorToolThreaded::FinalRendering: + { + DDebug() << "Final" << toolName() << " completed..." << endl; + putFinalData(); + EditorToolIface::editorToolIface()->setToolStopProgress(); + kapp->restoreOverrideCursor(); + emit okClicked(); + break; + } + + default: + break; + } + } + else // Computation Failed ! + { + switch (d->currentRenderingMode) + { + case EditorToolThreaded::PreviewRendering: + { + DDebug() << "Preview " << toolName() << " failed..." << endl; + slotAbort(); + break; + } + + case EditorToolThreaded::FinalRendering: + default: + break; + } + } + } + + delete ed; +} + +void EditorToolThreaded::setToolView(TQWidget *view) +{ + EditorTool::setToolView(view); + + if (dynamic_cast<ImageWidget*>(view) || dynamic_cast<ImageGuideWidget*>(view) || + dynamic_cast<ImagePanelWidget*>(view)) + { + connect(view, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotResized())); + } +} + +void EditorToolThreaded::slotOk() +{ + writeSettings(); + + d->currentRenderingMode = EditorToolThreaded::FinalRendering; + DDebug() << "Final " << toolName() << " started..." << endl; + writeSettings(); + + toolSettings()->enableButton(EditorToolSettings::Ok, false); + toolSettings()->enableButton(EditorToolSettings::SaveAs, false); + toolSettings()->enableButton(EditorToolSettings::Load, false); + toolSettings()->enableButton(EditorToolSettings::Default, false); + toolSettings()->enableButton(EditorToolSettings::Try, false); + + EditorToolIface::editorToolIface()->setToolStartProgress(d->progressMess.isEmpty() ? toolName() : d->progressMess); + kapp->setOverrideCursor( KCursor::waitCursor() ); + + if (d->threadedFilter) + { + delete d->threadedFilter; + d->threadedFilter = 0; + } + + prepareFinal(); +} + +void EditorToolThreaded::slotEffect() +{ + // Computation already in process. + if (d->currentRenderingMode != EditorToolThreaded::NoneRendering) + return; + + d->currentRenderingMode = EditorToolThreaded::PreviewRendering; + DDebug() << "Preview " << toolName() << " started..." << endl; + + toolSettings()->enableButton(EditorToolSettings::Ok, false); + toolSettings()->enableButton(EditorToolSettings::SaveAs, false); + toolSettings()->enableButton(EditorToolSettings::Load, false); + toolSettings()->enableButton(EditorToolSettings::Default, false); + toolSettings()->enableButton(EditorToolSettings::Try, false); + + EditorToolIface::editorToolIface()->setToolStartProgress(d->progressMess.isEmpty() ? toolName() : d->progressMess); + + if (d->threadedFilter) + { + delete d->threadedFilter; + d->threadedFilter = 0; + } + + prepareEffect(); +} + +void EditorToolThreaded::slotCancel() +{ + writeSettings(); + slotAbort(); + kapp->restoreOverrideCursor(); + emit cancelClicked(); +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/editor/editortool.h b/src/utilities/imageeditor/editor/editortool.h new file mode 100644 index 00000000..120ff9f0 --- /dev/null +++ b/src/utilities/imageeditor/editor/editortool.h @@ -0,0 +1,164 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-20 + * Description : editor tool template class. + * + * Copyright (C) 2008-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef EDITORTOOL_H +#define EDITORTOOL_H + +// TQt includes. + +#include <tqobject.h> +#include <tqstring.h> +#include <tqpixmap.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DImgThreadedFilter; +class EditorToolSettings; +class EditorToolPriv; + +class DIGIKAM_EXPORT EditorTool : public TQObject +{ + TQ_OBJECT + + +public: + + EditorTool(TQObject *parent); + virtual ~EditorTool(); + + void init(); + + TQString toolHelp() const; + TQString toolName() const; + TQPixmap toolIcon() const; + TQWidget* toolView() const; + EditorToolSettings* toolSettings() const; + +signals: + + void okClicked(); + void cancelClicked(); + +protected: + + void setToolHelp(const TQString& anchor); + void setToolName(const TQString& name); + void setToolIcon(const TQPixmap& icon); + + virtual void setToolView(TQWidget *view); + virtual void setToolSettings(EditorToolSettings *settings); + virtual void setBusy(bool); + virtual void readSettings(); + virtual void writeSettings(); + virtual void finalRendering(){}; + +protected slots: + + void slotTimer(); + + virtual void slotOk(); + virtual void slotCancel(); + virtual void slotInit(); + virtual void slotLoadSettings(){}; + virtual void slotSaveAsSettings(){}; + virtual void slotResetSettings(); + virtual void slotEffect(){}; + +private: + + EditorToolPriv *d; +}; + +// ----------------------------------------------------------------- + +class EditorToolThreadedPriv; + +class DIGIKAM_EXPORT EditorToolThreaded : public EditorTool +{ + TQ_OBJECT + + +public: + + enum RenderingMode + { + NoneRendering=0, + PreviewRendering, + FinalRendering + }; + +public: + + EditorToolThreaded(TQObject *parent); + virtual ~EditorToolThreaded(); + + /** Set the small text to show in editor status progress bar during + tool computation. If it's not set, tool name is used instead. + */ + void setProgressMessage(const TQString& mess); + + /** return the current tool rendering mode. + */ + RenderingMode renderingMode() const; + +public slots: + + virtual void slotAbort(); + +protected: + + DImgThreadedFilter* filter() const; + void setFilter(DImgThreadedFilter *filter); + + void customEvent(TQCustomEvent *event); + + virtual void setToolView(TQWidget *view); + virtual void prepareEffect(){}; + virtual void prepareFinal(){}; + virtual void putPreviewData(){}; + virtual void putFinalData(){}; + virtual void renderingFinished(){}; + +protected slots: + + virtual void slotOk(); + virtual void slotCancel(); + virtual void slotEffect(); + +private slots: + + void slotResized(); + +private: + + EditorToolThreadedPriv *d; +}; + +} //namespace Digikam + +#endif /* IMAGEPLUGIN_H */ diff --git a/src/utilities/imageeditor/editor/editortooliface.cpp b/src/utilities/imageeditor/editor/editortooliface.cpp new file mode 100644 index 00000000..f666bbf2 --- /dev/null +++ b/src/utilities/imageeditor/editor/editortooliface.cpp @@ -0,0 +1,147 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-20 + * Description : Image editor interface used by editor tools. + * + * Copyright (C) 2008-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqwidget.h> + +// Local includes. + +#include "sidebar.h" +#include "canvas.h" +#include "statusprogressbar.h" +#include "editortool.h" +#include "editortoolsettings.h" +#include "editorstackview.h" +#include "editorwindow.h" +#include "editortooliface.h" +#include "editortooliface.moc" + +namespace Digikam +{ + +class EditorToolIfacePriv +{ + +public: + + EditorToolIfacePriv() + { + prevTab = 0; + editor = 0; + tool = 0; + } + + TQWidget *prevTab; + + EditorTool *tool; + + EditorWindow *editor; +}; + +EditorToolIface* EditorToolIface::m_iface = 0; + +EditorToolIface* EditorToolIface::editorToolIface() +{ + return m_iface; +} + +EditorToolIface::EditorToolIface(EditorWindow *editor) + : TQObject() +{ + d = new EditorToolIfacePriv; + d->editor = editor; + m_iface = this; +} + +EditorToolIface::~EditorToolIface() +{ + delete d; + if (m_iface == this) + m_iface = 0; +} + +EditorTool* EditorToolIface::currentTool() const +{ + return d->tool; +} + +void EditorToolIface::loadTool(EditorTool* tool) +{ + if (d->tool) unLoadTool(); + + d->tool = tool; + d->editor->editorStackView()->setToolView(d->tool->toolView()); + d->editor->editorStackView()->setViewMode(EditorStackView::ToolViewMode); + d->prevTab = d->editor->rightSideBar()->getActiveTab(); + d->editor->rightSideBar()->appendTab(d->tool->toolSettings(), d->tool->toolIcon(), d->tool->toolName()); + d->editor->rightSideBar()->setActiveTab(d->tool->toolSettings()); + d->editor->toggleActions(false); + + // If editor tool has zoomable preview, switch on zoom actions. + if (d->editor->editorStackView()->previewWidget()) + d->editor->toggleZoomActions(true); +} + +void EditorToolIface::unLoadTool() +{ + if (!d->tool) return; + + d->editor->editorStackView()->setViewMode(EditorStackView::CanvasMode); + d->editor->editorStackView()->setToolView(0); + d->editor->rightSideBar()->deleteTab(d->tool->toolSettings()); + d->editor->rightSideBar()->setActiveTab(d->prevTab); + d->editor->toggleActions(true); + // To restore canvas zoom level in zoom combobox. + if (!d->editor->editorStackView()->canvas()->fitToWindow()) + d->editor->editorStackView()->setZoomFactor(d->editor->editorStackView()->canvas()->zoomFactor()); + delete d->tool; + d->tool = 0; +} + +void EditorToolIface::setToolStartProgress(const TQString& toolName) +{ + d->editor->setToolStartProgress(toolName); + if (d->editor->editorStackView()->previewWidget()) + d->editor->toggleZoomActions(false); +} + +void EditorToolIface::setToolProgress(int progress) +{ + d->editor->setToolProgress(progress); +} + +void EditorToolIface::setToolStopProgress() +{ + d->editor->setToolStopProgress(); + if (d->editor->editorStackView()->previewWidget()) + d->editor->toggleZoomActions(true); +} + +void EditorToolIface::slotToolAborted() +{ + EditorToolThreaded *tool = dynamic_cast<EditorToolThreaded*>(d->tool); + if (tool) tool->slotAbort(); +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/editor/editortooliface.h b/src/utilities/imageeditor/editor/editortooliface.h new file mode 100644 index 00000000..ce8708cf --- /dev/null +++ b/src/utilities/imageeditor/editor/editortooliface.h @@ -0,0 +1,76 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-20 + * Description : Image editor interface used by editor tools. + * + * Copyright (C) 2008-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef EDITORTOOLIFACE_H +#define EDITORTOOLIFACE_H + +// TQt includes. + +#include <tqobject.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class EditorTool; +class EditorWindow; +class EditorToolIfacePriv; + +class DIGIKAM_EXPORT EditorToolIface : public TQObject +{ + TQ_OBJECT + + +public: + + static EditorToolIface* editorToolIface(); + + EditorToolIface(EditorWindow *editor); + ~EditorToolIface(); + + EditorTool* currentTool() const; + + void loadTool(EditorTool* tool); + void unLoadTool(); + + void setToolStartProgress(const TQString& toolName); + void setToolProgress(int progress); + void setToolStopProgress(); + +public slots: + + void slotToolAborted(); + +private: + + static EditorToolIface *m_iface; + + EditorToolIfacePriv *d; +}; + +} // namespace Digikam + +#endif /* EDITORTOOLIFACE_H */ diff --git a/src/utilities/imageeditor/editor/editortoolsettings.cpp b/src/utilities/imageeditor/editor/editortoolsettings.cpp new file mode 100644 index 00000000..3ac45ccf --- /dev/null +++ b/src/utilities/imageeditor/editor/editortoolsettings.cpp @@ -0,0 +1,331 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-21 + * Description : Editor tool settings template box + * + * Copyright (C) 2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqhbox.h> +#include <tqvbox.h> +#include <tqlabel.h> +#include <tqlayout.h> +#include <tqstring.h> +#include <tqtooltip.h> +#include <tqwhatsthis.h> + +// KDE includes. + +#include <tdeapplication.h> +#include <kcolorbutton.h> +#include <kdialog.h> +#include <tdeglobalsettings.h> +#include <kiconloader.h> +#include <tdelocale.h> +#include <kpushbutton.h> +#include <kstandarddirs.h> +#include <kstdguiitem.h> + +// LibKDcraw includes. + +#include <libkdcraw/rnuminput.h> + +// Local includes. + +#include "ddebug.h" +#include "imagepaniconwidget.h" +#include "editortoolsettings.h" +#include "editortoolsettings.moc" + +using namespace KDcrawIface; + +namespace Digikam +{ + +class EditorToolSettingsPriv +{ + +public: + + EditorToolSettingsPriv() + { + okBtn = 0; + cancelBtn = 0; + tryBtn = 0; + defaultBtn = 0; + mainVBox = 0; + plainPage = 0; + btnBox1 = 0; + btnBox2 = 0; + btnBox3 = 0; + saveAsBtn = 0; + loadBtn = 0; + guideBox = 0; + guideColorBt = 0; + guideSize = 0; + panIconView = 0; + } + + TQHBox *btnBox1; + TQHBox *btnBox2; + TQHBox *btnBox3; + TQHBox *guideBox; + + TQVBox *mainVBox; + TQWidget *plainPage; + + KPushButton *okBtn; + KPushButton *cancelBtn; + KPushButton *tryBtn; + KPushButton *defaultBtn; + KPushButton *saveAsBtn; + KPushButton *loadBtn; + + KColorButton *guideColorBt; + + ImagePanIconWidget *panIconView; + + RIntNumInput *guideSize; +}; + +EditorToolSettings::EditorToolSettings(int buttonMask, int toolMask, TQWidget *parent) + : TQScrollView(parent) +{ + d = new EditorToolSettingsPriv; + + viewport()->setBackgroundMode(TQt::PaletteBackground); + setResizePolicy(TQScrollView::AutoOneFit); + setFrameStyle(TQFrame::NoFrame); + + d->mainVBox = new TQVBox(viewport()); + addChild(d->mainVBox); + + // --------------------------------------------------------------- + + TQFrame *frame = new TQFrame(d->mainVBox); + frame->setFrameStyle(TQFrame::Panel|TQFrame::Sunken); + TQVBoxLayout* vlay = new TQVBoxLayout(frame, 5, 0); + d->panIconView = new ImagePanIconWidget(360, 240, frame); + TQWhatsThis::add(d->panIconView, i18n("<p>Here you can see the original image panel " + "which can help you to select the clip preview." + "<p>Click and drag the mouse cursor in the " + "red rectangle to change the clip focus.")); + vlay->addWidget(d->panIconView, 0, TQt::AlignCenter); + + if (!(toolMask & PanIcon)) + frame->hide(); + + // --------------------------------------------------------------- + + d->plainPage = new TQWidget(d->mainVBox); + d->guideBox = new TQHBox(d->mainVBox); + d->btnBox1 = new TQHBox(d->mainVBox); + d->btnBox2 = new TQHBox(d->mainVBox); + + // --------------------------------------------------------------- + + new TQLabel(i18n("Guide:"), d->guideBox); + TQLabel *space4 = new TQLabel(d->guideBox); + d->guideColorBt = new KColorButton(TQColor(TQt::red), d->guideBox); + TQWhatsThis::add(d->guideColorBt, i18n("<p>Set here the color used to draw guides dashed-lines.")); + d->guideSize = new RIntNumInput(d->guideBox); + d->guideSize->setRange(1, 5, 1); + d->guideSize->setDefaultValue(1); + TQWhatsThis::add(d->guideSize, i18n("<p>Set here the width in pixels used to draw guides dashed-lines.")); + + d->guideBox->setStretchFactor(space4, 10); + d->guideBox->setSpacing(spacingHint()); + d->guideBox->setMargin(0); + + if (!(toolMask & ColorGuide)) + d->guideBox->hide(); + + // --------------------------------------------------------------- + + d->defaultBtn = new KPushButton(d->btnBox1); + d->defaultBtn->setGuiItem(KStdGuiItem::defaults()); + d->defaultBtn->setIconSet(SmallIconSet("reload_page")); + TQToolTip::add(d->defaultBtn, i18n("<p>Reset all settings to their default values.")); + if (!(buttonMask & Default)) + d->defaultBtn->hide(); + + TQLabel *space = new TQLabel(d->btnBox1); + + d->okBtn = new KPushButton(d->btnBox1); + d->okBtn->setGuiItem(KStdGuiItem::ok()); + if (!(buttonMask & Ok)) + d->okBtn->hide(); + + d->cancelBtn = new KPushButton(d->btnBox1); + d->cancelBtn->setGuiItem(KStdGuiItem::cancel()); + if (!(buttonMask & Cancel)) + d->cancelBtn->hide(); + + d->btnBox1->setStretchFactor(space, 10); + d->btnBox1->setSpacing(spacingHint()); + d->btnBox1->setMargin(0); + + if (!(buttonMask & Default) && !(buttonMask & Ok) && !(buttonMask & Cancel)) + d->btnBox1->hide(); + + // --------------------------------------------------------------- + + d->loadBtn = new KPushButton(d->btnBox2); + d->loadBtn->setGuiItem(KStdGuiItem::open()); + d->loadBtn->setText(i18n("Load...")); + TQToolTip::add(d->loadBtn, i18n("<p>Load all parameters from settings text file.")); + if (!(buttonMask & Load)) + d->loadBtn->hide(); + + d->saveAsBtn = new KPushButton(d->btnBox2); + d->saveAsBtn->setGuiItem(KStdGuiItem::saveAs()); + TQToolTip::add(d->saveAsBtn, i18n("<p>Save all parameters to settings text file.")); + if (!(buttonMask & SaveAs)) + d->saveAsBtn->hide(); + + TQLabel *space2 = new TQLabel(d->btnBox2); + + d->tryBtn = new KPushButton(d->btnBox2); + d->tryBtn->setGuiItem(KStdGuiItem::apply()); + d->tryBtn->setText(i18n("Try")); + TQToolTip::add(d->tryBtn, i18n("<p>Try all settings.")); + if (!(buttonMask & Try)) + d->tryBtn->hide(); + + d->btnBox2->setStretchFactor(space2, 10); + d->btnBox2->setSpacing(spacingHint()); + d->btnBox2->setMargin(0); + + if (!(buttonMask & Load) && !(buttonMask & SaveAs) && !(buttonMask & Try)) + d->btnBox2->hide(); + + // --------------------------------------------------------------- + + connect(d->okBtn, TQ_SIGNAL(clicked()), + this, TQ_SIGNAL(signalOkClicked())); + + connect(d->cancelBtn, TQ_SIGNAL(clicked()), + this, TQ_SIGNAL(signalCancelClicked())); + + connect(d->tryBtn, TQ_SIGNAL(clicked()), + this, TQ_SIGNAL(signalTryClicked())); + + connect(d->defaultBtn, TQ_SIGNAL(clicked()), + this, TQ_SIGNAL(signalDefaultClicked())); + + connect(d->saveAsBtn, TQ_SIGNAL(clicked()), + this, TQ_SIGNAL(signalSaveAsClicked())); + + connect(d->loadBtn, TQ_SIGNAL(clicked()), + this, TQ_SIGNAL(signalLoadClicked())); + + connect(d->guideColorBt, TQ_SIGNAL(changed(const TQColor&)), + this, TQ_SIGNAL(signalColorGuideChanged())); + + connect(d->guideSize, TQ_SIGNAL(valueChanged(int)), + this, TQ_SIGNAL(signalColorGuideChanged())); +} + +EditorToolSettings::~EditorToolSettings() +{ + delete d; +} + +TQSize EditorToolSettings::minimumSizeHint() const +{ + // Editor Tools usually require a larger horizontal space than other widgets in right side bar + // Set scroll area to a horizontal minimum size sufficient for the settings. + // Do not touch vertical size hint. + // Limit to 40% of the desktop width. + TQSize hint = TQScrollView::minimumSizeHint(); + TQRect desktopRect = TDEGlobalSettings::desktopGeometry(d->mainVBox); + hint.setWidth(TQMIN(d->mainVBox->minimumSizeHint().width(), desktopRect.width() * 2 / 5)); + return hint; +} + +int EditorToolSettings::marginHint() +{ + return KDialog::marginHint(); +} + +int EditorToolSettings::spacingHint() +{ + return KDialog::spacingHint(); +} + +TQWidget *EditorToolSettings::plainPage() const +{ + return d->plainPage; +} + +ImagePanIconWidget* EditorToolSettings::panIconView() const +{ + return d->panIconView; +} + +KPushButton* EditorToolSettings::button(int buttonCode) const +{ + if (buttonCode & Default) + return d->defaultBtn; + + if (buttonCode & Try) + return d->tryBtn; + + if (buttonCode & Ok) + return d->okBtn; + + if (buttonCode & Cancel) + return d->cancelBtn; + + if (buttonCode & Load) + return d->loadBtn; + + if (buttonCode & SaveAs) + return d->saveAsBtn; + + return 0; +} + +void EditorToolSettings::enableButton(int buttonCode, bool state) +{ + KPushButton *btn = button(buttonCode); + if (btn) btn->setEnabled(state); +} + +TQColor EditorToolSettings::guideColor() const +{ + return d->guideColorBt->color(); +} + +void EditorToolSettings::setGuideColor(const TQColor& color) +{ + d->guideColorBt->setColor(color); +} + +int EditorToolSettings::guideSize() const +{ + return d->guideSize->value(); +} + +void EditorToolSettings::setGuideSize(int size) +{ + d->guideSize->setValue(size); +} + +} // NameSpace Digikam diff --git a/src/utilities/imageeditor/editor/editortoolsettings.h b/src/utilities/imageeditor/editor/editortoolsettings.h new file mode 100644 index 00000000..062b2e34 --- /dev/null +++ b/src/utilities/imageeditor/editor/editortoolsettings.h @@ -0,0 +1,110 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-21 + * Description : Editor tool settings template box + * + * Copyright (C) 2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef EDITORTOOLSETTINGS_H +#define EDITORTOOLSETTINGS_H + +// TQt includes. + +#include <tqscrollview.h> + +// Local includes. + +#include "digikam_export.h" + +class KPushButton; + +namespace Digikam +{ + +class ImagePanIconWidget; +class EditorToolSettingsPriv; + +class DIGIKAM_EXPORT EditorToolSettings : public TQScrollView +{ + TQ_OBJECT + + +public: + + enum ButtonCode + { + Default = 0x00000001, + Try = 0x00000002, + Ok = 0x00000004, + Cancel = 0x00000008, + SaveAs = 0x00000010, + Load = 0x00000020 + }; + + enum ToolCode + { + NoTool = 0x00000001, + ColorGuide = 0x00000002, + PanIcon = 0x00000004 + }; + +public: + + EditorToolSettings(int buttonMask, int toolMask=NoTool, TQWidget *parent=0); + ~EditorToolSettings(); + + virtual void setBusy(bool){}; + virtual void writeSettings(){}; + virtual void readSettings(){}; + virtual void resetSettings(){}; + + int marginHint(); + int spacingHint(); + + TQWidget *plainPage() const; + + TQColor guideColor() const; + void setGuideColor(const TQColor& color); + + int guideSize() const; + void setGuideSize(int size); + + ImagePanIconWidget* panIconView() const; + KPushButton* button(int buttonCode) const; + void enableButton(int buttonCode, bool state); + + virtual TQSize minimumSizeHint() const; + +signals: + + void signalOkClicked(); + void signalCancelClicked(); + void signalTryClicked(); + void signalDefaultClicked(); + void signalSaveAsClicked(); + void signalLoadClicked(); + void signalColorGuideChanged(); + +private: + + EditorToolSettingsPriv *d; +}; + +} // NameSpace Digikam + +#endif // EDITORTOOLSETTINGS_H diff --git a/src/utilities/imageeditor/editor/editorwindow.cpp b/src/utilities/imageeditor/editor/editorwindow.cpp new file mode 100644 index 00000000..eed7cfea --- /dev/null +++ b/src/utilities/imageeditor/editor/editorwindow.cpp @@ -0,0 +1,1932 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-01-20 + * Description : main image editor GUI implementation + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include <sys/types.h> +#include <sys/stat.h> +} + +// C++ includes. + +#include <cmath> + +// TQt includes. + +#include <tqlabel.h> +#include <tqdockarea.h> +#include <tqlayout.h> +#include <tqtooltip.h> +#include <tqtoolbutton.h> +#include <tqsplitter.h> +#include <tqdir.h> +#include <tqfileinfo.h> +#include <tqfile.h> +#include <tqcursor.h> +#include <tqtimer.h> +#include <tqfileinfo.h> + +// KDE includes. + +#include <kprinter.h> +#include <kkeydialog.h> +#include <tdeversion.h> +#include <tdeaction.h> +#include <kedittoolbar.h> +#include <tdeaboutdata.h> +#include <kcursor.h> +#include <kstdaction.h> +#include <tdeapplication.h> +#include <tdeconfig.h> +#include <tdelocale.h> +#include <tdefiledialog.h> +#include <tdemenubar.h> +#include <kimageio.h> +#include <tdeaccel.h> +#include <tdemessagebox.h> +#include <tdeglobal.h> +#include <kstandarddirs.h> +#include <kiconloader.h> +#include <tdeio/netaccess.h> +#include <tdeio/job.h> +#include <kprotocolinfo.h> +#include <tdeglobalsettings.h> +#include <tdetoolbar.h> +#include <kstatusbar.h> +#include <kprogress.h> +#include <twin.h> +#include <kcombobox.h> + +// Local includes. + +#include "ddebug.h" +#include "dpopupmenu.h" +#include "canvas.h" +#include "dimginterface.h" +#include "imagedialog.h" +#include "imageplugin.h" +#include "imagepluginloader.h" +#include "imageresize.h" +#include "imageprint.h" +#include "filesaveoptionsbox.h" +#include "statusprogressbar.h" +#include "iccsettingscontainer.h" +#include "exposurecontainer.h" +#include "iofilesettingscontainer.h" +#include "savingcontextcontainer.h" +#include "loadingcacheinterface.h" +#include "slideshowsettings.h" +#include "themeengine.h" +#include "rawcameradlg.h" +#include "editorstackview.h" +#include "editortooliface.h" +#include "editorwindowprivate.h" +#include "editorwindow.h" +#include "editorwindow.moc" + +void tqt_enter_modal( TQWidget *widget ); +void tqt_leave_modal( TQWidget *widget ); + +namespace Digikam +{ + +EditorWindow::EditorWindow(const char *name) + : TDEMainWindow(0, name, WType_TopLevel) +{ + d = new EditorWindowPriv; + + m_themeMenuAction = 0; + m_contextMenu = 0; + m_canvas = 0; + m_imagePluginLoader = 0; + m_undoAction = 0; + m_redoAction = 0; + m_fullScreenAction = 0; + m_saveAction = 0; + m_saveAsAction = 0; + m_revertAction = 0; + m_fileDeleteAction = 0; + m_forwardAction = 0; + m_backwardAction = 0; + m_firstAction = 0; + m_lastAction = 0; + m_undoAction = 0; + m_redoAction = 0; + m_stackView = 0; + m_fullScreen = false; + m_rotatedOrFlipped = false; + m_setExifOrientationTag = true; + m_cancelSlideShow = false; + + // Settings containers instance. + + d->ICCSettings = new ICCSettingsContainer(); + d->exposureSettings = new ExposureSettingsContainer(); + d->toolIface = new EditorToolIface(this); + m_IOFileSettings = new IOFileSettingsContainer(); + m_savingContext = new SavingContextContainer(); +} + +EditorWindow::~EditorWindow() +{ + delete m_canvas; + delete m_IOFileSettings; + delete m_savingContext; + delete d->ICCSettings; + delete d->exposureSettings; + delete d; +} + +EditorStackView* EditorWindow::editorStackView() const +{ + return m_stackView; +} + +void EditorWindow::setupContextMenu() +{ + m_contextMenu = new DPopupMenu(this); + TDEActionCollection *ac = actionCollection(); + if( ac->action("editorwindow_backward") ) ac->action("editorwindow_backward")->plug(m_contextMenu); + if( ac->action("editorwindow_forward") ) ac->action("editorwindow_forward")->plug(m_contextMenu); + m_contextMenu->insertSeparator(); + if( ac->action("editorwindow_slideshow") ) ac->action("editorwindow_slideshow")->plug(m_contextMenu); + if( ac->action("editorwindow_rotate_left") ) ac->action("editorwindow_rotate_left")->plug(m_contextMenu); + if( ac->action("editorwindow_rotate_right") ) ac->action("editorwindow_rotate_right")->plug(m_contextMenu); + if( ac->action("editorwindow_crop") ) ac->action("editorwindow_crop")->plug(m_contextMenu); + m_contextMenu->insertSeparator(); + if( ac->action("editorwindow_delete") ) ac->action("editorwindow_delete")->plug(m_contextMenu); +} + +void EditorWindow::setupStandardConnections() +{ + // -- Canvas connections ------------------------------------------------ + + connect(m_canvas, TQ_SIGNAL(signalToggleOffFitToWindow()), + this, TQ_SLOT(slotToggleOffFitToWindow())); + + connect(m_canvas, TQ_SIGNAL(signalShowNextImage()), + this, TQ_SLOT(slotForward())); + + connect(m_canvas, TQ_SIGNAL(signalShowPrevImage()), + this, TQ_SLOT(slotBackward())); + + connect(m_canvas, TQ_SIGNAL(signalRightButtonClicked()), + this, TQ_SLOT(slotContextMenu())); + + connect(m_stackView, TQ_SIGNAL(signalZoomChanged(bool, bool, double)), + this, TQ_SLOT(slotZoomChanged(bool, bool, double))); + + connect(m_canvas, TQ_SIGNAL(signalChanged()), + this, TQ_SLOT(slotChanged())); + + connect(m_canvas, TQ_SIGNAL(signalUndoStateChanged(bool, bool, bool)), + this, TQ_SLOT(slotUndoStateChanged(bool, bool, bool))); + + connect(m_canvas, TQ_SIGNAL(signalSelected(bool)), + this, TQ_SLOT(slotSelected(bool))); + + connect(m_canvas, TQ_SIGNAL(signalPrepareToLoad()), + this, TQ_SLOT(slotPrepareToLoad())); + + connect(m_canvas, TQ_SIGNAL(signalLoadingStarted(const TQString &)), + this, TQ_SLOT(slotLoadingStarted(const TQString &))); + + connect(m_canvas, TQ_SIGNAL(signalLoadingFinished(const TQString &, bool)), + this, TQ_SLOT(slotLoadingFinished(const TQString &, bool))); + + connect(m_canvas, TQ_SIGNAL(signalLoadingProgress(const TQString &, float)), + this, TQ_SLOT(slotLoadingProgress(const TQString &, float))); + + connect(m_canvas, TQ_SIGNAL(signalSavingStarted(const TQString&)), + this, TQ_SLOT(slotSavingStarted(const TQString&))); + + connect(m_canvas, TQ_SIGNAL(signalSavingFinished(const TQString&, bool)), + this, TQ_SLOT(slotSavingFinished(const TQString&, bool))); + + connect(m_canvas, TQ_SIGNAL(signalSavingProgress(const TQString&, float)), + this, TQ_SLOT(slotSavingProgress(const TQString&, float))); + + connect(m_canvas, TQ_SIGNAL(signalSelectionChanged(const TQRect&)), + this, TQ_SLOT(slotSelectionChanged(const TQRect&))); + + // -- if rotating/flipping set the rotatedflipped flag to true ----------- + + connect(d->rotateLeftAction, TQ_SIGNAL(activated()), + this, TQ_SLOT(slotRotatedOrFlipped())); + + connect(d->rotateRightAction, TQ_SIGNAL(activated()), + this, TQ_SLOT(slotRotatedOrFlipped())); + + connect(d->flipHorizAction, TQ_SIGNAL(activated()), + this, TQ_SLOT(slotRotatedOrFlipped())); + + connect(d->flipVertAction, TQ_SIGNAL(activated()), + this, TQ_SLOT(slotRotatedOrFlipped())); + + // -- status bar connections -------------------------------------- + + connect(m_nameLabel, TQ_SIGNAL(signalCancelButtonPressed()), + this, TQ_SLOT(slotNameLabelCancelButtonPressed())); + + connect(m_nameLabel, TQ_SIGNAL(signalCancelButtonPressed()), + d->toolIface, TQ_SLOT(slotToolAborted())); +} + +void EditorWindow::setupStandardActions() +{ + // -- Standard 'File' menu actions --------------------------------------------- + + m_backwardAction = KStdAction::back(this, TQ_SLOT(slotBackward()), + actionCollection(), "editorwindow_backward"); + + m_forwardAction = KStdAction::forward(this, TQ_SLOT(slotForward()), + actionCollection(), "editorwindow_forward"); + + m_firstAction = new TDEAction(i18n("&First"), "go-first", + TDEStdAccel::shortcut( TDEStdAccel::Home), + this, TQ_SLOT(slotFirst()), + actionCollection(), "editorwindow_first"); + + m_lastAction = new TDEAction(i18n("&Last"), "go-last", + TDEStdAccel::shortcut( TDEStdAccel::End), + this, TQ_SLOT(slotLast()), + actionCollection(), "editorwindow_last"); + + m_saveAction = KStdAction::save(this, TQ_SLOT(slotSave()), + actionCollection(), "editorwindow_save"); + + m_saveAsAction = KStdAction::saveAs(this, TQ_SLOT(slotSaveAs()), + actionCollection(), "editorwindow_saveas"); + + m_revertAction = KStdAction::revert(this, TQ_SLOT(slotRevert()), + actionCollection(), "editorwindow_revert"); + + m_saveAction->setEnabled(false); + m_saveAsAction->setEnabled(false); + m_revertAction->setEnabled(false); + + d->filePrintAction = new TDEAction(i18n("Print Image..."), "document-print", + CTRL+Key_P, + this, TQ_SLOT(slotFilePrint()), + actionCollection(), "editorwindow_print"); + + m_fileDeleteAction = new TDEAction(i18n("Move to Trash"), "edittrash", + Key_Delete, + this, TQ_SLOT(slotDeleteCurrentItem()), + actionCollection(), "editorwindow_delete"); + + KStdAction::close(this, TQ_SLOT(close()), actionCollection(), "editorwindow_close"); + + // -- Standard 'Edit' menu actions --------------------------------------------- + + d->copyAction = KStdAction::copy(m_canvas, TQ_SLOT(slotCopy()), + actionCollection(), "editorwindow_copy"); + + d->copyAction->setEnabled(false); + + m_undoAction = new TDEToolBarPopupAction(i18n("Undo"), "edit-undo", + TDEStdAccel::shortcut(TDEStdAccel::Undo), + m_canvas, TQ_SLOT(slotUndo()), + actionCollection(), "editorwindow_undo"); + + connect(m_undoAction->popupMenu(), TQ_SIGNAL(aboutToShow()), + this, TQ_SLOT(slotAboutToShowUndoMenu())); + + connect(m_undoAction->popupMenu(), TQ_SIGNAL(activated(int)), + m_canvas, TQ_SLOT(slotUndo(int))); + + m_undoAction->setEnabled(false); + + m_redoAction = new TDEToolBarPopupAction(i18n("Redo"), "edit-redo", + TDEStdAccel::shortcut(TDEStdAccel::Redo), + m_canvas, TQ_SLOT(slotRedo()), + actionCollection(), "editorwindow_redo"); + + connect(m_redoAction->popupMenu(), TQ_SIGNAL(aboutToShow()), + this, TQ_SLOT(slotAboutToShowRedoMenu())); + + connect(m_redoAction->popupMenu(), TQ_SIGNAL(activated(int)), + m_canvas, TQ_SLOT(slotRedo(int))); + + m_redoAction->setEnabled(false); + + d->selectAllAction = new TDEAction(i18n("Select All"), + 0, + CTRL+Key_A, + m_canvas, + TQ_SLOT(slotSelectAll()), + actionCollection(), + "editorwindow_selectAll"); + + d->selectNoneAction = new TDEAction(i18n("Select None"), + 0, + CTRL+SHIFT+Key_A, + m_canvas, + TQ_SLOT(slotSelectNone()), + actionCollection(), + "editorwindow_selectNone"); + + // -- Standard 'View' menu actions --------------------------------------------- + + d->zoomPlusAction = KStdAction::zoomIn(this, TQ_SLOT(slotIncreaseZoom()), + actionCollection(), "editorwindow_zoomplus"); + + d->zoomMinusAction = KStdAction::zoomOut(this, TQ_SLOT(slotDecreaseZoom()), + actionCollection(), "editorwindow_zoomminus"); + + d->zoomTo100percents = new TDEAction(i18n("Zoom to 100%"), "zoom-original", + ALT+CTRL+Key_0, // NOTE: Photoshop 7 use ALT+CTRL+0. + this, TQ_SLOT(slotZoomTo100Percents()), + actionCollection(), "editorwindow_zoomto100percents"); + + + d->zoomFitToWindowAction = new TDEToggleAction(i18n("Fit to &Window"), "view_fit_window", + CTRL+SHIFT+Key_E, // NOTE: Gimp 2 use CTRL+SHIFT+E. + this, TQ_SLOT(slotToggleFitToWindow()), + actionCollection(), "editorwindow_zoomfit2window"); + + d->zoomFitToSelectAction = new TDEAction(i18n("Fit to &Selection"), "zoom-fit-best", + ALT+CTRL+Key_S, this, TQ_SLOT(slotFitToSelect()), + actionCollection(), "editorwindow_zoomfit2select"); + d->zoomFitToSelectAction->setEnabled(false); + d->zoomFitToSelectAction->setWhatsThis(i18n("This option can be used to zoom the image to the " + "current selection area.")); + + d->zoomCombo = new KComboBox(true); + d->zoomCombo->setDuplicatesEnabled(false); + d->zoomCombo->setFocusPolicy(TQWidget::ClickFocus); + d->zoomCombo->setInsertionPolicy(TQComboBox::NoInsertion); + d->zoomComboAction = new KWidgetAction(d->zoomCombo, i18n("Zoom"), 0, 0, 0, + actionCollection(), "editorwindow_zoomto"); + + d->zoomCombo->insertItem(TQString("10%")); + d->zoomCombo->insertItem(TQString("25%")); + d->zoomCombo->insertItem(TQString("50%")); + d->zoomCombo->insertItem(TQString("75%")); + d->zoomCombo->insertItem(TQString("100%")); + d->zoomCombo->insertItem(TQString("150%")); + d->zoomCombo->insertItem(TQString("200%")); + d->zoomCombo->insertItem(TQString("300%")); + d->zoomCombo->insertItem(TQString("450%")); + d->zoomCombo->insertItem(TQString("600%")); + d->zoomCombo->insertItem(TQString("800%")); + d->zoomCombo->insertItem(TQString("1200%")); + + connect(d->zoomCombo, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotZoomSelected()) ); + + connect(d->zoomCombo, TQ_SIGNAL(returnPressed(const TQString&)), + this, TQ_SLOT(slotZoomTextChanged(const TQString &)) ); + + // Do not use std KDE action for full screen because action text is too large for app. toolbar. + m_fullScreenAction = new TDEToggleAction(i18n("Full Screen"), "view-fullscreen", + CTRL+SHIFT+Key_F, this, + TQ_SLOT(slotToggleFullScreen()), + actionCollection(), "editorwindow_fullscreen"); + m_fullScreenAction->setWhatsThis(i18n("Toggle the window to full screen mode")); + + d->slideShowAction = new TDEAction(i18n("Slideshow"), "slideshow", Key_F9, + this, TQ_SLOT(slotToggleSlideShow()), + actionCollection(),"editorwindow_slideshow"); + + d->viewUnderExpoAction = new TDEToggleAction(i18n("Under-Exposure Indicator"), "underexposure", + Key_F10, this, + TQ_SLOT(slotToggleUnderExposureIndicator()), + actionCollection(),"editorwindow_underexposure"); + + d->viewOverExpoAction = new TDEToggleAction(i18n("Over-Exposure Indicator"), "overexposure", + Key_F11, this, + TQ_SLOT(slotToggleOverExposureIndicator()), + actionCollection(),"editorwindow_overexposure"); + + d->viewCMViewAction = new TDEToggleAction(i18n("Color Managed View"), "tv", + Key_F12, this, + TQ_SLOT(slotToggleColorManagedView()), + actionCollection(),"editorwindow_cmview"); + + // -- Standard 'Transform' menu actions --------------------------------------------- + + d->resizeAction = new TDEAction(i18n("&Resize..."), "resize_image", 0, + this, TQ_SLOT(slotResize()), + actionCollection(), "editorwindow_resize"); + + d->cropAction = new TDEAction(i18n("Crop"), "crop", + CTRL+Key_X, + m_canvas, TQ_SLOT(slotCrop()), + actionCollection(), "editorwindow_crop"); + + d->cropAction->setEnabled(false); + d->cropAction->setWhatsThis(i18n("This option can be used to crop the image. " + "Select a region of the image to enable this action.")); + + // -- Standard 'Flip' menu actions --------------------------------------------- + + d->flipHorizAction = new TDEAction(i18n("Flip Horizontally"), "mirror", CTRL+Key_Asterisk, + m_canvas, TQ_SLOT(slotFlipHoriz()), + actionCollection(), "editorwindow_flip_horiz"); + d->flipHorizAction->setEnabled(false); + + d->flipVertAction = new TDEAction(i18n("Flip Vertically"), "flip", CTRL+Key_Slash, + m_canvas, TQ_SLOT(slotFlipVert()), + actionCollection(), "editorwindow_flip_vert"); + d->flipVertAction->setEnabled(false); + + // -- Standard 'Rotate' menu actions ---------------------------------------- + + d->rotateLeftAction = new TDEAction(i18n("Rotate Left"), + "object-rotate-left", SHIFT+CTRL+Key_Left, + m_canvas, TQ_SLOT(slotRotate270()), + actionCollection(), + "editorwindow_rotate_left"); + d->rotateLeftAction->setEnabled(false); + d->rotateRightAction = new TDEAction(i18n("Rotate Right"), + "object-rotate-right", SHIFT+CTRL+Key_Right, + m_canvas, TQ_SLOT(slotRotate90()), + actionCollection(), + "editorwindow_rotate_right"); + d->rotateRightAction->setEnabled(false); + + // -- Standard 'Configure' menu actions ---------------------------------------- + + d->showMenuBarAction = KStdAction::showMenubar(this, TQ_SLOT(slotShowMenuBar()), actionCollection()); + + KStdAction::keyBindings(this, TQ_SLOT(slotEditKeys()), actionCollection()); + KStdAction::configureToolbars(this, TQ_SLOT(slotConfToolbars()), actionCollection()); + KStdAction::preferences(this, TQ_SLOT(slotSetup()), actionCollection()); + + // ----------------------------------------------------------------------------------------- + + m_themeMenuAction = new TDESelectAction(i18n("&Themes"), 0, actionCollection(), "theme_menu"); + m_themeMenuAction->setItems(ThemeEngine::instance()->themeNames()); + + connect(m_themeMenuAction, TQ_SIGNAL(activated(const TQString&)), + this, TQ_SLOT(slotChangeTheme(const TQString&))); + + connect(ThemeEngine::instance(), TQ_SIGNAL(signalThemeChanged()), + this, TQ_SLOT(slotThemeChanged())); + + // -- Standard 'Help' menu actions --------------------------------------------- + + d->donateMoneyAction = new TDEAction(i18n("Donate..."), + 0, 0, + this, TQ_SLOT(slotDonateMoney()), + actionCollection(), + "editorwindow_donatemoney"); + + d->contributeAction = new TDEAction(i18n("Contribute..."), + 0, 0, + this, TQ_SLOT(slotContribute()), + actionCollection(), + "editorwindow_contribute"); + + d->rawCameraListAction = new TDEAction(i18n("Supported RAW Cameras"), + "kdcraw", + 0, + this, + TQ_SLOT(slotRawCameraList()), + actionCollection(), + "editorwindow_rawcameralist"); +} + +void EditorWindow::setupStandardAccelerators() +{ + d->accelerators = new TDEAccel(this); + + d->accelerators->insert("Exit fullscreen", i18n("Exit Fullscreen mode"), + i18n("Exit out of the fullscreen mode"), + Key_Escape, this, TQ_SLOT(slotEscapePressed()), + false, true); + + d->accelerators->insert("Next Image Key_Space", i18n("Next Image"), + i18n("Load Next Image"), + Key_Space, this, TQ_SLOT(slotForward()), + false, true); + + d->accelerators->insert("Previous Image SHIFT+Key_Space", i18n("Previous Image"), + i18n("Load Previous Image"), + SHIFT+Key_Space, this, TQ_SLOT(slotBackward()), + false, true); + + d->accelerators->insert("Previous Image Key_Backspace", i18n("Previous Image"), + i18n("Load Previous Image"), + Key_Backspace, this, TQ_SLOT(slotBackward()), + false, true); + + d->accelerators->insert("Next Image Key_Next", i18n("Next Image"), + i18n("Load Next Image"), + Key_Next, this, TQ_SLOT(slotForward()), + false, true); + + d->accelerators->insert("Previous Image Key_Prior", i18n("Previous Image"), + i18n("Load Previous Image"), + Key_Prior, this, TQ_SLOT(slotBackward()), + false, true); + + d->accelerators->insert("Zoom Plus Key_Plus", i18n("Zoom In"), + i18n("Zoom in on Image"), + Key_Plus, this, TQ_SLOT(slotIncreaseZoom()), + false, true); + + d->accelerators->insert("Zoom Plus Key_Minus", i18n("Zoom Out"), + i18n("Zoom out of Image"), + Key_Minus, this, TQ_SLOT(slotDecreaseZoom()), + false, true); + + d->accelerators->insert("Redo CTRL+Key_Y", i18n("Redo"), + i18n("Redo Last action"), + CTRL+Key_Y, m_canvas, TQ_SLOT(slotRedo()), + false, true); +} + +void EditorWindow::setupStatusBar() +{ + m_nameLabel = new StatusProgressBar(statusBar()); + m_nameLabel->setAlignment(TQt::AlignCenter); + m_nameLabel->setMaximumHeight(fontMetrics().height()+2); + statusBar()->addWidget(m_nameLabel, 100); + + d->selectLabel = new TQLabel(i18n("No selection"), statusBar()); + d->selectLabel->setAlignment(TQt::AlignCenter); + d->selectLabel->setMaximumHeight(fontMetrics().height()+2); + statusBar()->addWidget(d->selectLabel, 100); + TQToolTip::add(d->selectLabel, i18n("Information about current selection area")); + + m_resLabel = new TQLabel(statusBar()); + m_resLabel->setAlignment(TQt::AlignCenter); + m_resLabel->setMaximumHeight(fontMetrics().height()+2); + statusBar()->addWidget(m_resLabel, 100); + TQToolTip::add(m_resLabel, i18n("Information about image size")); + + d->underExposureIndicator = new TQToolButton(statusBar()); + d->underExposureIndicator->setIconSet(SmallIcon("underexposure")); + d->underExposureIndicator->setToggleButton(true); + statusBar()->addWidget(d->underExposureIndicator, 1); + + d->overExposureIndicator = new TQToolButton(statusBar()); + d->overExposureIndicator->setIconSet(SmallIcon("overexposure")); + d->overExposureIndicator->setToggleButton(true); + statusBar()->addWidget(d->overExposureIndicator, 1); + + d->cmViewIndicator = new TQToolButton(statusBar()); + d->cmViewIndicator->setIconSet(SmallIcon("tv")); + d->cmViewIndicator->setToggleButton(true); + statusBar()->addWidget(d->cmViewIndicator, 1); + + connect(d->underExposureIndicator, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotToggleUnderExposureIndicator())); + + connect(d->overExposureIndicator, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotToggleOverExposureIndicator())); + + connect(d->cmViewIndicator, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotToggleColorManagedView())); +} + +void EditorWindow::printImage(KURL url) +{ + uchar* ptr = m_canvas->interface()->getImage(); + int w = m_canvas->interface()->origWidth(); + int h = m_canvas->interface()->origHeight(); + bool hasAlpha = m_canvas->interface()->hasAlpha(); + bool sixteenBit = m_canvas->interface()->sixteenBit(); + + if (!ptr || !w || !h) + return; + + DImg image(w, h, sixteenBit, hasAlpha, ptr); + + KPrinter printer; + TQString appName = TDEApplication::kApplication()->aboutData()->appName(); + printer.setDocName( url.filename() ); + printer.setCreator( appName ); +#if KDE_IS_VERSION(3,2,0) + printer.setUsePrinterResolution(true); +#endif + + KPrinter::addDialogPage( new ImageEditorPrintDialogPage(image, this, TQString(appName.append(" page")).ascii() )); + + if ( printer.setup( this, i18n("Print %1").arg(printer.docName().section('/', -1)) ) ) + { + ImagePrint printOperations(image, printer, url.filename()); + if (!printOperations.printImageWithTQt()) + { + KMessageBox::error(this, i18n("Failed to print file: '%1'") + .arg(url.filename())); + } + } +} + +void EditorWindow::slotEditKeys() +{ + KKeyDialog dialog(true, this); + dialog.insert( actionCollection(), i18n( "General" ) ); + + TQPtrList<ImagePlugin> pluginList = ImagePluginLoader::instance()->pluginList(); + + for (ImagePlugin* plugin = pluginList.first(); + plugin; plugin = pluginList.next()) + { + if (plugin) + { + dialog.insert( plugin->actionCollection(), plugin->name() ); + } + } + + dialog.configure(); +} + +void EditorWindow::slotResize() +{ + ImageResize dlg(this); + dlg.exec(); +} + +void EditorWindow::slotAboutToShowUndoMenu() +{ + m_undoAction->popupMenu()->clear(); + TQStringList titles; + m_canvas->getUndoHistory(titles); + + if(!titles.isEmpty()) + { + int id = 1; + TQStringList::Iterator iter = titles.begin(); + for(; iter != titles.end(); ++iter,++id) + { + m_undoAction->popupMenu()->insertItem(*iter, id); + } + } +} + +void EditorWindow::slotAboutToShowRedoMenu() +{ + m_redoAction->popupMenu()->clear(); + TQStringList titles; + m_canvas->getRedoHistory(titles); + + if(!titles.isEmpty()) + { + int id = 1; + TQStringList::Iterator iter = titles.begin(); + for(; iter != titles.end(); ++iter,++id) + { + m_redoAction->popupMenu()->insertItem(*iter, id); + } + } +} + +void EditorWindow::slotConfToolbars() +{ + saveMainWindowSettings(TDEGlobal::config(), "ImageViewer Settings"); + KEditToolbar dlg(factory(), this); + + connect(&dlg, TQ_SIGNAL(newToolbarConfig()), + this, TQ_SLOT(slotNewToolbarConfig())); + + dlg.exec(); +} + +void EditorWindow::slotNewToolbarConfig() +{ + applyMainWindowSettings(TDEGlobal::config(), "ImageViewer Settings"); +} + +void EditorWindow::slotIncreaseZoom() +{ + m_stackView->increaseZoom(); +} + +void EditorWindow::slotDecreaseZoom() +{ + m_stackView->decreaseZoom(); +} + +void EditorWindow::slotToggleFitToWindow() +{ + d->zoomPlusAction->setEnabled(true); + d->zoomComboAction->setEnabled(true); + d->zoomMinusAction->setEnabled(true); + m_stackView->toggleFitToWindow(); +} + +void EditorWindow::slotFitToSelect() +{ + d->zoomPlusAction->setEnabled(true); + d->zoomComboAction->setEnabled(true); + d->zoomMinusAction->setEnabled(true); + m_stackView->fitToSelect(); +} + +void EditorWindow::slotZoomTo100Percents() +{ + d->zoomPlusAction->setEnabled(true); + d->zoomComboAction->setEnabled(true); + d->zoomMinusAction->setEnabled(true); + m_stackView->zoomTo100Percents(); +} + +void EditorWindow::slotZoomSelected() +{ + TQString txt = d->zoomCombo->currentText(); + txt = txt.left(txt.find('%')); + slotZoomTextChanged(txt); +} + +void EditorWindow::slotZoomTextChanged(const TQString &txt) +{ + bool r = false; + double zoom = TDEGlobal::locale()->readNumber(txt, &r) / 100.0; + if (r && zoom > 0.0) + m_stackView->setZoomFactor(zoom); +} + +void EditorWindow::slotZoomChanged(bool isMax, bool isMin, double zoom) +{ + d->zoomPlusAction->setEnabled(!isMax); + d->zoomMinusAction->setEnabled(!isMin); + + d->zoomCombo->blockSignals(true); + d->zoomCombo->setCurrentText(TQString::number(lround(zoom*100.0)) + TQString("%")); + d->zoomCombo->blockSignals(false); +} + +void EditorWindow::slotToggleOffFitToWindow() +{ + d->zoomFitToWindowAction->blockSignals(true); + d->zoomFitToWindowAction->setChecked(false); + d->zoomFitToWindowAction->blockSignals(false); +} + +void EditorWindow::slotEscapePressed() +{ + if (m_fullScreen) + m_fullScreenAction->activate(); +} + +void EditorWindow::plugActionAccel(TDEAction* action) +{ + if (!action) + return; + + d->accelerators->insert(action->text(), + action->text(), + action->whatsThis(), + action->shortcut(), + action, + TQ_SLOT(activate())); +} + +void EditorWindow::unplugActionAccel(TDEAction* action) +{ + d->accelerators->remove(action->text()); +} + +void EditorWindow::loadImagePlugins() +{ + TQPtrList<ImagePlugin> pluginList = m_imagePluginLoader->pluginList(); + + for (ImagePlugin* plugin = pluginList.first(); + plugin; plugin = pluginList.next()) + { + if (plugin) + { + guiFactory()->addClient(plugin); + plugin->setEnabledSelectionActions(false); + } + else + DDebug() << "Invalid plugin to add!" << endl; + } +} + +void EditorWindow::unLoadImagePlugins() +{ + TQPtrList<ImagePlugin> pluginList = m_imagePluginLoader->pluginList(); + + for (ImagePlugin* plugin = pluginList.first(); + plugin; plugin = pluginList.next()) + { + if (plugin) + { + guiFactory()->removeClient(plugin); + plugin->setEnabledSelectionActions(false); + } + } +} + +void EditorWindow::readStandardSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("ImageViewer Settings"); + + // Restore full screen Mode ? + + if (config->readBoolEntry("FullScreen", false)) + { + m_fullScreenAction->activate(); + m_fullScreen = true; + } + + // Restore Auto zoom action ? + bool autoZoom = config->readBoolEntry("AutoZoom", true); + if (autoZoom) + d->zoomFitToWindowAction->activate(); +} + +void EditorWindow::applyStandardSettings() +{ + TDEConfig* config = kapp->config(); + + // -- Settings for Color Management stuff ---------------------------------------------- + + config->setGroup("Color Management"); + + d->ICCSettings->enableCMSetting = config->readBoolEntry("EnableCM", false); + d->ICCSettings->askOrApplySetting = config->readBoolEntry("BehaviourICC", false); + d->ICCSettings->BPCSetting = config->readBoolEntry("BPCAlgorithm",false); + d->ICCSettings->managedViewSetting = config->readBoolEntry("ManagedView", false); + d->ICCSettings->renderingSetting = config->readNumEntry("RenderingIntent"); + d->ICCSettings->inputSetting = config->readPathEntry("InProfileFile", TQString()); + d->ICCSettings->workspaceSetting = config->readPathEntry("WorkProfileFile", TQString()); + d->ICCSettings->monitorSetting = config->readPathEntry("MonitorProfileFile", TQString()); + d->ICCSettings->proofSetting = config->readPathEntry("ProofProfileFile", TQString()); + + d->viewCMViewAction->setEnabled(d->ICCSettings->enableCMSetting); + d->viewCMViewAction->setChecked(d->ICCSettings->managedViewSetting); + d->cmViewIndicator->setEnabled(d->ICCSettings->enableCMSetting); + d->cmViewIndicator->setOn(d->ICCSettings->managedViewSetting); + setColorManagedViewIndicatorToolTip(d->ICCSettings->enableCMSetting, d->ICCSettings->managedViewSetting); + m_canvas->setICCSettings(d->ICCSettings); + + // -- JPEG, PNG, TIFF JPEG2000 files format settings -------------------------------------- + + config->setGroup("ImageViewer Settings"); + + // JPEG quality slider settings : 1 - 100 ==> libjpeg settings : 25 - 100. + m_IOFileSettings->JPEGCompression = (int)((75.0/100.0)* + (float)config->readNumEntry("JPEGCompression", 75) + + 26.0 - (75.0/100.0)); + + m_IOFileSettings->JPEGSubSampling = config->readNumEntry("JPEGSubSampling", 1); // Medium subsampling + + // PNG compression slider settings : 1 - 9 ==> libpng settings : 100 - 1. + m_IOFileSettings->PNGCompression = (int)(((1.0-100.0)/8.0)* + (float)config->readNumEntry("PNGCompression", 1) + + 100.0 - ((1.0-100.0)/8.0)); + + // TIFF compression setting. + m_IOFileSettings->TIFFCompression = config->readBoolEntry("TIFFCompression", false); + + // JPEG2000 quality slider settings : 1 - 100 + m_IOFileSettings->JPEG2000Compression = config->readNumEntry("JPEG2000Compression", 100); + + // JPEG2000 LossLess setting. + m_IOFileSettings->JPEG2000LossLess = config->readBoolEntry("JPEG2000LossLess", true); + + // -- RAW images decoding settings ------------------------------------------------------ + + // If digiKam Color Management is enable, no need to correct color of decoded RAW image, + // else, sRGB color workspace will be used. + + if (d->ICCSettings->enableCMSetting) + m_IOFileSettings->rawDecodingSettings.outputColorSpace = DRawDecoding::RAWCOLOR; + else + m_IOFileSettings->rawDecodingSettings.outputColorSpace = DRawDecoding::SRGB; + + m_IOFileSettings->rawDecodingSettings.sixteenBitsImage = config->readBoolEntry("SixteenBitsImage", false); + m_IOFileSettings->rawDecodingSettings.whiteBalance = (DRawDecoding::WhiteBalance)config->readNumEntry("WhiteBalance", + DRawDecoding::CAMERA); + m_IOFileSettings->rawDecodingSettings.customWhiteBalance = config->readNumEntry("CustomWhiteBalance", 6500); + m_IOFileSettings->rawDecodingSettings.customWhiteBalanceGreen = config->readDoubleNumEntry("CustomWhiteBalanceGreen", 1.0); + m_IOFileSettings->rawDecodingSettings.RGBInterpolate4Colors = config->readBoolEntry("RGBInterpolate4Colors", false); + m_IOFileSettings->rawDecodingSettings.DontStretchPixels = config->readBoolEntry("DontStretchPixels", false); + m_IOFileSettings->rawDecodingSettings.enableNoiseReduction = config->readBoolEntry("EnableNoiseReduction", false); + m_IOFileSettings->rawDecodingSettings.unclipColors = config->readNumEntry("UnclipColors", 0); + m_IOFileSettings->rawDecodingSettings.RAWQuality = (DRawDecoding::DecodingQuality)config->readNumEntry("RAWQuality", + DRawDecoding::BILINEAR); + m_IOFileSettings->rawDecodingSettings.NRThreshold = config->readNumEntry("NRThreshold", 100); + m_IOFileSettings->rawDecodingSettings.enableCACorrection = config->readBoolEntry("EnableCACorrection", false); + m_IOFileSettings->rawDecodingSettings.caMultiplier[0] = config->readDoubleNumEntry("caRedMultiplier", 1.0); + m_IOFileSettings->rawDecodingSettings.caMultiplier[1] = config->readDoubleNumEntry("caBlueMultiplier", 1.0); + m_IOFileSettings->rawDecodingSettings.brightness = config->readDoubleNumEntry("RAWBrightness", 1.0); + m_IOFileSettings->rawDecodingSettings.medianFilterPasses = config->readNumEntry("MedianFilterPasses", 0); + + m_IOFileSettings->useRAWImport = config->readBoolEntry("UseRawImportTool", false); + + // -- GUI Settings ------------------------------------------------------- + + TQSizePolicy rightSzPolicy(TQSizePolicy::Preferred, TQSizePolicy::Expanding, 2, 1); + if(config->hasKey("Splitter Sizes")) + m_splitter->setSizes(config->readIntListEntry("Splitter Sizes")); + else + m_canvas->setSizePolicy(rightSzPolicy); + + d->fullScreenHideToolBar = config->readBoolEntry("FullScreen Hide ToolBar", false); + + slotThemeChanged(); + + // -- Exposure Indicators Settings --------------------------------------- + + TQColor black(TQt::black); + TQColor white(TQt::white); + d->exposureSettings->underExposureIndicator = config->readBoolEntry("UnderExposureIndicator", false); + d->exposureSettings->overExposureIndicator = config->readBoolEntry("OverExposureIndicator", false); + d->exposureSettings->underExposureColor = config->readColorEntry("UnderExposureColor", &white); + d->exposureSettings->overExposureColor = config->readColorEntry("OverExposureColor", &black); + + d->viewUnderExpoAction->setChecked(d->exposureSettings->underExposureIndicator); + d->viewOverExpoAction->setChecked(d->exposureSettings->overExposureIndicator); + d->underExposureIndicator->setOn(d->exposureSettings->underExposureIndicator); + d->overExposureIndicator->setOn(d->exposureSettings->overExposureIndicator); + setUnderExposureToolTip(d->exposureSettings->underExposureIndicator); + setOverExposureToolTip(d->exposureSettings->overExposureIndicator); + m_canvas->setExposureSettings(d->exposureSettings); +} + +void EditorWindow::saveStandardSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("ImageViewer Settings"); + + config->writeEntry("AutoZoom", d->zoomFitToWindowAction->isChecked()); + config->writeEntry("Splitter Sizes", m_splitter->sizes()); + + config->writeEntry("FullScreen", m_fullScreenAction->isChecked()); + config->writeEntry("UnderExposureIndicator", d->exposureSettings->underExposureIndicator); + config->writeEntry("OverExposureIndicator", d->exposureSettings->overExposureIndicator); + + config->sync(); +} + +/** Method used by Editor Tools. Only Zoom+ and Zoom- are currently supported. + TODO: Fix this behavour when editor tool preview widgets will be factored. + */ +void EditorWindow::toggleZoomActions(bool val) +{ + d->zoomMinusAction->setEnabled(val); + d->zoomPlusAction->setEnabled(val); +} + +void EditorWindow::toggleStandardActions(bool val) +{ + d->zoomComboAction->setEnabled(val); + d->zoomTo100percents->setEnabled(val); + d->zoomFitToWindowAction->setEnabled(val); + d->zoomFitToSelectAction->setEnabled(val); + toggleZoomActions(val); + + d->rotateLeftAction->setEnabled(val); + d->rotateRightAction->setEnabled(val); + d->flipHorizAction->setEnabled(val); + d->flipVertAction->setEnabled(val); + d->filePrintAction->setEnabled(val); + d->resizeAction->setEnabled(val); + m_fileDeleteAction->setEnabled(val); + m_saveAsAction->setEnabled(val); + d->selectAllAction->setEnabled(val); + d->selectNoneAction->setEnabled(val); + d->slideShowAction->setEnabled(val); + + // these actions are special: They are turned off if val is false, + // but if val is true, they may be turned on or off. + if (val) + { + // Trigger sending of signalUndoStateChanged + // Note that for saving and loading, this is not necessary + // because the signal will be sent later anyway. + m_canvas->updateUndoState(); + } + else + { + m_saveAction->setEnabled(val); + m_undoAction->setEnabled(val); + m_redoAction->setEnabled(val); + } + + TQPtrList<ImagePlugin> pluginList = m_imagePluginLoader->pluginList(); + + for (ImagePlugin* plugin = pluginList.first(); + plugin; plugin = pluginList.next()) + { + if (plugin) + { + plugin->setEnabledActions(val); + } + } +} + +void EditorWindow::slotToggleFullScreen() +{ + if (m_fullScreen) // out of fullscreen + { + m_canvas->setBackgroundColor(m_bgColor); + + setWindowState( windowState() & ~WindowFullScreen ); + menuBar()->show(); + statusBar()->show(); + leftDock()->show(); + rightDock()->show(); + topDock()->show(); + bottomDock()->show(); + + TQObject* obj = child("ToolBar","TDEToolBar"); + + if (obj) + { + TDEToolBar* toolBar = static_cast<TDEToolBar*>(obj); + + if (m_fullScreenAction->isPlugged(toolBar) && d->removeFullScreenButton) + m_fullScreenAction->unplug(toolBar); + + if (toolBar->isHidden()) + showToolBars(); + } + + // -- remove the gui action accels ---- + + unplugActionAccel(m_forwardAction); + unplugActionAccel(m_backwardAction); + unplugActionAccel(m_firstAction); + unplugActionAccel(m_lastAction); + unplugActionAccel(m_saveAction); + unplugActionAccel(m_saveAsAction); + unplugActionAccel(d->zoomPlusAction); + unplugActionAccel(d->zoomMinusAction); + unplugActionAccel(d->zoomFitToWindowAction); + unplugActionAccel(d->zoomFitToSelectAction); + unplugActionAccel(d->cropAction); + unplugActionAccel(d->filePrintAction); + unplugActionAccel(m_fileDeleteAction); + unplugActionAccel(d->selectAllAction); + unplugActionAccel(d->selectNoneAction); + + toggleGUI2FullScreen(); + m_fullScreen = false; + } + else // go to fullscreen + { + m_canvas->setBackgroundColor(TQColor(TQt::black)); + + // hide the menubar and the statusbar + menuBar()->hide(); + statusBar()->hide(); + topDock()->hide(); + leftDock()->hide(); + rightDock()->hide(); + bottomDock()->hide(); + + TQObject* obj = child("ToolBar","TDEToolBar"); + + if (obj) + { + TDEToolBar* toolBar = static_cast<TDEToolBar*>(obj); + + if (d->fullScreenHideToolBar) + { + hideToolBars(); + } + else + { + showToolBars(); + + if ( !m_fullScreenAction->isPlugged(toolBar) ) + { + m_fullScreenAction->plug(toolBar); + d->removeFullScreenButton=true; + } + else + { + // If FullScreen button is enable in toolbar settings + // We don't remove it when we out of fullscreen mode. + d->removeFullScreenButton=false; + } + } + } + + // -- Insert all the gui actions into the accel -- + + plugActionAccel(m_forwardAction); + plugActionAccel(m_backwardAction); + plugActionAccel(m_firstAction); + plugActionAccel(m_lastAction); + plugActionAccel(m_saveAction); + plugActionAccel(m_saveAsAction); + plugActionAccel(d->zoomPlusAction); + plugActionAccel(d->zoomMinusAction); + plugActionAccel(d->zoomFitToWindowAction); + plugActionAccel(d->zoomFitToSelectAction); + plugActionAccel(d->cropAction); + plugActionAccel(d->filePrintAction); + plugActionAccel(m_fileDeleteAction); + plugActionAccel(d->selectAllAction); + plugActionAccel(d->selectNoneAction); + + toggleGUI2FullScreen(); + showFullScreen(); + m_fullScreen = true; + } +} + +void EditorWindow::slotRotatedOrFlipped() +{ + m_rotatedOrFlipped = true; +} + +void EditorWindow::slotLoadingProgress(const TQString&, float progress) +{ + m_nameLabel->setProgressValue((int)(progress*100.0)); +} + +void EditorWindow::slotSavingProgress(const TQString&, float progress) +{ + m_nameLabel->setProgressValue((int)(progress*100.0)); +} + +bool EditorWindow::promptForOverWrite() +{ + TQFileInfo fi(m_canvas->currentImageFilePath()); + TQString warnMsg(i18n("About to overwrite file \"%1\"\nAre you sure?") + .arg(fi.fileName())); + return (KMessageBox::warningContinueCancel(this, + warnMsg, + i18n("Warning"), + i18n("Overwrite"), + "editorWindowSaveOverwrite") + == KMessageBox::Continue); +} + +bool EditorWindow::promptUserSave(const KURL& url) +{ + if (m_saveAction->isEnabled()) + { + // if window is iconified, show it + if (isMinimized()) + { + KWin::deIconifyWindow(winId()); + } + + int result = KMessageBox::warningYesNoCancel(this, + i18n("The image '%1' has been modified.\n" + "Do you want to save it?") + .arg(url.filename()), + TQString(), + KStdGuiItem::save(), + KStdGuiItem::discard()); + + if (result == KMessageBox::Yes) + { + bool saving = false; + + if (m_canvas->isReadOnly()) + saving = saveAs(); + else if (promptForOverWrite()) + saving = save(); + + // save and saveAs return false if they were cancelled and did not enter saving at all + // In this case, do not call enter_loop because exit_loop will not be called. + if (saving) + { + // Waiting for asynchronous image file saving operation runing in separate thread. + m_savingContext->synchronizingState = SavingContextContainer::SynchronousSaving; + enter_loop(); + m_savingContext->synchronizingState = SavingContextContainer::NormalSaving; + return m_savingContext->synchronousSavingResult; + } + else + { + return false; + } + } + else if (result == KMessageBox::No) + { + m_saveAction->setEnabled(false); + return true; + } + else + { + return false; + } + } + + return true; +} + +bool EditorWindow::waitForSavingToComplete() +{ + // avoid reentrancy - return false means we have reentered the loop already. + if (m_savingContext->synchronizingState == SavingContextContainer::SynchronousSaving) + return false; + + if (m_savingContext->savingState != SavingContextContainer::SavingStateNone) + { + // Waiting for asynchronous image file saving operation runing in separate thread. + m_savingContext->synchronizingState = SavingContextContainer::SynchronousSaving; + KMessageBox::queuedMessageBox(this, + KMessageBox::Information, + i18n("Please wait while the image is being saved...")); + enter_loop(); + m_savingContext->synchronizingState = SavingContextContainer::NormalSaving; + } + return true; +} + +void EditorWindow::enter_loop() +{ + TQWidget dummy(0, 0, WType_Dialog | WShowModal); + dummy.setFocusPolicy( TQWidget::NoFocus ); + tqt_enter_modal(&dummy); + tqApp->enter_loop(); + tqt_leave_modal(&dummy); +} + +void EditorWindow::slotSelected(bool val) +{ + // Update menu actions. + d->cropAction->setEnabled(val); + d->zoomFitToSelectAction->setEnabled(val); + d->copyAction->setEnabled(val); + + for (ImagePlugin* plugin = m_imagePluginLoader->pluginList().first(); + plugin; plugin = m_imagePluginLoader->pluginList().next()) + { + if (plugin) + { + plugin->setEnabledSelectionActions(val); + } + } + + TQRect sel = m_canvas->getSelectedArea(); + // Update histogram into sidebar. + emit signalSelectionChanged(sel); + + // Update status bar + if (val) + d->selectLabel->setText(TQString("(%1, %2) (%3 x %4)").arg(sel.x()).arg(sel.y()) + .arg(sel.width()).arg(sel.height())); + else + d->selectLabel->setText(i18n("No selection")); +} + +void EditorWindow::hideToolBars() +{ + TQPtrListIterator<TDEToolBar> it = toolBarIterator(); + TDEToolBar* bar; + + for(;it.current()!=0L; ++it) + { + bar = it.current(); + + if (bar->area()) + bar->area()->hide(); + else + bar->hide(); + } +} + +void EditorWindow::showToolBars() +{ + TQPtrListIterator<TDEToolBar> it = toolBarIterator(); + TDEToolBar* bar; + + for( ; it.current()!=0L ; ++it) + { + bar = it.current(); + + if (bar->area()) + bar->area()->show(); + else + bar->show(); + } +} + +void EditorWindow::slotPrepareToLoad() +{ + // Disable actions as appropriate during loading + emit signalNoCurrentItem(); + toggleActions(false); + slotUpdateItemInfo(); +} + +void EditorWindow::slotLoadingStarted(const TQString& /*filename*/) +{ + setCursor( KCursor::waitCursor() ); + + m_nameLabel->progressBarMode(StatusProgressBar::ProgressBarMode, i18n("Loading: ")); +} + +void EditorWindow::slotLoadingFinished(const TQString& filename, bool success) +{ + m_nameLabel->progressBarMode(StatusProgressBar::TextMode); + slotUpdateItemInfo(); + + // Enable actions as appropriate after loading + // No need to re-enable image properties sidebar here, it's will be done + // automatically by a signal from canvas + toggleActions(success); + unsetCursor(); + + // Note: in showfoto, we using a null filename to clear canvas. + if (!success && filename != TQString()) + { + TQFileInfo fi(filename); + TQString message = i18n("Failed to load image \"%1\"").arg(fi.fileName()); + KMessageBox::error(this, message); + DWarning() << "Failed to load image " << fi.fileName() << endl; + } +} + +void EditorWindow::slotNameLabelCancelButtonPressed() +{ + // If we saving an image... + if (m_savingContext->savingState != SavingContextContainer::SavingStateNone) + { + m_savingContext->abortingSaving = true; + m_canvas->abortSaving(); + } + + // If we preparing SlideShow... + m_cancelSlideShow = true; +} + +void EditorWindow::slotSave() +{ + if (m_canvas->isReadOnly()) + saveAs(); + else if (promptForOverWrite()) + save(); +} + +void EditorWindow::slotSavingStarted(const TQString& /*filename*/) +{ + setCursor( KCursor::waitCursor() ); + + // Disable actions as appropriate during saving + emit signalNoCurrentItem(); + toggleActions(false); + + m_nameLabel->progressBarMode(StatusProgressBar::CancelProgressBarMode, i18n("Saving: ")); +} + +void EditorWindow::slotSavingFinished(const TQString& filename, bool success) +{ + if (m_savingContext->savingState == SavingContextContainer::SavingStateSave) + { + // from save() + m_savingContext->savingState = SavingContextContainer::SavingStateNone; + + if (!success) + { + if (!m_savingContext->abortingSaving) + { + KMessageBox::error(this, i18n("Failed to save file\n\"%1\"\nto\n\"%2\".") + .arg(m_savingContext->destinationURL.filename()) + .arg(m_savingContext->destinationURL.path())); + } + finishSaving(false); + return; + } + + DDebug() << "renaming to " << m_savingContext->destinationURL.path() << endl; + + if (!moveFile()) + { + finishSaving(false); + return; + } + + m_canvas->setUndoHistoryOrigin(); + + // remove image from cache since it has changed + LoadingCacheInterface::cleanFromCache(m_savingContext->destinationURL.path()); + // this won't be in the cache, but does not hurt to do it anyway + LoadingCacheInterface::cleanFromCache(filename); + + // restore state of disabled actions. saveIsComplete can start any other task + // (loading!) which might itself in turn change states + finishSaving(true); + + saveIsComplete(); + + // Take all actions necessary to update information and re-enable sidebar + slotChanged(); + } + else if (m_savingContext->savingState == SavingContextContainer::SavingStateSaveAs) + { + m_savingContext->savingState = SavingContextContainer::SavingStateNone; + + // from saveAs() + if (!success) + { + if (!m_savingContext->abortingSaving) + { + KMessageBox::error(this, i18n("Failed to save file\n\"%1\"\nto\n\"%2\".") + .arg(m_savingContext->destinationURL.filename()) + .arg(m_savingContext->destinationURL.path())); + } + finishSaving(false); + return; + } + + // Only try to write exif if both src and destination are jpeg files + + DDebug() << "renaming to " << m_savingContext->destinationURL.path() << endl; + + if (!moveFile()) + { + finishSaving(false); + return; + } + + m_canvas->setUndoHistoryOrigin(); + + LoadingCacheInterface::cleanFromCache(m_savingContext->destinationURL.path()); + LoadingCacheInterface::cleanFromCache(filename); + + finishSaving(true); + saveAsIsComplete(); + + // Take all actions necessary to update information and re-enable sidebar + slotChanged(); + } +} + +void EditorWindow::finishSaving(bool success) +{ + m_savingContext->synchronousSavingResult = success; + + if (m_savingContext->saveTempFile) + { + delete m_savingContext->saveTempFile; + m_savingContext->saveTempFile = 0; + } + + // Exit of internal TQt event loop to unlock promptUserSave() method. + if (m_savingContext->synchronizingState == SavingContextContainer::SynchronousSaving) + tqApp->exit_loop(); + + // Enable actions as appropriate after saving + toggleActions(true); + unsetCursor(); + + m_nameLabel->progressBarMode(StatusProgressBar::TextMode); + + // On error, continue using current image + if (!success) + { + m_canvas->switchToLastSaved(m_savingContext->srcURL.path()); + } +} + +void EditorWindow::startingSave(const KURL& url) +{ + // avoid any reentrancy. Should be impossible anyway since actions will be disabled. + if (m_savingContext->savingState != SavingContextContainer::SavingStateNone) + return; + + if (!checkPermissions(url)) + return; + + m_savingContext->srcURL = url; + m_savingContext->destinationURL = m_savingContext->srcURL; + m_savingContext->destinationExisted = true; + m_savingContext->originalFormat = m_canvas->currentImageFileFormat(); + m_savingContext->format = m_savingContext->originalFormat; + m_savingContext->abortingSaving = false; + m_savingContext->savingState = SavingContextContainer::SavingStateSave; + // use magic file extension which tells the digikamalbums ioslave to ignore the file + m_savingContext->saveTempFile = new KTempFile(m_savingContext->srcURL.directory(false), + ".digikamtempfile.tmp"); + m_savingContext->saveTempFile->setAutoDelete(true); + + m_canvas->saveAs(m_savingContext->saveTempFile->name(), m_IOFileSettings, + m_setExifOrientationTag && (m_rotatedOrFlipped || m_canvas->exifRotated())); +} + +bool EditorWindow::startingSaveAs(const KURL& url) +{ + if (m_savingContext->savingState != SavingContextContainer::SavingStateNone) + return false; + + TQString mimetypes = KImageIO::mimeTypes(KImageIO::Writing).join(" "); + mimetypes.append(" image/tiff"); + DDebug () << "mimetypes=" << mimetypes << endl; + + m_savingContext->srcURL = url; + + FileSaveOptionsBox *options = new FileSaveOptionsBox(); + KFileDialog imageFileSaveDialog(m_savingContext->srcURL.isLocalFile() ? + m_savingContext->srcURL.directory() : TQDir::homeDirPath(), + TQString(), + this, + "imageFileSaveDialog", + false, + options); + + connect(&imageFileSaveDialog, TQ_SIGNAL(filterChanged(const TQString &)), + options, TQ_SLOT(slotImageFileFormatChanged(const TQString &))); + + connect(&imageFileSaveDialog, TQ_SIGNAL(fileSelected(const TQString &)), + options, TQ_SLOT(slotImageFileSelected(const TQString &))); + + ImageDialogPreview *preview = new ImageDialogPreview(&imageFileSaveDialog); + imageFileSaveDialog.setPreviewWidget(preview); + imageFileSaveDialog.setOperationMode(KFileDialog::Saving); + imageFileSaveDialog.setMode(KFile::File); + imageFileSaveDialog.setCaption(i18n("New Image File Name")); + imageFileSaveDialog.setFilter(mimetypes); + + TQFileInfo info(m_savingContext->srcURL.fileName()); + TDEConfig* config = kapp->config(); + config->setGroup("ImageViewer Settings"); + TQString ext = config->readEntry("LastSavedImageTypeMime", "png"); + TQString fileName = info.baseName(false) + TQString(".") + ext; + imageFileSaveDialog.setSelection(fileName); + + // Start dialog and check if canceled. + if ( imageFileSaveDialog.exec() != KFileDialog::Accepted ) + return false; + + // Update file save settings in editor instance. + options->applySettings(); + applyStandardSettings(); + + KURL newURL = imageFileSaveDialog.selectedURL(); + + // Check if target image format have been selected from Combo List of SaveAs dialog. + m_savingContext->format = KImageIO::typeForMime(imageFileSaveDialog.currentMimeFilter()); + + if ( m_savingContext->format.isEmpty() ) + { + // Else, check if target image format have been add to target image file name using extension. + + TQFileInfo fi(newURL.path()); + m_savingContext->format = fi.extension(false); + + if ( m_savingContext->format.isEmpty() ) + { + // If format is empty then file format is same as that of the original file. + m_savingContext->format = TQImageIO::imageFormat(m_savingContext->srcURL.path()); + } + else + { + // Else, check if format from file name extension is include on file mime type list. + + TQString imgExtPattern; + TQStringList imgExtList = TQStringList::split(" ", mimetypes); + for (TQStringList::ConstIterator it = imgExtList.begin() ; it != imgExtList.end() ; ++it) + { + imgExtPattern.append (KImageIO::typeForMime(*it).upper()); + imgExtPattern.append (" "); + } + imgExtPattern.append (" TIF TIFF"); + if ( imgExtPattern.contains("JPEG") ) + { + imgExtPattern.append (" JPG"); + imgExtPattern.append (" JPE"); + } + + if ( !imgExtPattern.contains( m_savingContext->format.upper() ) ) + { + KMessageBox::error(this, i18n("Target image file format \"%1\" unsupported.") + .arg(m_savingContext->format)); + DWarning() << k_funcinfo << "target image file format " << m_savingContext->format << " unsupported!" << endl; + return false; + } + } + } + + if (!newURL.isValid()) + { + KMessageBox::error(this, i18n("Failed to save file\n\"%1\" to\n\"%2\".") + .arg(newURL.filename()) + .arg(newURL.path().section('/', -2, -2))); + DWarning() << k_funcinfo << "target URL is not valid !" << endl; + return false; + } + + config->writeEntry("LastSavedImageTypeMime", m_savingContext->format); + config->sync(); + + // if new and original url are equal use slotSave() ------------------------------ + + KURL currURL(m_savingContext->srcURL); + currURL.cleanPath(); + newURL.cleanPath(); + + if (currURL.equals(newURL)) + { + slotSave(); + return false; + } + + // Check for overwrite ---------------------------------------------------------- + + TQFileInfo fi(newURL.path()); + m_savingContext->destinationExisted = fi.exists(); + if ( m_savingContext->destinationExisted ) + { + int result = + + KMessageBox::warningYesNo( this, i18n("A file named \"%1\" already " + "exists. Are you sure you want " + "to overwrite it?") + .arg(newURL.filename()), + i18n("Overwrite File?"), + i18n("Overwrite"), + KStdGuiItem::cancel() ); + + if (result != KMessageBox::Yes) + return false; + + // There will be two message boxes if the file is not writable. + // This may be controversial, and it may be changed, but it was a deliberate decision. + if (!checkPermissions(newURL)) + return false; + } + + // Now do the actual saving ----------------------------------------------------- + + // use magic file extension which tells the digikamalbums ioslave to ignore the file + m_savingContext->saveTempFile = new KTempFile(newURL.directory(false), ".digikamtempfile.tmp"); + m_savingContext->destinationURL = newURL; + m_savingContext->originalFormat = m_canvas->currentImageFileFormat(); + m_savingContext->savingState = SavingContextContainer::SavingStateSaveAs; + m_savingContext->saveTempFile->setAutoDelete(true); + m_savingContext->abortingSaving = false; + + m_canvas->saveAs(m_savingContext->saveTempFile->name(), m_IOFileSettings, + m_setExifOrientationTag && (m_rotatedOrFlipped || m_canvas->exifRotated()), + m_savingContext->format.lower()); + + return true; +} + +bool EditorWindow::checkPermissions(const KURL& url) +{ + //TODO: Check that the permissions can actually be changed + // if write permissions are not available. + + TQFileInfo fi(url.path()); + + if (fi.exists() && !fi.isWritable()) + { + int result = + + KMessageBox::warningYesNo( this, i18n("You do not have write permissions " + "for the file named \"%1\". " + "Are you sure you want " + "to overwrite it?") + .arg(url.filename()), + i18n("Overwrite File?"), + i18n("Overwrite"), + KStdGuiItem::cancel() ); + + if (result != KMessageBox::Yes) + return false; + } + + return true; +} + +bool EditorWindow::moveFile() +{ + TQCString dstFileName = TQFile::encodeName(m_savingContext->destinationURL.path()); + + // Store old permissions: + // Just get the current umask. + mode_t curr_umask = umask(S_IREAD | S_IWRITE); + // Restore the umask. + umask(curr_umask); + + // For new files respect the umask setting. + mode_t filePermissions = (S_IREAD | S_IWRITE | S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP) & ~curr_umask; + + // For existing files, use the mode of the original file. + if (m_savingContext->destinationExisted) + { + struct stat stbuf; + if (::stat(dstFileName, &stbuf) == 0) + { + filePermissions = stbuf.st_mode; + } + } + + // rename tmp file to dest + if (::rename(TQFile::encodeName(m_savingContext->saveTempFile->name()), dstFileName) != 0) + { + KMessageBox::error(this, i18n("Failed to overwrite original file"), + i18n("Error Saving File")); + return false; + } + + // restore permissions + if (::chmod(dstFileName, filePermissions) != 0) + { + DWarning() << "Failed to restore file permissions for file " << dstFileName << endl; + } + + return true; +} + +void EditorWindow::slotToggleColorManagedView() +{ + d->cmViewIndicator->blockSignals(true); + d->viewCMViewAction->blockSignals(true); + bool cmv = false; + if (d->ICCSettings->enableCMSetting) + { + cmv = !d->ICCSettings->managedViewSetting; + d->ICCSettings->managedViewSetting = cmv; + m_canvas->setICCSettings(d->ICCSettings); + + // Save Color Managed View setting in config file. For performance + // reason, no need to flush file, it cached in memory and will be flushed + // to disk at end of session. + TDEConfig* config = kapp->config(); + config->setGroup("Color Management"); + config->writeEntry("ManagedView", cmv); + } + + d->cmViewIndicator->setOn(cmv); + d->viewCMViewAction->setChecked(cmv); + setColorManagedViewIndicatorToolTip(d->ICCSettings->enableCMSetting, cmv); + d->cmViewIndicator->blockSignals(false); + d->viewCMViewAction->blockSignals(false); +} + +void EditorWindow::setColorManagedViewIndicatorToolTip(bool available, bool cmv) +{ + TQToolTip::remove(d->cmViewIndicator); + TQString tooltip; + if (available) + { + if (cmv) + tooltip = i18n("Color Managed View is enabled"); + else + tooltip = i18n("Color Managed View is disabled"); + } + else + { + tooltip = i18n("Color Management is not configured, so the Color Managed View is not available"); + } + TQToolTip::add(d->cmViewIndicator, tooltip); +} + +void EditorWindow::slotToggleUnderExposureIndicator() +{ + d->underExposureIndicator->blockSignals(true); + d->viewUnderExpoAction->blockSignals(true); + bool uei = !d->exposureSettings->underExposureIndicator; + d->underExposureIndicator->setOn(uei); + d->viewUnderExpoAction->setChecked(uei); + d->exposureSettings->underExposureIndicator = uei; + m_canvas->setExposureSettings(d->exposureSettings); + setUnderExposureToolTip(uei); + d->underExposureIndicator->blockSignals(false); + d->viewUnderExpoAction->blockSignals(false); +} + +void EditorWindow::setUnderExposureToolTip(bool uei) +{ + TQToolTip::remove(d->underExposureIndicator); + TQToolTip::add(d->underExposureIndicator, + uei ? i18n("Under-Exposure indicator is enabled") + : i18n("Under-Exposure indicator is disabled")); +} + +void EditorWindow::slotToggleOverExposureIndicator() +{ + d->overExposureIndicator->blockSignals(true); + d->viewOverExpoAction->blockSignals(true); + bool oei = !d->exposureSettings->overExposureIndicator; + d->overExposureIndicator->setOn(oei); + d->viewOverExpoAction->setChecked(oei); + d->exposureSettings->overExposureIndicator = oei; + m_canvas->setExposureSettings(d->exposureSettings); + setOverExposureToolTip(oei); + d->overExposureIndicator->blockSignals(false); + d->viewOverExpoAction->blockSignals(false); +} + +void EditorWindow::setOverExposureToolTip(bool oei) +{ + TQToolTip::remove(d->overExposureIndicator); + TQToolTip::add(d->overExposureIndicator, + oei ? i18n("Over-Exposure indicator is enabled") + : i18n("Over-Exposure indicator is disabled")); +} + +void EditorWindow::slotDonateMoney() +{ + TDEApplication::kApplication()->invokeBrowser("http://www.digikam.org/?q=donation"); +} + +void EditorWindow::slotContribute() +{ + TDEApplication::kApplication()->invokeBrowser("http://www.digikam.org/?q=contrib"); +} + +void EditorWindow::slotToggleSlideShow() +{ + TDEConfig* config = kapp->config(); + config->setGroup("ImageViewer Settings"); + bool startWithCurrent = config->readBoolEntry("SlideShowStartCurrent", false); + + SlideShowSettings settings; + settings.delay = config->readNumEntry("SlideShowDelay", 5) * 1000; + settings.printName = config->readBoolEntry("SlideShowPrintName", true); + settings.printDate = config->readBoolEntry("SlideShowPrintDate", false); + settings.printApertureFocal = config->readBoolEntry("SlideShowPrintApertureFocal", false); + settings.printExpoSensitivity = config->readBoolEntry("SlideShowPrintExpoSensitivity", false); + settings.printMakeModel = config->readBoolEntry("SlideShowPrintMakeModel", false); + settings.printComment = config->readBoolEntry("SlideShowPrintComment", false); + settings.loop = config->readBoolEntry("SlideShowLoop", false); + slideShow(startWithCurrent, settings); +} + +void EditorWindow::slotSelectionChanged(const TQRect& sel) +{ + d->selectLabel->setText(TQString("(%1, %2) (%3 x %4)").arg(sel.x()).arg(sel.y()) + .arg(sel.width()).arg(sel.height())); +} + +void EditorWindow::slotRawCameraList() +{ + RawCameraDlg dlg(this); + dlg.exec(); +} + +void EditorWindow::slotThemeChanged() +{ + TQStringList themes(ThemeEngine::instance()->themeNames()); + int index = themes.findIndex(ThemeEngine::instance()->getCurrentThemeName()); + if (index == -1) + index = themes.findIndex(i18n("Default")); + + m_themeMenuAction->setCurrentItem(index); + + TDEConfig* config = kapp->config(); + config->setGroup("ImageViewer Settings"); + + if (!config->readBoolEntry("UseThemeBackgroundColor", true)) + m_bgColor = config->readColorEntry("BackgroundColor", &TQt::black); + else + m_bgColor = ThemeEngine::instance()->baseColor(); + + m_canvas->setBackgroundColor(m_bgColor); +} + +void EditorWindow::slotChangeTheme(const TQString& theme) +{ + ThemeEngine::instance()->slotChangeTheme(theme); +} + +void EditorWindow::setToolStartProgress(const TQString& toolName) +{ + m_nameLabel->setProgressValue(0); + m_nameLabel->progressBarMode(StatusProgressBar::CancelProgressBarMode, TQString("%1: ").arg(toolName)); +} + +void EditorWindow::setToolProgress(int progress) +{ + m_nameLabel->setProgressValue(progress); +} + +void EditorWindow::setToolStopProgress() +{ + m_nameLabel->setProgressValue(0); + m_nameLabel->progressBarMode(StatusProgressBar::TextMode); + slotUpdateItemInfo(); +} + + +void EditorWindow::slotShowMenuBar() +{ + if (menuBar()->isVisible()) + menuBar()->hide(); + else + menuBar()->show(); +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/editor/editorwindow.h b/src/utilities/imageeditor/editor/editorwindow.h new file mode 100644 index 00000000..cbb06221 --- /dev/null +++ b/src/utilities/imageeditor/editor/editorwindow.h @@ -0,0 +1,263 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-01-20 + * Description : main image editor GUI implementation + * + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef EDITORWINDOW_H +#define EDITORWINDOW_H + +// TQt includes. + +#include <tqcolor.h> +#include <tqstring.h> +#include <tqrect.h> + +// KDE includes. + +#include <tdemainwindow.h> +#include <kurl.h> + +// Local includes. + +#include "sidebar.h" +#include "digikam_export.h" + +class TQSplitter; +class TQPopupMenu; +class TQLabel; + +class TDEToolBarPopupAction; +class TDEToggleAction; +class TDEAction; +class TDESelectAction; + +namespace Digikam +{ + +class Sidebar; +class DPopupMenu; +class Canvas; +class ImagePluginLoader; +class IOFileSettingsContainer; +class SavingContextContainer; +class StatusProgressBar; +class SlideShowSettings; +class EditorStackView; +class EditorWindowPriv; + +class DIGIKAM_EXPORT EditorWindow : public TDEMainWindow +{ + TQ_OBJECT + + +public: + + EditorWindow(const char *name); + ~EditorWindow(); + + virtual void applySettings(){}; + virtual bool setup(bool iccSetupPage=false)=0; + +signals: + + void signalSelectionChanged( const TQRect & ); + void signalNoCurrentItem(); + +protected: + + bool m_cancelSlideShow; + bool m_fullScreen; + bool m_rotatedOrFlipped; + bool m_setExifOrientationTag; + + TQLabel *m_resLabel; + + TQColor m_bgColor; + + TQSplitter *m_splitter; + + TDEAction *m_saveAction; + TDEAction *m_saveAsAction; + TDEAction *m_revertAction; + TDEAction *m_fileDeleteAction; + TDEAction *m_forwardAction; + TDEAction *m_backwardAction; + TDEAction *m_firstAction; + TDEAction *m_lastAction; + + TDEToggleAction *m_fullScreenAction; + + TDESelectAction *m_themeMenuAction; + + TDEToolBarPopupAction *m_undoAction; + TDEToolBarPopupAction *m_redoAction; + + DPopupMenu *m_contextMenu; + EditorStackView *m_stackView; + Canvas *m_canvas; + ImagePluginLoader *m_imagePluginLoader; + StatusProgressBar *m_nameLabel; + IOFileSettingsContainer *m_IOFileSettings; + SavingContextContainer *m_savingContext; + +protected: + + void saveStandardSettings(); + void readStandardSettings(); + void applyStandardSettings(); + + void setupStandardConnections(); + void setupStandardActions(); + void setupStandardAccelerators(); + void setupStatusBar(); + void setupContextMenu(); + void toggleStandardActions(bool val); + void toggleZoomActions(bool val); + + void printImage(KURL url); + + void plugActionAccel(TDEAction* action); + void unplugActionAccel(TDEAction* action); + + void unLoadImagePlugins(); + void loadImagePlugins(); + + bool promptForOverWrite(); + bool promptUserSave(const KURL& url); + bool waitForSavingToComplete(); + void startingSave(const KURL& url); + bool startingSaveAs(const KURL& url); + bool checkPermissions(const KURL& url); + bool moveFile(); + + EditorStackView* editorStackView() const; + + virtual void finishSaving(bool success); + + virtual void readSettings() { readStandardSettings(); }; + virtual void saveSettings() { saveStandardSettings(); }; + virtual void toggleActions(bool val) { toggleStandardActions(val); }; + virtual void toggleGUI2FullScreen() {}; + + virtual void slideShow(bool startWithCurrent, SlideShowSettings& settings)=0; + + virtual void setupConnections()=0; + virtual void setupActions()=0; + virtual void setupUserArea()=0; + virtual bool saveAs()=0; + virtual bool save()=0; + + virtual void saveIsComplete()=0; + virtual void saveAsIsComplete()=0; + + virtual Sidebar *rightSideBar() const=0; + +protected slots: + + void slotSave(); + void slotSaveAs() { saveAs(); }; + + void slotEditKeys(); + void slotResize(); + + void slotAboutToShowUndoMenu(); + void slotAboutToShowRedoMenu(); + + void slotConfToolbars(); + void slotNewToolbarConfig(); + + void slotToggleFullScreen(); + void slotEscapePressed(); + + void slotSelected(bool); + + void slotLoadingProgress(const TQString& filePath, float progress); + void slotSavingProgress(const TQString& filePath, float progress); + + void slotNameLabelCancelButtonPressed(); + + void slotThemeChanged(); + + virtual void slotLoadingStarted(const TQString& filename); + virtual void slotLoadingFinished(const TQString &filename, bool success); + virtual void slotSavingStarted(const TQString &filename); + + virtual void slotSetup(){ setup(); }; + virtual void slotChangeTheme(const TQString& theme); + + virtual void slotFilePrint()=0; + virtual void slotDeleteCurrentItem()=0; + virtual void slotBackward()=0; + virtual void slotForward()=0; + virtual void slotFirst()=0; + virtual void slotLast()=0; + virtual void slotUpdateItemInfo()=0; + virtual void slotChanged()=0; + virtual void slotContextMenu()=0; + virtual void slotRevert()=0; + +private slots: + + void slotToggleUnderExposureIndicator(); + void slotToggleOverExposureIndicator(); + void slotToggleColorManagedView(); + void slotRotatedOrFlipped(); + void slotSavingFinished(const TQString &filename, bool success); + void slotDonateMoney(); + void slotContribute(); + void slotToggleSlideShow(); + void slotZoomTo100Percents(); + void slotZoomSelected(); + void slotZoomTextChanged(const TQString &); + void slotZoomChanged(bool isMax, bool isMin, double zoom); + void slotSelectionChanged(const TQRect& sel); + void slotToggleFitToWindow(); + void slotToggleOffFitToWindow(); + void slotFitToSelect(); + void slotIncreaseZoom(); + void slotDecreaseZoom(); + void slotRawCameraList(); + void slotPrepareToLoad(); + void slotShowMenuBar(); + +private: + + void enter_loop(); + void hideToolBars(); + void showToolBars(); + void setColorManagedViewIndicatorToolTip(bool available, bool cmv); + void setUnderExposureToolTip(bool uei); + void setOverExposureToolTip(bool oei); + + void setToolStartProgress(const TQString& toolName); + void setToolProgress(int progress); + void setToolStopProgress(); + +private: + + EditorWindowPriv *d; + + friend class EditorToolIface; +}; + +} // namespace Digikam + +#endif /* EDITORWINDOW_H */ diff --git a/src/utilities/imageeditor/editor/editorwindowprivate.h b/src/utilities/imageeditor/editor/editorwindowprivate.h new file mode 100644 index 00000000..df07d0bd --- /dev/null +++ b/src/utilities/imageeditor/editor/editorwindowprivate.h @@ -0,0 +1,143 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-01-20 + * Description : main image editor GUI implementation + * private data. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef EDITORWINDOWPRIVATE_H +#define EDITORWINDOWPRIVATE_H + +class TQToolButton; +class TQLabel; + +class KComboBox; +class TDEAction; +class TDEToggleAction; +class KWidgetAction; +class TDESelectAction; +class TDEActionMenu; +class TDEAccel; + +namespace Digikam +{ + +class EditorToolIface; +class ExposureSettingsContainer; +class ICCSettingsContainer; + +class EditorWindowPriv +{ + +public: + + EditorWindowPriv() + { + removeFullScreenButton = false; + fullScreenHideToolBar = false; + selectLabel = 0; + donateMoneyAction = 0; + accelerators = 0; + viewCMViewAction = 0; + filePrintAction = 0; + copyAction = 0; + resizeAction = 0; + cropAction = 0; + rotateLeftAction = 0; + rotateRightAction = 0; + flipHorizAction = 0; + flipVertAction = 0; + ICCSettings = 0; + exposureSettings = 0; + underExposureIndicator = 0; + overExposureIndicator = 0; + cmViewIndicator = 0; + viewUnderExpoAction = 0; + viewOverExpoAction = 0; + slideShowAction = 0; + zoomFitToWindowAction = 0; + zoomFitToSelectAction = 0; + zoomPlusAction = 0; + zoomMinusAction = 0; + zoomTo100percents = 0; + zoomCombo = 0; + zoomComboAction = 0; + selectAllAction = 0; + selectNoneAction = 0; + rawCameraListAction = 0; + contributeAction = 0; + toolIface = 0; + showMenuBarAction = 0; + } + + ~EditorWindowPriv() + { + } + + bool removeFullScreenButton; + bool fullScreenHideToolBar; + + TQLabel *selectLabel; + + TQToolButton *cmViewIndicator; + TQToolButton *underExposureIndicator; + TQToolButton *overExposureIndicator; + + TDEAction *rawCameraListAction; + TDEAction *donateMoneyAction; + TDEAction *contributeAction; + TDEAction *filePrintAction; + TDEAction *copyAction; + TDEAction *resizeAction; + TDEAction *cropAction; + TDEAction *zoomPlusAction; + TDEAction *zoomMinusAction; + TDEAction *zoomTo100percents; + TDEAction *zoomFitToSelectAction; + TDEAction *rotateLeftAction; + TDEAction *rotateRightAction; + TDEAction *flipHorizAction; + TDEAction *flipVertAction; + TDEAction *slideShowAction; + TDEAction *selectAllAction; + TDEAction *selectNoneAction; + + TDEToggleAction *zoomFitToWindowAction; + TDEToggleAction *viewCMViewAction; + TDEToggleAction *viewUnderExpoAction; + TDEToggleAction *viewOverExpoAction; + TDEToggleAction *showMenuBarAction; + + KWidgetAction *zoomComboAction; + + KComboBox *zoomCombo; + + TDEAccel *accelerators; + + ICCSettingsContainer *ICCSettings; + + ExposureSettingsContainer *exposureSettings; + + EditorToolIface *toolIface; +}; + +} // NameSpace Digikam + +#endif /* EDITORWINDOWPRIVATE_H */ diff --git a/src/utilities/imageeditor/editor/imageiface.cpp b/src/utilities/imageeditor/editor/imageiface.cpp new file mode 100644 index 00000000..56cb559c --- /dev/null +++ b/src/utilities/imageeditor/editor/imageiface.cpp @@ -0,0 +1,444 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-02-14 + * Description : image data interface for image plugins + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqwidget.h> +#include <tqsize.h> +#include <tqpixmap.h> +#include <tqbitmap.h> +#include <tqpainter.h> + +// Local includes. + +#include "ddebug.h" +#include "exposurecontainer.h" +#include "iccsettingscontainer.h" +#include "icctransform.h" +#include "dimginterface.h" +#include "bcgmodifier.h" +#include "dmetadata.h" +#include "imageiface.h" + +namespace Digikam +{ + +class ImageIfacePriv +{ +public: + + ImageIfacePriv() + { + usePreviewSelection = false; + previewWidth = 0; + previewHeight = 0; + } + + bool usePreviewSelection; + + int originalWidth; + int originalHeight; + int originalBytesDepth; + + int constrainWidth; + int constrainHeight; + + int previewWidth; + int previewHeight; + + TQPixmap qcheck; + TQPixmap qpix; + TQBitmap qmask; + + DImg previewImage; + DImg targetPreviewImage; +}; + +ImageIface::ImageIface(int w, int h) +{ + d = new ImageIfacePriv; + + d->constrainWidth = w; + d->constrainHeight = h; + + d->originalWidth = DImgInterface::defaultInterface()->origWidth(); + d->originalHeight = DImgInterface::defaultInterface()->origHeight(); + d->originalBytesDepth = DImgInterface::defaultInterface()->bytesDepth(); + + d->qpix.setMask(d->qmask); + d->qcheck.resize(8, 8); + + TQPainter p; + p.begin(&d->qcheck); + p.fillRect(0, 0, 4, 4, TQColor(144,144,144)); + p.fillRect(4, 4, 4, 4, TQColor(144,144,144)); + p.fillRect(0, 4, 4, 4, TQColor(100,100,100)); + p.fillRect(4, 0, 4, 4, TQColor(100,100,100)); + p.end(); +} + +ImageIface::~ImageIface() +{ + delete d; +} + +void ImageIface::setPreviewType(bool useSelect) +{ + d->usePreviewSelection = useSelect; +} + +bool ImageIface::previewType() +{ + return d->usePreviewSelection; +} + +DColor ImageIface::getColorInfoFromOriginalImage(const TQPoint& point) +{ + if ( !DImgInterface::defaultInterface()->getImage() || point.x() > originalWidth() || point.y() > originalHeight() ) + { + DWarning() << k_funcinfo << "Coordinate out of range or no image data available!" << endl; + return DColor(); + } + + return DImgInterface::defaultInterface()->getImg()->getPixelColor(point.x(), point.y()); +} + +DColor ImageIface::getColorInfoFromPreviewImage(const TQPoint& point) +{ + if ( d->previewImage.isNull() || point.x() > previewWidth() || point.y() > previewHeight() ) + { + DWarning() << k_funcinfo << "Coordinate out of range or no image data available!" << endl; + return DColor(); + } + + return d->previewImage.getPixelColor(point.x(), point.y()); +} + +DColor ImageIface::getColorInfoFromTargetPreviewImage(const TQPoint& point) +{ + if ( d->targetPreviewImage.isNull() || point.x() > previewWidth() || point.y() > previewHeight() ) + { + DWarning() << k_funcinfo << "Coordinate out of range or no image data available!" << endl; + return DColor(); + } + + return d->targetPreviewImage.getPixelColor(point.x(), point.y()); +} + +uchar* ImageIface::setPreviewImageSize(int w, int h) const +{ + d->previewImage.reset(); + d->targetPreviewImage.reset(); + + d->constrainWidth = w; + d->constrainHeight = h; + + return (getPreviewImage()); +} + +uchar* ImageIface::getPreviewImage() const +{ + if (d->previewImage.isNull()) + { + DImg *im = 0; + + if (!d->usePreviewSelection) + { + im = DImgInterface::defaultInterface()->getImg(); + if (!im || im->isNull()) + return 0; + } + else + { + int x, y, w, h; + bool s = DImgInterface::defaultInterface()->sixteenBit(); + bool a = DImgInterface::defaultInterface()->hasAlpha(); + uchar *data = DImgInterface::defaultInterface()->getImageSelection(); + DImgInterface::defaultInterface()->getSelectedArea(x, y, w, h); + im = new DImg(w, h, s, a, data, true); + delete [] data; + + if (!im) + return 0; + + if (im->isNull()) + { + delete im; + return 0; + } + } + + TQSize sz(im->width(), im->height()); + sz.scale(d->constrainWidth, d->constrainHeight, TQSize::ScaleMin); + + d->previewImage = im->smoothScale(sz.width(), sz.height()); + d->previewWidth = d->previewImage.width(); + d->previewHeight = d->previewImage.height(); + + // only create another copy if needed, in putPreviewImage + d->targetPreviewImage = d->previewImage; + + d->qmask.resize(d->previewWidth, d->previewHeight); + d->qpix.resize(d->previewWidth, d->previewHeight); + + if (d->usePreviewSelection) + delete im; + } + + DImg previewData = d->previewImage.copyImageData(); + return previewData.stripImageData(); +} + +uchar* ImageIface::getOriginalImage() const +{ + DImg *im = DImgInterface::defaultInterface()->getImg(); + + if (!im || im->isNull()) + return 0; + + DImg origData = im->copyImageData(); + return origData.stripImageData(); +} + +DImg* ImageIface::getOriginalImg() const +{ + return DImgInterface::defaultInterface()->getImg(); +} + +uchar* ImageIface::getImageSelection() const +{ + return DImgInterface::defaultInterface()->getImageSelection(); +} + +void ImageIface::putPreviewImage(uchar* data) +{ + if (!data) + return; + + if (d->targetPreviewImage == d->previewImage) + { + d->targetPreviewImage = DImg(d->previewImage.width(), d->previewImage.height(), + d->previewImage.sixteenBit(), d->previewImage.hasAlpha(), data); + d->targetPreviewImage.setICCProfil( d->previewImage.getICCProfil() ); + } + else + { + d->targetPreviewImage.putImageData(data); + } +} + +void ImageIface::putOriginalImage(const TQString &caller, uchar* data, int w, int h) +{ + if (!data) + return; + + DImgInterface::defaultInterface()->putImage(caller, data, w, h); +} + +void ImageIface::setEmbeddedICCToOriginalImage(const TQString& profilePath) +{ + DImgInterface::defaultInterface()->setEmbeddedICCToOriginalImage( profilePath ); +} + +void ImageIface::putImageSelection(const TQString &caller, uchar* data) +{ + if (!data) + return; + + DImgInterface::defaultInterface()->putImageSelection(caller, data); +} + +int ImageIface::previewWidth() +{ + return d->previewWidth; +} + +int ImageIface::previewHeight() +{ + return d->previewHeight; +} + +bool ImageIface::previewSixteenBit() +{ + return originalSixteenBit(); +} + +bool ImageIface::previewHasAlpha() +{ + return originalHasAlpha(); +} + +int ImageIface::originalWidth() +{ + return DImgInterface::defaultInterface()->origWidth(); +} + +int ImageIface::originalHeight() +{ + return DImgInterface::defaultInterface()->origHeight(); +} + +bool ImageIface::originalSixteenBit() +{ + return DImgInterface::defaultInterface()->sixteenBit(); +} + +bool ImageIface::originalHasAlpha() +{ + return DImgInterface::defaultInterface()->hasAlpha(); +} + +int ImageIface::selectedWidth() +{ + int x, y, w, h; + DImgInterface::defaultInterface()->getSelectedArea(x, y, w, h); + return w; +} + +int ImageIface::selectedHeight() +{ + int x, y, w, h; + DImgInterface::defaultInterface()->getSelectedArea(x, y, w, h); + return h; +} + +int ImageIface::selectedXOrg() +{ + int x, y, w, h; + DImgInterface::defaultInterface()->getSelectedArea(x, y, w, h); + return x; +} + +int ImageIface::selectedYOrg() +{ + int x, y, w, h; + DImgInterface::defaultInterface()->getSelectedArea(x, y, w, h); + return y; +} + +void ImageIface::setPreviewBCG(double brightness, double contrast, double gamma) +{ + DImg preview = d->targetPreviewImage.copyImageData(); + BCGModifier cmod; + cmod.setGamma(gamma); + cmod.setBrightness(brightness); + cmod.setContrast(contrast); + cmod.applyBCG(preview); + putPreviewImage(preview.bits()); +} + +void ImageIface::setOriginalBCG(double brightness, double contrast, double gamma) +{ + DImgInterface::defaultInterface()->setBCG(brightness, contrast, gamma); +} + +void ImageIface::convertOriginalColorDepth(int depth) +{ + DImgInterface::defaultInterface()->convertDepth(depth); +} + +TQPixmap ImageIface::convertToPixmap(DImg& img) +{ + return DImgInterface::defaultInterface()->convertToPixmap(img); +} + +TQByteArray ImageIface::getEmbeddedICCFromOriginalImage() +{ + return DImgInterface::defaultInterface()->getEmbeddedICC(); +} + +TQByteArray ImageIface::getExifFromOriginalImage() +{ + return DImgInterface::defaultInterface()->getExif(); +} + +TQByteArray ImageIface::getIptcFromOriginalImage() +{ + return DImgInterface::defaultInterface()->getIptc(); +} + +PhotoInfoContainer ImageIface::getPhotographInformations() const +{ + DMetadata meta; + meta.setExif(DImgInterface::defaultInterface()->getExif()); + meta.setIptc(DImgInterface::defaultInterface()->getIptc()); + return meta.getPhotographInformations(); +} + +void ImageIface::paint(TQPaintDevice* device, int x, int y, int w, int h, + bool underExposure, bool overExposure) +{ + if ( !d->targetPreviewImage.isNull() ) + { + if (d->targetPreviewImage.hasAlpha()) + { + TQPainter p(&d->qpix); + p.drawTiledPixmap(0, 0, d->qpix.width(), d->qpix.height(), d->qcheck); + p.end(); + } + + TQPixmap pixImage; + ICCSettingsContainer *iccSettings = DImgInterface::defaultInterface()->getICCSettings(); + + if (iccSettings) + { + IccTransform monitorICCtrans; + monitorICCtrans.setProfiles(iccSettings->workspaceSetting, iccSettings->monitorSetting); + + if (iccSettings->enableCMSetting && iccSettings->managedViewSetting) + { + pixImage = d->targetPreviewImage.convertToPixmap(&monitorICCtrans); + } + else + { + pixImage = d->targetPreviewImage.convertToPixmap(); + } + } + else + { + pixImage = d->targetPreviewImage.convertToPixmap(); + } + + bitBlt(&d->qpix, 0, 0, static_cast<TQPaintDevice*>(&pixImage), 0, 0, w, h, TQt::CopyROP, false); + + // Show the Over/Under exposure pixels indicators + + if (underExposure || overExposure) + { + ExposureSettingsContainer expoSettings; + expoSettings.underExposureIndicator = underExposure; + expoSettings.overExposureIndicator = overExposure; + expoSettings.underExposureColor = DImgInterface::defaultInterface()->underExposureColor(); + expoSettings.overExposureColor = DImgInterface::defaultInterface()->overExposureColor(); + + TQImage pureColorMask = d->targetPreviewImage.pureColorMask(&expoSettings); + TQPixmap pixMask(pureColorMask); + bitBlt(&d->qpix, 0, 0, static_cast<TQPaintDevice*>(&pixMask), 0, 0, w, h, TQt::CopyROP, false); + } + } + + bitBlt(device, x, y, static_cast<TQPaintDevice*>(&d->qpix), 0, 0, -1, -1, TQt::CopyROP, false); +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/editor/imageiface.h b/src/utilities/imageeditor/editor/imageiface.h new file mode 100644 index 00000000..272c62a5 --- /dev/null +++ b/src/utilities/imageeditor/editor/imageiface.h @@ -0,0 +1,198 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-02-14 + * Description : image data interface for image plugins + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef IMAGEIFACE_H +#define IMAGEIFACE_H + +// TQt includes. + +#include <tqglobal.h> +#include <tqstring.h> + +// KDE includes. + +#include <tdelocale.h> + +// Local includes. + +#include "dimg.h" +#include "dcolor.h" +#include "photoinfocontainer.h" +#include "digikam_export.h" + +#define MAX3(a, b, c) (TQMAX(TQMAX(a,b),b)) +#define MIN3(a, b, c) (TQMIN(TQMIN(a,b),b)) +#define ROUND(x) ((int) ((x) + 0.5)) + +class TQPaintDevice; + +namespace Digikam +{ + +class ImageIfacePriv; + +class DIGIKAM_EXPORT ImageIface +{ +public: + + ImageIface(int w=0, int h=0); + ~ImageIface(); + + /** Use this method to use the current selection in editor instead the full + image to render the preview. + */ + void setPreviewType(bool useSelect=false); + + /** Return 'true' if the preview is rendered using the current selection in editor. + Return 'false' if the preview is rendered using the full image in editor. + */ + bool previewType(); + + /** Return image data for the current, scaled preview image. + The preview...() methods provide the characteristics of the data + (width, heigh, sixteen bit, alpha). + Ownership of the returned buffer is passed to the caller. + */ + uchar* getPreviewImage() const; + + /** Return image data for the current original image selection. + The selectionWidth(), selectionHeight(), originalSixteenBit() + and originalHasAlpha() methods provide the characteristics of the data. + Ownership of the returned buffer is passed to the caller. + */ + uchar* getImageSelection() const; + + /** Return image data for the original image. + The preview...() methods provide the characteristics of the data. + Ownership of the returned buffer is passed to the caller. + */ + uchar* getOriginalImage() const; + + /** Return a pointer to the DImg object representing the original image. + This object may not be modified or stored. Make copies if you need. + */ + DImg* getOriginalImg() const; + + /** Replace the image data of the original image with the given data. + The characteristics of the data must match the characteristics of + the original image as returned by the original...() methods, + respectively the given width and height parameters. + No ownership of the data pointer is assumed. + If w == -1 and h == -1, the size is unchanged. + Caller is an i18n'ed string that will be shown as the undo/redo action name. + */ + void putOriginalImage(const TQString &caller, uchar* data, int w=-1, int h=-1); + + /** Embed the Color Profile we have used in ICC plugin when this option is + selected + */ + void setEmbeddedICCToOriginalImage(const TQString& profilePath); + + /** Replace the data of the current original image selection with the given data. + The characteristics of the data must match the characteristics of the current + selection as returned by the selectionWidth(), selectionHeight(), + originalSixteenBit() and originalHasAlpha() methods. + No ownership of the data pointer is assumed. + Caller is an i18n'ed string that will be shown as the undo/redo action name. + */ + void putImageSelection(const TQString &caller, uchar* data); + + /** Replace the stored target preview data with the given data. + The characteristics of the data must match the characteristics of the current + as returned by the preview...() methods. + The target preview data is used by the paint() and + getColorInfoFromTargetPreviewImage() methods. + The data returned by getPreviewImage() is unaffected. + No ownership of the data pointer is assumed. + */ + void putPreviewImage(uchar* data); + + /** Get colors from original, (unchanged) preview + or target preview (set by putPreviewImage) image. + */ + + DColor getColorInfoFromOriginalImage(const TQPoint& point); + DColor getColorInfoFromPreviewImage(const TQPoint& point); + DColor getColorInfoFromTargetPreviewImage(const TQPoint& point); + + /** Original image information.*/ + int originalWidth(); + int originalHeight(); + bool originalSixteenBit(); + bool originalHasAlpha(); + + /** Original image metadata.*/ + TQByteArray getEmbeddedICCFromOriginalImage(); + TQByteArray getExifFromOriginalImage(); + TQByteArray getIptcFromOriginalImage(); + + /** Get photograph information from original image.*/ + PhotoInfoContainer getPhotographInformations() const; + + /** Standard methods to get/set preview information.*/ + int previewWidth(); + int previewHeight(); + bool previewHasAlpha(); + bool previewSixteenBit(); + + /** Sets preview size and returns new preview data as with getPreviewImage. + The parameters are only hints, previewWidth() and previewHeight() + may differ from w and h. + */ + uchar* setPreviewImageSize(int w, int h) const; + + /** Standard methods to get image selection information.*/ + int selectedWidth(); + int selectedHeight(); + + /** Get selected (X, Y) position on the top/left corner of the original image.*/ + int selectedXOrg(); + int selectedYOrg(); + + /** Set BCG correction for preview and original image */ + void setPreviewBCG(double brightness, double contrast, double gamma); + void setOriginalBCG(double brightness, double contrast, double gamma); + + /** Convert depth of original image */ + void convertOriginalColorDepth(int depth); + + /** Convert a DImg image to a pixmap for screen using color + managemed view if necessary */ + TQPixmap convertToPixmap(DImg& img); + + /** Paint the current target preview image (or the preview image, + if putPreviewImage has not been called) on the given paint device. + at x|y, with given maximum width and height. + */ + void paint(TQPaintDevice* device, int x, int y, int w, int h, + bool underExposure=false, bool overExposure=false); + +private: + + ImageIfacePriv* d; +}; + +} // namespace Digikam + +#endif /* IMAGEIFACE_H */ diff --git a/src/utilities/imageeditor/editor/imagewindow.cpp b/src/utilities/imageeditor/editor/imagewindow.cpp new file mode 100644 index 00000000..2c10a6f6 --- /dev/null +++ b/src/utilities/imageeditor/editor/imagewindow.cpp @@ -0,0 +1,1263 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-02-12 + * Description : digiKam image editor GUI + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// C++ includes. + +#include <cstdio> + +// TQt includes. + +#include <tqcursor.h> +#include <tqtimer.h> +#include <tqlabel.h> +#include <tqimage.h> +#include <tqsplitter.h> +#include <tqpainter.h> +#include <tqpixmap.h> + +// KDE includes. + +#include <kcursor.h> +#include <tdelocale.h> +#include <tdeconfig.h> +#include <kstandarddirs.h> +#include <tdeapplication.h> +#include <tdemessagebox.h> +#include <tdetempfile.h> +#include <kimageio.h> +#include <tdefiledialog.h> +#include <tdeversion.h> +#include <tdemenubar.h> +#include <tdetoolbar.h> +#include <tdeaccel.h> +#include <tdeaction.h> +#include <tdestdaccel.h> +#include <kstdaction.h> +#include <kstdguiitem.h> +#include <kstatusbar.h> +#include <kprogress.h> +#include <twin.h> + +// Local includes. + +#include "constants.h" +#include "ddebug.h" +#include "dlogoaction.h" +#include "dpopupmenu.h" +#include "dragobjects.h" +#include "canvas.h" +#include "dimginterface.h" +#include "dimg.h" +#include "dmetadata.h" +#include "imageplugin.h" +#include "imagepluginloader.h" +#include "imageprint.h" +#include "albummanager.h" +#include "album.h" +#include "albumdb.h" +#include "albumsettings.h" +#include "syncjob.h" +#include "imageinfo.h" +#include "imagepropertiessidebardb.h" +#include "tagspopupmenu.h" +#include "ratingpopupmenu.h" +#include "slideshow.h" +#include "setup.h" +#include "iccsettingscontainer.h" +#include "iofilesettingscontainer.h" +#include "loadingcacheinterface.h" +#include "savingcontextcontainer.h" +#include "statusprogressbar.h" +#include "imageattributeswatch.h" +#include "deletedialog.h" +#include "metadatahub.h" +#include "themeengine.h" +#include "editorstackview.h" +#include "imagewindow.h" +#include "imagewindow.moc" + +namespace Digikam +{ + +class ImageWindowPriv +{ + +public: + + ImageWindowPriv() + { + allowSaving = true; + star0 = 0; + star1 = 0; + star2 = 0; + star3 = 0; + star4 = 0; + star5 = 0; + fileDeletePermanentlyAction = 0; + fileDeletePermanentlyDirectlyAction = 0; + fileTrashDirectlyAction = 0; + imageInfoCurrent = 0; + rightSidebar = 0; + } + + // If image editor is launched by camera interface, current + // image cannot be saved. + bool allowSaving; + + KURL::List urlList; + KURL urlCurrent; + + // Rating actions. + TDEAction *star0; + TDEAction *star1; + TDEAction *star2; + TDEAction *star3; + TDEAction *star4; + TDEAction *star5; + + // Delete actions + TDEAction *fileDeletePermanentlyAction; + TDEAction *fileDeletePermanentlyDirectlyAction; + TDEAction *fileTrashDirectlyAction; + + ImageInfoList imageInfoList; + ImageInfo *imageInfoCurrent; + + ImagePropertiesSideBarDB *rightSidebar; +}; + +ImageWindow* ImageWindow::m_instance = 0; + +ImageWindow* ImageWindow::imagewindow() +{ + if (!m_instance) + new ImageWindow(); + + return m_instance; +} + +bool ImageWindow::imagewindowCreated() +{ + return m_instance; +} + +ImageWindow::ImageWindow() + : EditorWindow( "Image Editor" ) +{ + d = new ImageWindowPriv; + m_instance = this; + setAcceptDrops(true); + + // -- Build the GUI ------------------------------- + + setupUserArea(); + setupStatusBar(); + setupActions(); + + // Load image plugins to GUI + + m_imagePluginLoader = ImagePluginLoader::instance(); + loadImagePlugins(); + + // Create context menu. + + setupContextMenu(); + + // Make signals/slots connections + + setupConnections(); + + // -- Read settings -------------------------------- + + readSettings(); + applySettings(); + setAutoSaveSettings("ImageViewer Settings"); + + //------------------------------------------------------------- + + d->rightSidebar->loadViewState(); + d->rightSidebar->populateTags(); +} + +ImageWindow::~ImageWindow() +{ + m_instance = 0; + + unLoadImagePlugins(); + + // No need to delete m_imagePluginLoader instance here, it will be done by main interface. + + delete d->rightSidebar; + delete d; +} + +Sidebar* ImageWindow::rightSideBar() const +{ + return dynamic_cast<Sidebar*>(d->rightSidebar); +} + +void ImageWindow::closeEvent(TQCloseEvent* e) +{ + if (!e) + return; + + if (!queryClose()) + return; + + // put right side bar in a defined state + emit signalNoCurrentItem(); + + m_canvas->resetImage(); + + saveSettings(); + + e->accept(); +} + +bool ImageWindow::queryClose() +{ + // Note: we reimplement closeEvent above for this window. + // Additionally, queryClose is called from DigikamApp. + + // wait if a save operation is currently running + if (!waitForSavingToComplete()) + return false; + + return promptUserSave(d->urlCurrent); +} + +void ImageWindow::setupConnections() +{ + setupStandardConnections(); + + // To toggle properly keyboards shortcuts from comments & tags side bar tab. + + connect(d->rightSidebar, TQ_SIGNAL(signalNextItem()), + this, TQ_SLOT(slotForward())); + + connect(d->rightSidebar, TQ_SIGNAL(signalPrevItem()), + this, TQ_SLOT(slotBackward())); + + connect(this, TQ_SIGNAL(signalSelectionChanged( const TQRect &)), + d->rightSidebar, TQ_SLOT(slotImageSelectionChanged( const TQRect &))); + + connect(this, TQ_SIGNAL(signalNoCurrentItem()), + d->rightSidebar, TQ_SLOT(slotNoCurrentItem())); + + ImageAttributesWatch *watch = ImageAttributesWatch::instance(); + + connect(watch, TQ_SIGNAL(signalFileMetadataChanged(const KURL &)), + this, TQ_SLOT(slotFileMetadataChanged(const KURL &))); +} + +void ImageWindow::setupUserArea() +{ + TQWidget* widget = new TQWidget(this); + TQHBoxLayout *lay = new TQHBoxLayout(widget); + + m_splitter = new TQSplitter(widget); + m_stackView = new EditorStackView(m_splitter); + m_canvas = new Canvas(m_stackView); + m_stackView->setCanvas(m_canvas); + m_stackView->setViewMode(EditorStackView::CanvasMode); + + m_canvas->makeDefaultEditingCanvas(); + + TQSizePolicy rightSzPolicy(TQSizePolicy::Preferred, TQSizePolicy::Expanding, 2, 1); + m_canvas->setSizePolicy(rightSzPolicy); + + d->rightSidebar = new ImagePropertiesSideBarDB(widget, "ImageEditor Right Sidebar", m_splitter, + Sidebar::Right, true); + lay->addWidget(m_splitter); + lay->addWidget(d->rightSidebar); + + m_splitter->setFrameStyle( TQFrame::NoFrame ); + m_splitter->setFrameShadow( TQFrame::Plain ); + m_splitter->setFrameShape( TQFrame::NoFrame ); + m_splitter->setOpaqueResize(false); + setCentralWidget(widget); +} + +void ImageWindow::setupActions() +{ + setupStandardActions(); + + // Provides a menu entry that allows showing/hiding the toolbar(s) + setStandardToolBarMenuEnabled(true); + + // Provides a menu entry that allows showing/hiding the statusbar + createStandardStatusBarAction(); + + // -- Rating actions --------------------------------------------------------------- + + d->star0 = new TDEAction(i18n("Assign Rating \"No Stars\""), CTRL+Key_0, + this, TQ_SLOT(slotAssignRatingNoStar()), + actionCollection(), "imageview_ratenostar"); + d->star1 = new TDEAction(i18n("Assign Rating \"One Star\""), CTRL+Key_1, + this, TQ_SLOT(slotAssignRatingOneStar()), + actionCollection(), "imageview_rateonestar"); + d->star2 = new TDEAction(i18n("Assign Rating \"Two Stars\""), CTRL+Key_2, + this, TQ_SLOT(slotAssignRatingTwoStar()), + actionCollection(), "imageview_ratetwostar"); + d->star3 = new TDEAction(i18n("Assign Rating \"Three Stars\""), CTRL+Key_3, + this, TQ_SLOT(slotAssignRatingThreeStar()), + actionCollection(), "imageview_ratethreestar"); + d->star4 = new TDEAction(i18n("Assign Rating \"Four Stars\""), CTRL+Key_4, + this, TQ_SLOT(slotAssignRatingFourStar()), + actionCollection(), "imageview_ratefourstar"); + d->star5 = new TDEAction(i18n("Assign Rating \"Five Stars\""), CTRL+Key_5, + this, TQ_SLOT(slotAssignRatingFiveStar()), + actionCollection(), "imageview_ratefivestar"); + + // -- Special Delete actions --------------------------------------------------------------- + + // Pop up dialog to ask user whether to permanently delete + d->fileDeletePermanentlyAction = new TDEAction(i18n("Delete File Permanently"), + "edit-delete", + SHIFT+Key_Delete, + this, + TQ_SLOT(slotDeleteCurrentItemPermanently()), + actionCollection(), + "image_delete_permanently"); + + // These two actions are hidden, no menu entry, no toolbar entry, no shortcut. + // Power users may add them. + d->fileDeletePermanentlyDirectlyAction = new TDEAction(i18n("Delete Permanently without Confirmation"), + "edit-delete", + 0, + this, + TQ_SLOT(slotDeleteCurrentItemPermanentlyDirectly()), + actionCollection(), + "image_delete_permanently_directly"); + + d->fileTrashDirectlyAction = new TDEAction(i18n("Move to Trash without Confirmation"), + "edittrash", + 0, + this, + TQ_SLOT(slotTrashCurrentItemDirectly()), + actionCollection(), + "image_trash_directly"); + + // --------------------------------------------------------------------------------- + + new DLogoAction(actionCollection(), "logo_action"); + + createGUI("digikamimagewindowui.rc", false); + + setupStandardAccelerators(); +} + +void ImageWindow::applySettings() +{ + applyStandardSettings(); + + AlbumSettings *settings = AlbumSettings::instance(); + m_canvas->setExifOrient(settings->getExifRotate()); + m_setExifOrientationTag = settings->getExifSetOrientation(); + refreshView(); +} + +void ImageWindow::refreshView() +{ + d->rightSidebar->refreshTagsView(); +} + +void ImageWindow::loadURL(const KURL::List& urlList, const KURL& urlCurrent, + const TQString& caption, bool allowSaving) +{ + if (!promptUserSave(d->urlCurrent)) + return; + + d->urlList = urlList; + d->urlCurrent = urlCurrent; + d->imageInfoList = ImageInfoList(); + d->imageInfoCurrent = 0; + + loadCurrentList(caption, allowSaving); +} + +void ImageWindow::loadImageInfos(const ImageInfoList &imageInfoList, ImageInfo *imageInfoCurrent, + const TQString& caption, bool allowSaving) +{ + // The ownership of objects of imageInfoList is passed to us. + // imageInfoCurrent is contained in imageInfoList. + + // Very first thing is to check for changes, user may choose to cancel operation + if (!promptUserSave(d->urlCurrent)) + { + // delete objects from list + for (ImageInfoList::iterator it = imageInfoList.begin(); it != imageInfoList.end(); ++it) + delete *it; + return; + } + + // take over ImageInfo list + d->imageInfoList = imageInfoList; + d->imageInfoCurrent = imageInfoCurrent; + + d->imageInfoList.setAutoDelete(true); + + // create URL list + d->urlList = KURL::List(); + + ImageInfoListIterator it(d->imageInfoList); + ImageInfo *info; + for (; (info = it.current()); ++it) + { + d->urlList.append(info->kurl()); + } + + d->urlCurrent = d->imageInfoCurrent->kurl(); + + loadCurrentList(caption, allowSaving); +} + +void ImageWindow::loadCurrentList(const TQString& caption, bool allowSaving) +{ + // this method contains the code shared by loadURL and loadImageInfos + + // if window is iconified, show it + if (isMinimized()) + { + KWin::deIconifyWindow(winId()); + } + + if (!caption.isEmpty()) + setCaption(i18n("Image Editor - %1").arg(caption)); + else + setCaption(i18n("Image Editor")); + + d->allowSaving = allowSaving; + + m_saveAction->setEnabled(false); + m_revertAction->setEnabled(false); + m_undoAction->setEnabled(false); + m_redoAction->setEnabled(false); + + TQTimer::singleShot(0, this, TQ_SLOT(slotLoadCurrent())); +} + +void ImageWindow::slotLoadCurrent() +{ + KURL::List::iterator it = d->urlList.find(d->urlCurrent); + + if (it != d->urlList.end()) + { + m_canvas->load(d->urlCurrent.path(), m_IOFileSettings); + + ++it; + if (it != d->urlList.end()) + m_canvas->preload((*it).path()); + } + + // Do this _after_ the canvas->load(), so that the main view histogram does not load + // a smaller version if a raw image, and after that the DImgInterface loads the full version. + // So first let DImgInterface create its loading task, only then any external objects. + setViewToURL(d->urlCurrent); +} + +void ImageWindow::setViewToURL(const KURL &url) +{ + emit signalURLChanged(url); +} + +void ImageWindow::slotForward() +{ + if(!promptUserSave(d->urlCurrent)) + return; + + KURL::List::iterator it = d->urlList.find(d->urlCurrent); + int index = d->imageInfoList.find(d->imageInfoCurrent); + + if (it != d->urlList.end()) + { + if (d->urlCurrent != d->urlList.last()) + { + KURL urlNext = *(++it); + d->imageInfoCurrent = d->imageInfoList.at(index + 1); + d->urlCurrent = urlNext; + slotLoadCurrent(); + } + } +} + +void ImageWindow::slotBackward() +{ + if(!promptUserSave(d->urlCurrent)) + return; + + KURL::List::iterator it = d->urlList.find(d->urlCurrent); + int index = d->imageInfoList.find(d->imageInfoCurrent); + + if (it != d->urlList.begin()) + { + if (d->urlCurrent != d->urlList.first()) + { + KURL urlPrev = *(--it); + d->imageInfoCurrent = d->imageInfoList.at(index - 1); + d->urlCurrent = urlPrev; + slotLoadCurrent(); + } + } +} + +void ImageWindow::slotFirst() +{ + if(!promptUserSave(d->urlCurrent)) + return; + + d->urlCurrent = d->urlList.first(); + d->imageInfoCurrent = d->imageInfoList.first(); + slotLoadCurrent(); +} + +void ImageWindow::slotLast() +{ + if(!promptUserSave(d->urlCurrent)) + return; + + d->urlCurrent = d->urlList.last(); + d->imageInfoCurrent = d->imageInfoList.last(); + slotLoadCurrent(); +} + +void ImageWindow::slotContextMenu() +{ + if (m_contextMenu) + { + RatingPopupMenu *ratingMenu = 0; + TagsPopupMenu *assignTagsMenu = 0; + TagsPopupMenu *removeTagsMenu = 0; + int separatorID1 = -1; + int separatorID2 = -1; + + if (d->imageInfoCurrent) + { + // Bulk assignment/removal of tags -------------------------- + + TQ_LLONG id = d->imageInfoCurrent->id(); + TQValueList<TQ_LLONG> idList; + idList.append(id); + + assignTagsMenu = new TagsPopupMenu(idList, 1000, TagsPopupMenu::ASSIGN); + removeTagsMenu = new TagsPopupMenu(idList, 2000, TagsPopupMenu::REMOVE); + + separatorID1 = m_contextMenu->insertSeparator(); + + m_contextMenu->insertItem(i18n("Assign Tag"), assignTagsMenu); + int i = m_contextMenu->insertItem(i18n("Remove Tag"), removeTagsMenu); + + connect(assignTagsMenu, TQ_SIGNAL(signalTagActivated(int)), + this, TQ_SLOT(slotAssignTag(int))); + + connect(removeTagsMenu, TQ_SIGNAL(signalTagActivated(int)), + this, TQ_SLOT(slotRemoveTag(int))); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + if (!db->hasTags( idList )) + m_contextMenu->setItemEnabled(i, false); + + separatorID2 = m_contextMenu->insertSeparator(); + + // Assign Star Rating ------------------------------------------- + + ratingMenu = new RatingPopupMenu(); + + connect(ratingMenu, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotAssignRating(int))); + + m_contextMenu->insertItem(i18n("Assign Rating"), ratingMenu); + } + + m_contextMenu->exec(TQCursor::pos()); + + if (separatorID1 != -1) + m_contextMenu->removeItem(separatorID1); + if (separatorID2 != -1) + m_contextMenu->removeItem(separatorID2); + + delete assignTagsMenu; + delete removeTagsMenu; + delete ratingMenu; + } +} + +void ImageWindow::slotChanged() +{ + TQString mpixels; + TQSize dims(m_canvas->imageWidth(), m_canvas->imageHeight()); + mpixels.setNum(dims.width()*dims.height()/1000000.0, 'f', 2); + TQString str = (!dims.isValid()) ? i18n("Unknown") : i18n("%1x%2 (%3Mpx)") + .arg(dims.width()).arg(dims.height()).arg(mpixels); + m_resLabel->setText(str); + + if (d->urlCurrent.isValid()) + { + KURL u(d->urlCurrent.directory()); + + DImg* img = m_canvas->interface()->getImg(); + + if (d->imageInfoCurrent) + { + d->rightSidebar->itemChanged(d->imageInfoCurrent, + m_canvas->getSelectedArea(), img); + } + else + { + d->rightSidebar->itemChanged(d->urlCurrent, m_canvas->getSelectedArea(), img); + } + } +} + +void ImageWindow::slotUndoStateChanged(bool moreUndo, bool moreRedo, bool canSave) +{ + m_revertAction->setEnabled(canSave); + m_undoAction->setEnabled(moreUndo); + m_redoAction->setEnabled(moreRedo); + + if (d->allowSaving) + m_saveAction->setEnabled(canSave); + + if (!moreUndo) + m_rotatedOrFlipped = false; +} + +void ImageWindow::slotAssignTag(int tagID) +{ + if (d->imageInfoCurrent) + { + MetadataHub hub; + hub.load(d->imageInfoCurrent); + hub.setTag(tagID, true); + hub.write(d->imageInfoCurrent, MetadataHub::PartialWrite); + hub.write(d->imageInfoCurrent->filePath(), MetadataHub::FullWriteIfChanged); + } +} + +void ImageWindow::slotRemoveTag(int tagID) +{ + if (d->imageInfoCurrent) + { + MetadataHub hub; + hub.load(d->imageInfoCurrent); + hub.setTag(tagID, false); + hub.write(d->imageInfoCurrent, MetadataHub::PartialWrite); + hub.write(d->imageInfoCurrent->filePath(), MetadataHub::FullWriteIfChanged); + } +} + +void ImageWindow::slotAssignRatingNoStar() +{ + slotAssignRating(0); +} + +void ImageWindow::slotAssignRatingOneStar() +{ + slotAssignRating(1); +} + +void ImageWindow::slotAssignRatingTwoStar() +{ + slotAssignRating(2); +} + +void ImageWindow::slotAssignRatingThreeStar() +{ + slotAssignRating(3); +} + +void ImageWindow::slotAssignRatingFourStar() +{ + slotAssignRating(4); +} + +void ImageWindow::slotAssignRatingFiveStar() +{ + slotAssignRating(5); +} + +void ImageWindow::slotAssignRating(int rating) +{ + rating = TQMIN(RatingMax, TQMAX(RatingMin, rating)); + if (d->imageInfoCurrent) + { + MetadataHub hub; + hub.load(d->imageInfoCurrent); + hub.setRating(rating); + hub.write(d->imageInfoCurrent, MetadataHub::PartialWrite); + hub.write(d->imageInfoCurrent->filePath(), MetadataHub::FullWriteIfChanged); + } +} + +void ImageWindow::slotUpdateItemInfo() +{ + uint index = d->urlList.findIndex(d->urlCurrent); + + m_rotatedOrFlipped = false; + + TQString text = d->urlCurrent.filename() + i18n(" (%2 of %3)") + .arg(TQString::number(index+1)) + .arg(TQString::number(d->urlList.count())); + m_nameLabel->setText(text); + + if (d->urlList.count() == 1) + { + m_backwardAction->setEnabled(false); + m_forwardAction->setEnabled(false); + m_firstAction->setEnabled(false); + m_lastAction->setEnabled(false); + } + else + { + m_backwardAction->setEnabled(true); + m_forwardAction->setEnabled(true); + m_firstAction->setEnabled(true); + m_lastAction->setEnabled(true); + } + + if (index == 0) + { + m_backwardAction->setEnabled(false); + m_firstAction->setEnabled(false); + } + + if (index == d->urlList.count()-1) + { + m_forwardAction->setEnabled(false); + m_lastAction->setEnabled(false); + } + + // Disable some menu actions if the current root image URL + // is not include in the digiKam Albums library database. + // This is necessary when ImageEditor is opened from cameraclient. + + KURL u(d->urlCurrent.directory()); + PAlbum *palbum = AlbumManager::instance()->findPAlbum(u); + + if (!palbum) + { + m_fileDeleteAction->setEnabled(false); + } + else + { + m_fileDeleteAction->setEnabled(true); + } +} + +bool ImageWindow::setup(bool iccSetupPage) +{ + Setup setup(this, 0, iccSetupPage ? Setup::IccProfiles : Setup::LastPageUsed); + + if (setup.exec() != TQDialog::Accepted) + return false; + + kapp->config()->sync(); + + applySettings(); + return true; +} + +void ImageWindow::toggleGUI2FullScreen() +{ + if (m_fullScreen) + d->rightSidebar->restore(); + else + d->rightSidebar->backup(); +} + +void ImageWindow::saveIsComplete() +{ + // With save(), we do not reload the image but just continue using the data. + // This means that a saving operation does not lead to quality loss for + // subsequent editing operations. + + // put image in cache, the LoadingCacheInterface cares for the details + LoadingCacheInterface::putImage(m_savingContext->destinationURL.path(), m_canvas->currentImage()); + + // notify main app that file changed + emit signalFileModified(m_savingContext->destinationURL); + + // all that is done in slotLoadCurrent, except for loading + KURL::List::iterator it = d->urlList.find(d->urlCurrent); + setViewToURL(*it); + + if (++it != d->urlList.end()) + { + m_canvas->preload((*it).path()); + } + //slotLoadCurrent(); +} + +void ImageWindow::saveAsIsComplete() +{ + // Nothing to be done if operating without database + if (!d->imageInfoCurrent) + return; + + // Find the src and dest albums ------------------------------------------ + + KURL srcDirURL(TQDir::cleanDirPath(m_savingContext->srcURL.directory())); + PAlbum* srcAlbum = AlbumManager::instance()->findPAlbum(srcDirURL); + + KURL dstDirURL(TQDir::cleanDirPath(m_savingContext->destinationURL.directory())); + PAlbum* dstAlbum = AlbumManager::instance()->findPAlbum(dstDirURL); + + if (dstAlbum && srcAlbum) + { + // Now copy the metadata of the original file to the new file ------------ + + ImageInfo newInfo(d->imageInfoCurrent->copyItem(dstAlbum, m_savingContext->destinationURL.fileName())); + + if ( d->urlList.find(m_savingContext->destinationURL) == d->urlList.end() ) + { // The image file did not exist in the list. + KURL::List::iterator it = d->urlList.find(m_savingContext->srcURL); + int index = d->urlList.findIndex(m_savingContext->srcURL); + d->urlList.insert(it, m_savingContext->destinationURL); + d->imageInfoCurrent = new ImageInfo(newInfo); + d->imageInfoList.insert(index, d->imageInfoCurrent); + } + else if (d->urlCurrent != m_savingContext->destinationURL) + { + for (ImageInfo *info = d->imageInfoList.first(); info; info = d->imageInfoList.next()) + { + if (info->kurl() == m_savingContext->destinationURL) + { + d->imageInfoCurrent = new ImageInfo(newInfo); + // setAutoDelete is true + d->imageInfoList.replace(d->imageInfoList.at(), d->imageInfoCurrent); + break; + } + } + } + + d->urlCurrent = m_savingContext->destinationURL; + m_canvas->switchToLastSaved(m_savingContext->destinationURL.path()); + + slotUpdateItemInfo(); + + // If the DImg is put in the cache under the new name, this means the new file will not be reloaded. + // This may irritate users who want to check for quality loss in lossy formats. + // In any case, only do that if the format did not change - too many assumptions otherwise (see bug #138949). + if (m_savingContext->originalFormat == m_savingContext->format) + LoadingCacheInterface::putImage(m_savingContext->destinationURL.path(), m_canvas->currentImage()); + + // notify main app that file changed or a file is added + if(m_savingContext->destinationExisted) + emit signalFileModified(m_savingContext->destinationURL); + else + emit signalFileAdded(m_savingContext->destinationURL); + + // all that is done in slotLoadCurrent, except for loading + KURL::List::iterator it = d->urlList.find(d->urlCurrent); + + if (it != d->urlList.end()) + { + setViewToURL(*it); + m_canvas->preload((*++it).path()); + } + } + else + { + //TODO: make the user aware that the new path has not been used as new current filename + // because it is outside the digikam album hierachy + } +} + +bool ImageWindow::save() +{ + // Sanity check. Just to be homogenous with SaveAs. + if (d->imageInfoCurrent) + { + // Write metadata from database to DImg + MetadataHub hub; + hub.load(d->imageInfoCurrent); + DImg image(m_canvas->currentImage()); + hub.write(image, MetadataHub::FullWrite); + } + + startingSave(d->urlCurrent); + return true; +} + +bool ImageWindow::saveAs() +{ + // If image editor is started from CameraGUI, there is no ImageInfo instance to use. + if (d->imageInfoCurrent) + { + // Write metadata from database to DImg + MetadataHub hub; + hub.load(d->imageInfoCurrent); + DImg image(m_canvas->currentImage()); + hub.write(image, MetadataHub::FullWrite); + } + + return ( startingSaveAs(d->urlCurrent) ); +} + +void ImageWindow::slotDeleteCurrentItem() +{ + deleteCurrentItem(true, false); +} + +void ImageWindow::slotDeleteCurrentItemPermanently() +{ + deleteCurrentItem(true, true); +} + +void ImageWindow::slotDeleteCurrentItemPermanentlyDirectly() +{ + deleteCurrentItem(false, true); +} + +void ImageWindow::slotTrashCurrentItemDirectly() +{ + deleteCurrentItem(false, false); +} + +void ImageWindow::deleteCurrentItem(bool ask, bool permanently) +{ + // This function implements all four of the above slots. + // The meaning of permanently differs depending on the value of ask + + KURL u; + u.setPath(d->urlCurrent.directory()); + PAlbum *palbum = AlbumManager::instance()->findPAlbum(u); + + // if available, provide a digikamalbums:// URL to TDEIO + KURL kioURL; + if (d->imageInfoCurrent) + kioURL = d->imageInfoCurrent->kurlForKIO(); + else + kioURL = d->urlCurrent; + KURL fileURL = d->urlCurrent; + + if (!palbum) + return; + + bool useTrash; + + if (ask) + { + bool preselectDeletePermanently = permanently; + + DeleteDialog dialog(this); + + KURL::List urlList; + urlList.append(d->urlCurrent); + if (!dialog.confirmDeleteList(urlList, + DeleteDialogMode::Files, + preselectDeletePermanently ? + DeleteDialogMode::NoChoiceDeletePermanently : DeleteDialogMode::NoChoiceTrash)) + return; + + useTrash = !dialog.shouldDelete(); + } + else + { + useTrash = !permanently; + } + + // bring all (sidebar) to a defined state without letting them sit on the deleted file + emit signalNoCurrentItem(); + + // trash does not like non-local URLs, put is not implemented + if (useTrash) + kioURL = fileURL; + + if (!SyncJob::del(kioURL, useTrash)) + { + TQString errMsg(SyncJob::lastErrorMsg()); + KMessageBox::error(this, errMsg, errMsg); + return; + } + + emit signalFileDeleted(d->urlCurrent); + + KURL CurrentToRemove = d->urlCurrent; + KURL::List::iterator it = d->urlList.find(d->urlCurrent); + int index = d->imageInfoList.find(d->imageInfoCurrent); + + if (it != d->urlList.end()) + { + if (d->urlCurrent != d->urlList.last()) + { + // Try to get the next image in the current Album... + + KURL urlNext = *(++it); + d->urlCurrent = urlNext; + d->imageInfoCurrent = d->imageInfoList.at(index + 1); + d->urlList.remove(CurrentToRemove); + d->imageInfoList.remove(index); + slotLoadCurrent(); + return; + } + else if (d->urlCurrent != d->urlList.first()) + { + // Try to get the previous image in the current Album. + + KURL urlPrev = *(--it); + d->urlCurrent = urlPrev; + d->imageInfoCurrent = d->imageInfoList.at(index - 1); + d->urlList.remove(CurrentToRemove); + d->imageInfoList.remove(index); + slotLoadCurrent(); + return; + } + } + + // No image in the current Album -> Quit ImageEditor... + + KMessageBox::information(this, + i18n("There is no image to show in the current album.\n" + "The image editor will be closed."), + i18n("No Image in Current Album")); + + close(); +} + +void ImageWindow::slotFileMetadataChanged(const KURL &url) +{ + if (url == d->urlCurrent) + { + m_canvas->readMetadataFromFile(url.path()); + } +} + +void ImageWindow::slotFilePrint() +{ + printImage(d->urlCurrent); +}; + +void ImageWindow::slideShow(bool startWithCurrent, SlideShowSettings& settings) +{ + float cnt; + DMetadata meta; + int i = 0; + m_cancelSlideShow = false; + settings.exifRotate = AlbumSettings::instance()->getExifRotate(); + + if (!d->imageInfoList.isEmpty()) + { + // We have started image editor from Album GUI. we get picture comments from database. + + m_nameLabel->progressBarMode(StatusProgressBar::CancelProgressBarMode, + i18n("Preparing slideshow. Please wait...")); + + cnt = (float)d->imageInfoList.count(); + + for (ImageInfo *info = d->imageInfoList.first() ; + !m_cancelSlideShow && info ; info = d->imageInfoList.next()) + { + SlidePictureInfo pictInfo; + pictInfo.comment = info->caption(); + + // Perform optimizations: only read pictures metadata if necessary. + if (settings.printApertureFocal || settings.printExpoSensitivity || settings.printMakeModel) + { + meta.load(info->kurl().path()); + pictInfo.photoInfo = meta.getPhotographInformations(); + } + + // In case of dateTime extraction from metadata failed + pictInfo.photoInfo.dateTime = info->dateTime(); + settings.pictInfoMap.insert(info->kurl(), pictInfo); + + m_nameLabel->setProgressValue((int)((i++/cnt)*100.0)); + kapp->processEvents(); + } + } + else + { + // We have started image editor from Camera GUI. we get picture comments from metadata. + + m_nameLabel->progressBarMode(StatusProgressBar::CancelProgressBarMode, + i18n("Preparing slideshow. Please wait...")); + + cnt = (float)d->urlList.count(); + + for (KURL::List::Iterator it = d->urlList.begin() ; + !m_cancelSlideShow && (it != d->urlList.end()) ; ++it) + { + SlidePictureInfo pictInfo; + meta.load((*it).path()); + pictInfo.comment = meta.getImageComment(); + pictInfo.photoInfo = meta.getPhotographInformations(); + settings.pictInfoMap.insert(*it, pictInfo); + + m_nameLabel->setProgressValue((int)((i++/cnt)*100.0)); + kapp->processEvents(); + } + } + + m_nameLabel->progressBarMode(StatusProgressBar::TextMode, TQString()); + + if (!m_cancelSlideShow) + { + settings.exifRotate = AlbumSettings::instance()->getExifRotate(); + settings.fileList = d->urlList; + + SlideShow *slide = new SlideShow(settings); + if (startWithCurrent) + slide->setCurrent(d->urlCurrent); + + slide->show(); + } +} + +void ImageWindow::dragMoveEvent(TQDragMoveEvent *e) +{ + int albumID; + TQValueList<int> albumIDs; + TQValueList<int> imageIDs; + KURL::List urls; + KURL::List kioURLs; + + if (ItemDrag::decode(e, urls, kioURLs, albumIDs, imageIDs) || + AlbumDrag::decode(e, urls, albumID) || + TagDrag::canDecode(e)) + { + e->accept(); + return; + } + + e->ignore(); +} + +void ImageWindow::dropEvent(TQDropEvent *e) +{ + int albumID; + TQValueList<int> albumIDs; + TQValueList<int> imageIDs; + KURL::List urls; + KURL::List kioURLs; + + if (ItemDrag::decode(e, urls, kioURLs, albumIDs, imageIDs)) + { + ImageInfoList imageInfoList; + + for (TQValueList<int>::const_iterator it = imageIDs.begin(); + it != imageIDs.end(); ++it) + { + ImageInfo *info = new ImageInfo(*it); + imageInfoList.append(info); + } + + if (imageInfoList.isEmpty()) + { + e->ignore(); + return; + } + + TQString ATitle; + AlbumManager* man = AlbumManager::instance(); + PAlbum* palbum = man->findPAlbum(albumIDs.first()); + if (palbum) ATitle = palbum->title(); + + TAlbum* talbum = man->findTAlbum(albumIDs.first()); + if (talbum) ATitle = talbum->title(); + + loadImageInfos(imageInfoList, imageInfoList.first(), + i18n("Album \"%1\"").arg(ATitle), true); + e->accept(); + } + else if (AlbumDrag::decode(e, urls, albumID)) + { + AlbumManager* man = AlbumManager::instance(); + TQValueList<TQ_LLONG> itemIDs = man->albumDB()->getItemIDsInAlbum(albumID); + ImageInfoList imageInfoList; + + for (TQValueList<TQ_LLONG>::const_iterator it = itemIDs.begin(); + it != itemIDs.end(); ++it) + { + ImageInfo *info = new ImageInfo(*it); + imageInfoList.append(info); + } + + if (imageInfoList.isEmpty()) + { + e->ignore(); + return; + } + + TQString ATitle; + PAlbum* palbum = man->findPAlbum(albumIDs.first()); + if (palbum) ATitle = palbum->title(); + + loadImageInfos(imageInfoList, imageInfoList.first(), + i18n("Album \"%1\"").arg(ATitle), true); + e->accept(); + } + else if(TagDrag::canDecode(e)) + { + TQByteArray ba = e->encodedData("digikam/tag-id"); + TQDataStream ds(ba, IO_ReadOnly); + int tagID; + ds >> tagID; + + AlbumManager* man = AlbumManager::instance(); + TQValueList<TQ_LLONG> itemIDs = man->albumDB()->getItemIDsInTag(tagID, true); + ImageInfoList imageInfoList; + + for (TQValueList<TQ_LLONG>::const_iterator it = itemIDs.begin(); + it != itemIDs.end(); ++it) + { + ImageInfo *info = new ImageInfo(*it); + imageInfoList.append(info); + } + + if (imageInfoList.isEmpty()) + { + e->ignore(); + return; + } + + TQString ATitle; + TAlbum* talbum = man->findTAlbum(tagID); + if (talbum) ATitle = talbum->title(); + + loadImageInfos(imageInfoList, imageInfoList.first(), + i18n("Album \"%1\"").arg(ATitle), true); + e->accept(); + } + else + { + e->ignore(); + } +} + +void ImageWindow::slotRevert() +{ + if(!promptUserSave(d->urlCurrent)) + return; + + m_canvas->slotRestore(); +} + +void ImageWindow::slotChangeTheme(const TQString& theme) +{ + AlbumSettings::instance()->setCurrentTheme(theme); + ThemeEngine::instance()->slotChangeTheme(theme); +} + +} // namespace Digikam diff --git a/src/utilities/imageeditor/editor/imagewindow.h b/src/utilities/imageeditor/editor/imagewindow.h new file mode 100644 index 00000000..389518a8 --- /dev/null +++ b/src/utilities/imageeditor/editor/imagewindow.h @@ -0,0 +1,155 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-02-12 + * Description : digiKam image editor GUI + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef IMAGEWINDOW_H +#define IMAGEWINDOW_H + +// TQt includes. + +#include <tqstring.h> + +// KDE includes. + +#include <kurl.h> + +// Local includes. + +#include "editorwindow.h" +#include "imageinfo.h" + +class TQDragMoveEvent; +class TQDropEvent; + +namespace Digikam +{ + +class AlbumIconView; +class ImageWindowPriv; +class SlideShowSettings; + +class ImageWindow : public EditorWindow +{ + TQ_OBJECT + + +public: + + ~ImageWindow(); + + void loadURL(const KURL::List& urlList, const KURL& urlCurrent, + const TQString& caption=TQString(), + bool allowSaving=true); + + void loadImageInfos(const ImageInfoList &imageInfoList, + ImageInfo *imageInfoCurrent, + const TQString& caption, bool allowSaving); + + static ImageWindow* imagewindow(); + static bool imagewindowCreated(); + + void applySettings(); + void refreshView(); + bool setup(bool iccSetupPage=false); + + bool queryClose(); + +signals: + + void signalFileDeleted(const KURL& url); + void signalFileAdded(const KURL& url); + void signalFileModified(const KURL& url); + void signalURLChanged(const KURL& url); + +private: + + void loadCurrentList(const TQString& caption, bool allowSaving); + void closeEvent(TQCloseEvent* e); + + void dragMoveEvent(TQDragMoveEvent *e); + void dropEvent(TQDropEvent *e); + + void setupActions(); + void setupConnections(); + void setupUserArea(); + void toggleGUI2FullScreen(); + + bool save(); + bool saveAs(); + + void saveIsComplete(); + void saveAsIsComplete(); + void setViewToURL(const KURL &url); + void deleteCurrentItem(bool ask, bool permanently); + + void slideShow(bool startWithCurrent, SlideShowSettings& settings); + + Sidebar* rightSideBar() const; + + ImageWindow(); + +private slots: + + void slotForward(); + void slotBackward(); + void slotFirst(); + void slotLast(); + void slotFilePrint(); + + void slotLoadCurrent(); + void slotDeleteCurrentItem(); + void slotDeleteCurrentItemPermanently(); + void slotDeleteCurrentItemPermanentlyDirectly(); + void slotTrashCurrentItemDirectly(); + + void slotChanged(); + void slotUndoStateChanged(bool, bool, bool); + void slotUpdateItemInfo(); + + void slotContextMenu(); + void slotRevert(); + + void slotAssignTag(int tagID); + void slotRemoveTag(int tagID); + + void slotAssignRatingNoStar(); + void slotAssignRatingOneStar(); + void slotAssignRatingTwoStar(); + void slotAssignRatingThreeStar(); + void slotAssignRatingFourStar(); + void slotAssignRatingFiveStar(); + void slotAssignRating(int rating); + + void slotFileMetadataChanged(const KURL &); + void slotChangeTheme(const TQString& theme); + +private: + + ImageWindowPriv *d; + + static ImageWindow *m_instance; +}; + +} // namespace Digikam + +#endif /* IMAGEWINDOW_H */ diff --git a/src/utilities/imageeditor/editor/savingcontextcontainer.h b/src/utilities/imageeditor/editor/savingcontextcontainer.h new file mode 100644 index 00000000..c747a357 --- /dev/null +++ b/src/utilities/imageeditor/editor/savingcontextcontainer.h @@ -0,0 +1,89 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-01-20 + * Description : image editor GUI saving context container + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * Copyright (C) 2006-2007 by Marcel Wiesweg <marcel.wiesweg@gmx.de> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SAVINGCONTEXTCONTAINER_H +#define SAVINGCONTEXTCONTAINER_H + +// TQt includes. + +#include <tqstring.h> + +// KDE includes. + +#include <kurl.h> +#include <tdetempfile.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT SavingContextContainer +{ + +public: + + SavingContextContainer() + { + savingState = SavingStateNone; + synchronizingState = NormalSaving; + saveTempFile = 0; + destinationExisted = false; + synchronousSavingResult = false; + abortingSaving = false; + } + + enum SavingState + { + SavingStateNone, + SavingStateSave, + SavingStateSaveAs + }; + + enum SynchronizingState + { + NormalSaving, + SynchronousSaving + }; + + SavingState savingState; + SynchronizingState synchronizingState; + bool synchronousSavingResult; + bool destinationExisted; + bool abortingSaving; + + TQString originalFormat; + TQString format; + + KURL srcURL; + KURL destinationURL; + + KTempFile *saveTempFile; +}; + +} // namespace Digikam + +#endif /* SAVINGCONTEXTCONTAINER_H */ diff --git a/src/utilities/imageeditor/rawimport/Makefile.am b/src/utilities/imageeditor/rawimport/Makefile.am new file mode 100644 index 00000000..19071935 --- /dev/null +++ b/src/utilities/imageeditor/rawimport/Makefile.am @@ -0,0 +1,27 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = librawimport.la + +librawimport_la_SOURCES = rawpreview.cpp rawsettingsbox.cpp rawimport.cpp \ + rawpostprocessing.cpp + +librawimport_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TDEPRINT) + +INCLUDES= -I$(top_srcdir)/src/digikam \ + -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/threadimageio \ + -I$(top_srcdir)/src/libs/themeengine \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + diff --git a/src/utilities/imageeditor/rawimport/rawimport.cpp b/src/utilities/imageeditor/rawimport/rawimport.cpp new file mode 100644 index 00000000..a9254ce8 --- /dev/null +++ b/src/utilities/imageeditor/rawimport/rawimport.cpp @@ -0,0 +1,223 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-20 + * Description : Raw import tool + * + * Copyright (C) 2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqstring.h> +#include <tqlayout.h> +#include <tqtooltip.h> +#include <tqwhatsthis.h> + +// KDE includes. + +#include <kcursor.h> +#include <tdelocale.h> +#include <tdeconfig.h> +#include <tdeapplication.h> +#include <kiconloader.h> +#include <kstandarddirs.h> + +// Local includes. + +#include "ddebug.h" +#include "drawdecoding.h" +#include "histogramwidget.h" +#include "curveswidget.h" +#include "imagehistogram.h" +#include "rawsettingsbox.h" +#include "rawpostprocessing.h" +#include "editortooliface.h" +#include "rawpreview.h" +#include "rawimport.h" +#include "rawimport.moc" + +namespace Digikam +{ + +class RawImportPriv +{ +public: + + RawImportPriv() + { + previewWidget = 0; + settingsBox = 0; + } + + RawSettingsBox *settingsBox; + + RawPreview *previewWidget; +}; + +RawImport::RawImport(const KURL& url, TQObject *parent) + : EditorToolThreaded(parent) +{ + d = new RawImportPriv; + d->previewWidget = new RawPreview(url, 0); + d->settingsBox = new RawSettingsBox(url, 0); + + setToolName(i18n("Raw Import")); + setToolIcon(SmallIcon("kdcraw")); + setProgressMessage(i18n("Post Processing")); + setToolView(d->previewWidget); + setToolSettings(d->settingsBox); + + init(); +} + +RawImport::~RawImport() +{ + delete d; +} + +void RawImport::slotInit() +{ + EditorToolThreaded::slotInit(); + + // --------------------------------------------------------------- + + connect(d->previewWidget, TQ_SIGNAL(signalLoadingStarted()), + this, TQ_SLOT(slotLoadingStarted())); + + connect(d->previewWidget, TQ_SIGNAL(signalDemosaicedImage()), + this, TQ_SLOT(slotDemosaicedImage())); + + connect(d->previewWidget, TQ_SIGNAL(signalLoadingStarted()), + this, TQ_SLOT(slotLoadingStarted())); + + connect(d->previewWidget, TQ_SIGNAL(signalLoadingProgress(float)), + this, TQ_SLOT(slotLoadingProgress(float))); + + connect(d->previewWidget, TQ_SIGNAL(signalLoadingFailed()), + this, TQ_SLOT(slotLoadingFailed())); + + connect(d->settingsBox, TQ_SIGNAL(signalDemosaicingChanged()), + this, TQ_SLOT(slotDemosaicingChanged())); + + connect(d->settingsBox, TQ_SIGNAL(signalPostProcessingChanged()), + this, TQ_SLOT(slotTimer())); + + connect(d->settingsBox, TQ_SIGNAL(signalUpdatePreview()), + this, TQ_SLOT(slotUpdatePreview())); + + connect(d->settingsBox, TQ_SIGNAL(signalAbortPreview()), + this, TQ_SLOT(slotAbort())); + + // --------------------------------------------------------------- + + setBusy(true); + slotUpdatePreview(); +} + +void RawImport::setBusy(bool val) +{ + if (val) d->previewWidget->setCursor(KCursor::waitCursor()); + else d->previewWidget->unsetCursor(); + d->settingsBox->setBusy(val); +} + +DRawDecoding RawImport::rawDecodingSettings() +{ + return d->settingsBox->settings(); +} + +void RawImport::slotUpdatePreview() +{ + DRawDecoding settings = rawDecodingSettings(); + // We will load an half size image to speed up preview computing. + settings.halfSizeColorImage = true; + + d->previewWidget->setDecodingSettings(settings); +} + +void RawImport::slotAbort() +{ + // If preview loading, don't play with threaded filter interface. + if (renderingMode() == EditorToolThreaded::NoneRendering) + { + d->previewWidget->cancelLoading(); + d->settingsBox->histogram()->stopHistogramComputation(); + EditorToolIface::editorToolIface()->setToolStopProgress(); + setBusy(false); + return; + } + + EditorToolThreaded::slotAbort(); +} + +void RawImport::slotLoadingStarted() +{ + d->settingsBox->enableUpdateBtn(false); + d->settingsBox->histogram()->setDataLoading(); + d->settingsBox->curve()->setDataLoading(); + EditorToolIface::editorToolIface()->setToolStartProgress(i18n("Raw Decoding")); + setBusy(true); +} + +void RawImport::slotDemosaicedImage() +{ + d->settingsBox->setDemosaicedImage(d->previewWidget->demosaicedImage()); + slotEffect(); +} + +void RawImport::prepareEffect() +{ + DImg postImg = d->previewWidget->demosaicedImage(); + setFilter(dynamic_cast<DImgThreadedFilter*>(new RawPostProcessing(&postImg, this, rawDecodingSettings()))); +} + +void RawImport::putPreviewData() +{ + d->previewWidget->setPostProcessedImage(filter()->getTargetImage()); + d->settingsBox->setPostProcessedImage(d->previewWidget->postProcessedImage()); + EditorToolIface::editorToolIface()->setToolStopProgress(); + setBusy(false); +} + +void RawImport::slotLoadingFailed() +{ + d->settingsBox->histogram()->setLoadingFailed(); + EditorToolIface::editorToolIface()->setToolStopProgress(); + setBusy(false); +} + +void RawImport::slotDemosaicingChanged() +{ + d->settingsBox->enableUpdateBtn(true); +} + +void RawImport::slotLoadingProgress(float v) +{ + EditorToolIface::editorToolIface()->setToolProgress((int)(v*100)); +} + +void RawImport::slotOk() +{ + EditorTool::slotOk(); +} + +void RawImport::slotCancel() +{ + EditorTool::slotCancel(); +} + +} // NameSpace Digikam diff --git a/src/utilities/imageeditor/rawimport/rawimport.h b/src/utilities/imageeditor/rawimport/rawimport.h new file mode 100644 index 00000000..85255bc8 --- /dev/null +++ b/src/utilities/imageeditor/rawimport/rawimport.h @@ -0,0 +1,88 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-20 + * Description : Raw import tool + * + * Copyright (C) 2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef RAWIMPORTDLG_H +#define RAWIMPORTDLG_H + +// KDE includes. + +#include <kurl.h> + +// Local includes. + +#include "editortool.h" +#include "dimg.h" +#include "digikam_export.h" + +namespace KDcrawIface +{ +class RawDecodingSettings; +} + +namespace Digikam +{ + +class RawImportPriv; + +class DIGIKAM_EXPORT RawImport : public EditorToolThreaded +{ + TQ_OBJECT + + +public: + + RawImport(const KURL& url, TQObject *parent); + ~RawImport(); + + DRawDecoding rawDecodingSettings(); + +private: + + void setBusy(bool busy); + void prepareEffect(); + void putPreviewData(); + +private slots: + + void slotInit(); + + void slotLoadingStarted(); + void slotDemosaicedImage(); + void slotLoadingFailed(); + void slotLoadingProgress(float); + + void slotUpdatePreview(); + void slotAbort(); + + void slotDemosaicingChanged(); + + void slotOk(); + void slotCancel(); + +private: + + RawImportPriv *d; +}; + +} // NameSpace Digikam + +#endif // RAWIMPORTDLG_H diff --git a/src/utilities/imageeditor/rawimport/rawpostprocessing.cpp b/src/utilities/imageeditor/rawimport/rawpostprocessing.cpp new file mode 100644 index 00000000..d45dd5e4 --- /dev/null +++ b/src/utilities/imageeditor/rawimport/rawpostprocessing.cpp @@ -0,0 +1,137 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-13-08 + * Description : Raw post processing corrections. + * + * Copyright (C) 2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// Local includes. + +#include "ddebug.h" +#include "imagehistogram.h" +#include "imagecurves.h" +#include "imagelevels.h" +#include "bcgmodifier.h" +#include "whitebalance.h" +#include "dimgimagefilters.h" +#include "rawpostprocessing.h" + +namespace Digikam +{ + +RawPostProcessing::RawPostProcessing(DImg *orgImage, TQObject *parent, const DRawDecoding& settings) + : DImgThreadedFilter(orgImage, parent, "RawPostProcessing") +{ + m_customRawSettings = settings; + initFilter(); +} + +RawPostProcessing::RawPostProcessing(DImgThreadedFilter *parentFilter, + const DImg &orgImage, const DImg &destImage, + int progressBegin, int progressEnd, const DRawDecoding& settings) + : DImgThreadedFilter(parentFilter, orgImage, destImage, progressBegin, progressEnd, + parentFilter->filterName() + ": RawPostProcessing") +{ + m_customRawSettings = settings; + filterImage(); +} + +void RawPostProcessing::filterImage() +{ + rawPostProcessing(); +} + +void RawPostProcessing::rawPostProcessing() +{ + if (!m_orgImage.bits() || !m_orgImage.width() || !m_orgImage.height()) + { + DWarning() << ("RawPostProcessing::rawPostProcessing: no image m_orgImage.bits() available!") + << endl; + return; + } + + if (!m_customRawSettings.postProcessingSettingsIsDirty()) + { + m_destImage = m_orgImage; + return; + } + + postProgress(15); + + if (m_customRawSettings.exposureComp != 0.0 || m_customRawSettings.saturation != 1.0) + { + WhiteBalance wb(m_orgImage.sixteenBit()); + wb.whiteBalance(m_orgImage.bits(), m_orgImage.width(), m_orgImage.height(), m_orgImage.sixteenBit(), + 0.0, // black + m_customRawSettings.exposureComp, // exposure + 6500.0, // temperature (neutral) + 1.0, // green + 0.5, // dark + 1.0, // gamma + m_customRawSettings.saturation); // saturation + } + postProgress(30); + + if (m_customRawSettings.lightness != 0.0 || m_customRawSettings.contrast != 1.0 || m_customRawSettings.gamma != 1.0) + { + BCGModifier bcg; + bcg.setBrightness(m_customRawSettings.lightness); + bcg.setContrast(m_customRawSettings.contrast); + bcg.setGamma(m_customRawSettings.gamma); + bcg.applyBCG(m_orgImage.bits(), m_orgImage.width(), m_orgImage.height(), m_orgImage.sixteenBit()); + } + postProgress(45); + + if (!m_customRawSettings.curveAdjust.isEmpty()) + { + DImg tmp(m_orgImage.width(), m_orgImage.height(), m_orgImage.sixteenBit()); + ImageCurves curves(m_orgImage.sixteenBit()); + curves.setCurvePoints(ImageHistogram::ValueChannel, m_customRawSettings.curveAdjust); + curves.curvesCalculateCurve(ImageHistogram::ValueChannel); + curves.curvesLutSetup(ImageHistogram::AlphaChannel); + curves.curvesLutProcess(m_orgImage.bits(), tmp.bits(), m_orgImage.width(), m_orgImage.height()); + memcpy(m_orgImage.bits(), tmp.bits(), tmp.numBytes()); + } + postProgress(60); + + if (!m_customRawSettings.levelsAdjust.isEmpty()) + { + DImg tmp(m_orgImage.width(), m_orgImage.height(), m_orgImage.sixteenBit()); + ImageLevels levels(m_orgImage.sixteenBit()); + int j=0; + for (int i = 0 ; i < 4; i++) + { + levels.setLevelLowInputValue(i, m_customRawSettings.levelsAdjust[j++]); + levels.setLevelHighInputValue(i, m_customRawSettings.levelsAdjust[j++]); + levels.setLevelLowOutputValue(i, m_customRawSettings.levelsAdjust[j++]); + levels.setLevelHighOutputValue(i, m_customRawSettings.levelsAdjust[j++]); + } + + levels.levelsLutSetup(ImageHistogram::AlphaChannel); + levels.levelsLutProcess(m_orgImage.bits(), tmp.bits(), m_orgImage.width(), m_orgImage.height()); + memcpy(m_orgImage.bits(), tmp.bits(), tmp.numBytes()); + } + postProgress(75); + + m_destImage = m_orgImage; + + postProgress(100); +} + +} // NameSpace Digikam diff --git a/src/utilities/imageeditor/rawimport/rawpostprocessing.h b/src/utilities/imageeditor/rawimport/rawpostprocessing.h new file mode 100644 index 00000000..3b3c7761 --- /dev/null +++ b/src/utilities/imageeditor/rawimport/rawpostprocessing.h @@ -0,0 +1,63 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-13-08 + * Description : Raw post processing corrections. + * + * Copyright (C) 2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef RAWPOSTPROCESSING_H +#define RAWPOSTPROCESSING_H + +// Digikam includes. + +#include "digikam_export.h" + +// Local includes. + +#include "dimgthreadedfilter.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT RawPostProcessing : public DImgThreadedFilter +{ + +public: + + RawPostProcessing(DImg *orgImage, TQObject *parent=0, const DRawDecoding& settings=DRawDecoding()); + + // Constructor for slave mode: execute immediately in current thread with specified master filter + RawPostProcessing(DImgThreadedFilter *parentFilter, const DImg &orgImage, const DImg &destImage, + int progressBegin=0, int progressEnd=100, const DRawDecoding& settings=DRawDecoding()); + + ~RawPostProcessing(){}; + +private: + + virtual void filterImage(); + void rawPostProcessing(); + +private: + + DRawDecoding m_customRawSettings; +}; + +} // NameSpace Digikam + +#endif /* RAWPOSTPROCESSING_H */ diff --git a/src/utilities/imageeditor/rawimport/rawpreview.cpp b/src/utilities/imageeditor/rawimport/rawpreview.cpp new file mode 100644 index 00000000..3207ba14 --- /dev/null +++ b/src/utilities/imageeditor/rawimport/rawpreview.cpp @@ -0,0 +1,336 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-04 + * Description : RAW postProcessedImg widget. + * + * Copyright (C) 2008 Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqstring.h> +#include <tqpainter.h> +#include <tqtoolbutton.h> +#include <tqtooltip.h> +#include <tqpixmap.h> +#include <tqfileinfo.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kcursor.h> +#include <kdatetbl.h> +#include <kiconloader.h> + +// Local includes. + +#include "ddebug.h" +#include "paniconwidget.h" +#include "managedloadsavethread.h" +#include "loadingdescription.h" +#include "themeengine.h" +#include "rawpreview.h" +#include "rawpreview.moc" + +namespace Digikam +{ + +class RawPreviewPriv +{ +public: + + RawPreviewPriv() + { + panIconPopup = 0; + panIconWidget = 0; + cornerButton = 0; + thread = 0; + url = 0; + currentFitWindowZoom = 0; + } + + double currentFitWindowZoom; + + TQToolButton *cornerButton; + + TDEPopupFrame *panIconPopup; + + KURL url; + + PanIconWidget *panIconWidget; + + DImg demosaicedImg; + + DImg postProcessedImg; + + DRawDecoding settings; + + ManagedLoadSaveThread *thread; + + LoadingDescription loadingDesc; +}; + +RawPreview::RawPreview(const KURL& url, TQWidget *parent) + : PreviewWidget(parent) +{ + d = new RawPreviewPriv; + d->thread = new ManagedLoadSaveThread; + d->url = url; + + setMinimumWidth(500); + setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding); + + d->cornerButton = new TQToolButton(this); + d->cornerButton->setIconSet(SmallIcon("move")); + d->cornerButton->hide(); + TQToolTip::add(d->cornerButton, i18n("Pan the image to a region")); + setCornerWidget(d->cornerButton); + + // ------------------------------------------------------------ + + connect(d->thread, TQ_SIGNAL(signalImageLoaded(const LoadingDescription&, const DImg&)), + this, TQ_SLOT(slotImageLoaded(const LoadingDescription&, const DImg&))); + + connect(d->thread, TQ_SIGNAL(signalLoadingProgress(const LoadingDescription&, float)), + this, TQ_SLOT(slotLoadingProgress(const LoadingDescription&, float))); + + connect(d->cornerButton, TQ_SIGNAL(pressed()), + this, TQ_SLOT(slotCornerButtonPressed())); + + connect(ThemeEngine::instance(), TQ_SIGNAL(signalThemeChanged()), + this, TQ_SLOT(slotThemeChanged())); + + // ------------------------------------------------------------ + + slotReset(); +} + +RawPreview::~RawPreview() +{ + delete d; +} + +void RawPreview::setPostProcessedImage(const DImg& image) +{ + d->postProcessedImg = image; + + updateZoomAndSize(false); + + viewport()->setUpdatesEnabled(true); + viewport()->update(); +} + +DImg& RawPreview::postProcessedImage() const +{ + return d->postProcessedImg; +} + +DImg& RawPreview::demosaicedImage() const +{ + return d->demosaicedImg; +} + +void RawPreview::setDecodingSettings(const DRawDecoding& settings) +{ + // Save post processing settings. + d->settings = settings; + + // All post processing settings will be used after demosaicing. + DRawDecoding demosaisedSettings = settings; + demosaisedSettings.resetPostProcessingSettings(); + + d->loadingDesc = LoadingDescription(d->url.path(), demosaisedSettings); + d->thread->load(d->loadingDesc, ManagedLoadSaveThread::LoadingPolicyFirstRemovePrevious); + emit signalLoadingStarted(); +} + +void RawPreview::cancelLoading() +{ + d->thread->stopLoading(d->loadingDesc); +} + +void RawPreview::slotLoadingProgress(const LoadingDescription& description, float progress) +{ + if (description.filePath != d->loadingDesc.filePath) + return; + + emit signalLoadingProgress(progress); +} + +void RawPreview::slotImageLoaded(const LoadingDescription& description, const DImg& image) +{ + if (description.filePath != d->loadingDesc.filePath) + return; + + if (image.isNull()) + { + TQPixmap pix(visibleWidth(), visibleHeight()); + pix.fill(ThemeEngine::instance()->baseColor()); + TQPainter p(&pix); + p.setPen(TQPen(ThemeEngine::instance()->textRegColor())); + p.drawText(0, 0, pix.width(), pix.height(), + TQt::AlignCenter|TQt::WordBreak, + i18n("Cannot decode RAW image for\n\"%1\"") + .arg(TQFileInfo(d->loadingDesc.filePath).fileName())); + p.end(); + // three copies - but the image is small + setPostProcessedImage(DImg(pix.convertToImage())); + emit signalLoadingFailed(); + } + else + { + d->demosaicedImg = image; + emit signalDemosaicedImage(); + // NOTE: we will apply all Raw post processing corrections into RawImport class. + } +} + +void RawPreview::slotThemeChanged() +{ + setBackgroundColor(ThemeEngine::instance()->baseColor()); +} + +void RawPreview::slotCornerButtonPressed() +{ + if (d->panIconPopup) + { + d->panIconPopup->hide(); + delete d->panIconPopup; + d->panIconPopup = 0; + } + + d->panIconPopup = new TDEPopupFrame(this); + PanIconWidget *pan = new PanIconWidget(d->panIconPopup); + pan->setImage(180, 120, postProcessedImage()); + d->panIconPopup->setMainWidget(pan); + + TQRect r((int)(contentsX() / zoomFactor()), (int)(contentsY() / zoomFactor()), + (int)(visibleWidth() / zoomFactor()), (int)(visibleHeight() / zoomFactor())); + pan->setRegionSelection(r); + pan->setMouseFocus(); + + connect(pan, TQ_SIGNAL(signalSelectionMoved(const TQRect&, bool)), + this, TQ_SLOT(slotPanIconSelectionMoved(const TQRect&, bool))); + + connect(pan, TQ_SIGNAL(signalHiden()), + this, TQ_SLOT(slotPanIconHiden())); + + TQPoint g = mapToGlobal(viewport()->pos()); + g.setX(g.x()+ viewport()->size().width()); + g.setY(g.y()+ viewport()->size().height()); + d->panIconPopup->popup(TQPoint(g.x() - d->panIconPopup->width(), + g.y() - d->panIconPopup->height())); + + pan->setCursorToLocalRegionSelectionCenter(); +} + +void RawPreview::slotPanIconHiden() +{ + d->cornerButton->blockSignals(true); + d->cornerButton->animateClick(); + d->cornerButton->blockSignals(false); +} + +void RawPreview::slotPanIconSelectionMoved(const TQRect& r, bool b) +{ + setContentsPos((int)(r.x()*zoomFactor()), (int)(r.y()*zoomFactor())); + + if (b) + { + d->panIconPopup->hide(); + delete d->panIconPopup; + d->panIconPopup = 0; + slotPanIconHiden(); + } +} + +void RawPreview::zoomFactorChanged(double zoom) +{ + updateScrollBars(); + + if (horizontalScrollBar()->isVisible() || verticalScrollBar()->isVisible()) + d->cornerButton->show(); + else + d->cornerButton->hide(); + + PreviewWidget::zoomFactorChanged(zoom); +} + +void RawPreview::resizeEvent(TQResizeEvent* e) +{ + if (!e) return; + + TQScrollView::resizeEvent(e); + + if (!d->loadingDesc.filePath.isEmpty()) + d->cornerButton->hide(); + + updateZoomAndSize(false); +} + +void RawPreview::updateZoomAndSize(bool alwaysFitToWindow) +{ + // Set zoom for fit-in-window as minimum, but dont scale up images + // that are smaller than the available space, only scale down. + double zoom = calcAutoZoomFactor(ZoomInOnly); + setZoomMin(zoom); + setZoomMax(zoom*12.0); + + // Is currently the zoom factor set to fit to window? Then set it again to fit the new size. + if (zoomFactor() < zoom || alwaysFitToWindow || zoomFactor() == d->currentFitWindowZoom) + { + setZoomFactor(zoom); + } + + // store which zoom factor means it is fit to window + d->currentFitWindowZoom = zoom; + + updateContentsSize(); +} + +int RawPreview::previewWidth() +{ + return d->postProcessedImg.width(); +} + +int RawPreview::previewHeight() +{ + return d->postProcessedImg.height(); +} + +bool RawPreview::previewIsNull() +{ + return d->postProcessedImg.isNull(); +} + +void RawPreview::resetPreview() +{ + d->postProcessedImg = DImg(); + d->loadingDesc = LoadingDescription(); + + updateZoomAndSize(false); +} + +void RawPreview::paintPreview(TQPixmap *pix, int sx, int sy, int sw, int sh) +{ + DImg img = d->postProcessedImg.smoothScaleSection(sx, sy, sw, sh, tileSize(), tileSize()); + TQPixmap pix2 = img.convertToPixmap(); + bitBlt(pix, 0, 0, &pix2, 0, 0); +} + +} // NameSpace Digikam diff --git a/src/utilities/imageeditor/rawimport/rawpreview.h b/src/utilities/imageeditor/rawimport/rawpreview.h new file mode 100644 index 00000000..6c7e5379 --- /dev/null +++ b/src/utilities/imageeditor/rawimport/rawpreview.h @@ -0,0 +1,108 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-04 + * Description : RAW preview widget. + * + * Copyright (C) 2008 Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef RAWPREVIEW_H +#define RAWPREVIEW_H + +// TQt includes. + +#include <tqimage.h> + +// KDE includes. + +#include <kurl.h> + +// Local includes. + +#include "dimg.h" +#include "previewwidget.h" +#include "digikam_export.h" + +class TQPixmap; + +namespace Digikam +{ + +class LoadingDescription; +class RawPreviewPriv; + +class DIGIKAM_EXPORT RawPreview : public PreviewWidget +{ + +TQ_OBJECT + + +public: + + RawPreview(const KURL& url, TQWidget *parent); + ~RawPreview(); + + DImg& demosaicedImage() const; + DImg& postProcessedImage() const; + + void setDecodingSettings(const DRawDecoding& settings); + void setPostProcessedImage(const DImg& image); + + void cancelLoading(); + +signals: + + void signalLoadingStarted(); + void signalLoadingProgress(float); + void signalLoadingFailed(); + void signalDemosaicedImage(); + void signalPostProcessedImage(); + +protected: + + void resizeEvent(TQResizeEvent* e); + +private slots: + + void slotLoadingProgress(const LoadingDescription& description, float progress); + void slotImageLoaded(const LoadingDescription& description, const DImg &image); + void slotThemeChanged(); + void slotCornerButtonPressed(); + void slotPanIconSelectionMoved(const TQRect&, bool); + void slotPanIconHiden(); + +private: + + void setdemosaicedImg(const DImg& image); + void postProcessing(const DRawDecoding& settings); + int previewWidth(); + int previewHeight(); + bool previewIsNull(); + void resetPreview(); + void zoomFactorChanged(double zoom); + void updateZoomAndSize(bool alwaysFitToWindow); + inline void paintPreview(TQPixmap *pix, int sx, int sy, int sw, int sh); + +private: + + RawPreviewPriv* d; +}; + +} // NameSpace Digikam + +#endif /* RAWPREVIEW_H */ diff --git a/src/utilities/imageeditor/rawimport/rawsettingsbox.cpp b/src/utilities/imageeditor/rawimport/rawsettingsbox.cpp new file mode 100644 index 00000000..bf0ee67e --- /dev/null +++ b/src/utilities/imageeditor/rawimport/rawsettingsbox.cpp @@ -0,0 +1,741 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-11 + * Description : Raw import settings box + * + * Copyright (C) 2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqstring.h> +#include <tqlayout.h> +#include <tqtooltip.h> +#include <tqwhatsthis.h> +#include <tqhbuttongroup.h> +#include <tqcombobox.h> +#include <tqlabel.h> +#include <tqvbox.h> +#include <tqtoolbutton.h> +#include <tqtoolbox.h> +#include <tqpushbutton.h> + +// KDE includes. + +#include <tdeapplication.h> +#include <ktabwidget.h> +#include <tdelocale.h> +#include <kiconloader.h> +#include <tdeconfig.h> +#include <kstandarddirs.h> +#include <tdefiledialog.h> + +// LibKDcraw includes. + +#include <libkdcraw/dcrawsettingswidget.h> +#include <libkdcraw/rnuminput.h> + +// Local includes. + +#include "ddebug.h" +#include "imagedialog.h" +#include "imagehistogram.h" +#include "imagecurves.h" +#include "iccpreviewwidget.h" +#include "histogramwidget.h" +#include "curveswidget.h" +#include "colorgradientwidget.h" +#include "rawsettingsbox.h" +#include "rawsettingsbox.moc" + +using namespace KDcrawIface; + +namespace Digikam +{ + +class RawSettingsBoxPriv +{ +public: + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel, + ColorChannels + }; + + enum AllColorsColorType + { + AllColorsRed=0, + AllColorsGreen, + AllColorsBlue + }; + +public: + + RawSettingsBoxPriv() + { + channelCB = 0; + colorsCB = 0; + scaleBG = 0; + hGradient = 0; + histogramWidget = 0; + infoBox = 0; + advExposureBox = 0; + gammaLabel = 0; + gammaInput = 0; + saturationLabel = 0; + saturationInput = 0; + fineExposureLabel = 0; + fineExposureInput = 0; + contrastInput = 0; + contrastLabel = 0; + curveBox = 0; + curveWidget = 0; + resetCurveBtn = 0; + decodingSettingsBox = 0; + postProcessSettingsBox = 0; + tabView = 0; + abortBtn = 0; + updateBtn = 0; + rawdecodingBox = 0; + brightnessLabel = 0; + brightnessInput = 0; + } + + TQWidget *advExposureBox; + TQWidget *curveBox; + TQWidget *rawdecodingBox; + + TQComboBox *channelCB; + TQComboBox *colorsCB; + + TQLabel *brightnessLabel; + TQLabel *contrastLabel; + TQLabel *gammaLabel; + TQLabel *saturationLabel; + TQLabel *fineExposureLabel; + + TQHButtonGroup *scaleBG; + + TQPushButton *abortBtn; + TQPushButton *updateBtn; + + TQToolButton *resetCurveBtn; + + TQToolBox *postProcessSettingsBox; + + KTabWidget *tabView; + + ColorGradientWidget *hGradient; + + CurvesWidget *curveWidget; + + HistogramWidget *histogramWidget; + + ImageDialogPreview *infoBox; + + RIntNumInput *contrastInput; + RIntNumInput *brightnessInput; + + RDoubleNumInput *gammaInput; + RDoubleNumInput *saturationInput; + RDoubleNumInput *fineExposureInput; + + DcrawSettingsWidget *decodingSettingsBox; +}; + +RawSettingsBox::RawSettingsBox(const KURL& url, TQWidget *parent) + : EditorToolSettings(Default|Ok|Cancel, NoTool, parent) +{ + d = new RawSettingsBoxPriv; + + // --------------------------------------------------------------- + + TQGridLayout* gridSettings = new TQGridLayout(plainPage(), 5, 4); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), plainPage()); + label1->setAlignment( TQt::AlignRight | TQt::AlignVCenter ); + d->channelCB = new TQComboBox(false, plainPage()); + d->channelCB->insertItem( i18n("Luminosity") ); + d->channelCB->insertItem( i18n("Red") ); + d->channelCB->insertItem( i18n("Green") ); + d->channelCB->insertItem( i18n("Blue") ); + d->channelCB->insertItem( i18n("Colors") ); + TQWhatsThis::add(d->channelCB, i18n("<p>Select the histogram channel to display here:<p>" + "<b>Luminosity</b>: display the image's luminosity values.<p>" + "<b>Red</b>: display the red image-channel values.<p>" + "<b>Green</b>: display the green image-channel values.<p>" + "<b>Blue</b>: display the blue image-channel values.<p>" + "<b>Colors</b>: Display all color channel values at the same time.")); + + d->scaleBG = new TQHButtonGroup(plainPage()); + d->scaleBG->setExclusive(true); + d->scaleBG->setFrameShape(TQFrame::NoFrame); + d->scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add(d->scaleBG, i18n("<p>Select the histogram scale here.<p>" + "If the image's maximal counts are small, you can use the linear scale.<p>" + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton( d->scaleBG ); + TQToolTip::add( linHistoButton, i18n( "<p>Linear" ) ); + d->scaleBG->insert(linHistoButton, HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( d->scaleBG ); + TQToolTip::add( logHistoButton, i18n( "<p>Logarithmic" ) ); + d->scaleBG->insert(logHistoButton, HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQLabel *label10 = new TQLabel(i18n("Colors:"), plainPage()); + label10->setAlignment( TQt::AlignRight | TQt::AlignVCenter ); + d->colorsCB = new TQComboBox(false, plainPage()); + d->colorsCB->insertItem( i18n("Red") ); + d->colorsCB->insertItem( i18n("Green") ); + d->colorsCB->insertItem( i18n("Blue") ); + d->colorsCB->setEnabled( false ); + TQWhatsThis::add( d->colorsCB, i18n("<p>Select the main color displayed with Colors Channel mode here:<p>" + "<b>Red</b>: Draw the red image channel in the foreground.<p>" + "<b>Green</b>: Draw the green image channel in the foreground.<p>" + "<b>Blue</b>: Draw the blue image channel in the foreground.<p>")); + + // --------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(plainPage()); + d->histogramWidget = new HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add(d->histogramWidget, i18n("<p>Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + d->hGradient = new ColorGradientWidget( ColorGradientWidget::Horizontal, 10, histoBox ); + d->hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + // --------------------------------------------------------------- + + d->tabView = new KTabWidget(plainPage()); + d->rawdecodingBox = new TQWidget(d->tabView); + TQGridLayout* rawGrid = new TQGridLayout(d->rawdecodingBox, 1, 2); + d->decodingSettingsBox = new DcrawSettingsWidget(d->rawdecodingBox, true, true, false); + + KFileDialog *inputDlg = d->decodingSettingsBox->inputProfileUrlEdit()->fileDialog(); + inputDlg->setPreviewWidget(new ICCPreviewWidget(inputDlg)); + + KFileDialog *outputDlg = d->decodingSettingsBox->outputProfileUrlEdit()->fileDialog(); + outputDlg->setPreviewWidget(new ICCPreviewWidget(outputDlg)); + + d->abortBtn = new TQPushButton(d->rawdecodingBox); + d->abortBtn->setText(i18n("Abort")); + d->abortBtn->setIconSet(SmallIconSet("process-stop")); + d->abortBtn->setEnabled(false); + TQToolTip::add(d->abortBtn, i18n("Abort the current Raw image preview.")); + + d->updateBtn = new TQPushButton(d->rawdecodingBox); + d->updateBtn->setText(i18n("Update")); + d->updateBtn->setIconSet(SmallIconSet("reload_page")); + d->updateBtn->setEnabled(false); + TQToolTip::add(d->updateBtn, i18n("Generate a Raw image preview using current settings.")); + + rawGrid->addMultiCellWidget(d->decodingSettingsBox, 0, 0, 0, 2); + rawGrid->addMultiCellWidget(d->abortBtn, 1, 1, 0, 0); + rawGrid->addMultiCellWidget(d->updateBtn, 1, 1, 2, 2); + rawGrid->setColStretch(1, 10); + rawGrid->setSpacing(spacingHint()); + rawGrid->setMargin(spacingHint()); + + // --------------------------------------------------------------- + + d->postProcessSettingsBox = new TQToolBox(d->tabView); + d->infoBox = new ImageDialogPreview(d->postProcessSettingsBox); + d->infoBox->showPreview(url); + + // --------------------------------------------------------------- + + d->advExposureBox = new TQWidget(d->postProcessSettingsBox); + TQGridLayout* advExposureLayout = new TQGridLayout(d->advExposureBox, 5, 2); + + d->brightnessLabel = new TQLabel(i18n("Brightness:"), d->advExposureBox); + d->brightnessInput = new RIntNumInput(d->advExposureBox); + d->brightnessInput->setRange(-100, 100, 1); + d->brightnessInput->setDefaultValue(0); + TQWhatsThis::add(d->brightnessInput->input(), i18n("<p>Set here the brightness adjustment of the image.")); + + d->contrastLabel = new TQLabel(i18n("Contrast:"), d->advExposureBox); + d->contrastInput = new RIntNumInput(d->advExposureBox); + d->contrastInput->setRange(-100, 100, 1); + d->contrastInput->setDefaultValue(0); + TQWhatsThis::add(d->contrastInput->input(), i18n("<p>Set here the contrast adjustment of the image.")); + + d->gammaLabel = new TQLabel(i18n("Gamma:"), d->advExposureBox); + d->gammaInput = new RDoubleNumInput(d->advExposureBox); + d->gammaInput->setPrecision(2); + d->gammaInput->setRange(0.1, 3.0, 0.01); + d->gammaInput->setDefaultValue(1.0); + TQWhatsThis::add(d->gammaInput->input(), i18n("Set here the gamma adjustement of the image")); + + d->saturationLabel = new TQLabel(i18n("Saturation:"), d->advExposureBox); + d->saturationInput = new RDoubleNumInput(d->advExposureBox); + d->saturationInput->setPrecision(2); + d->saturationInput->setRange(0.0, 2.0, 0.01); + d->saturationInput->setDefaultValue(1.0); + TQWhatsThis::add(d->saturationInput->input(), i18n("<p>Set here the color saturation correction.")); + + d->fineExposureLabel = new TQLabel(i18n("Exposure (E.V):"), d->advExposureBox); + d->fineExposureInput = new RDoubleNumInput(d->advExposureBox); + d->fineExposureInput->setPrecision(2); + d->fineExposureInput->setRange(-3.0, 3.0, 0.1); + d->fineExposureInput->setDefaultValue(0.0); + TQWhatsThis::add(d->fineExposureInput->input(), i18n("<p>This value in E.V will be used to perform " + "an exposure compensation of the image.")); + + advExposureLayout->addMultiCellWidget(d->brightnessLabel, 0, 0, 0, 0); + advExposureLayout->addMultiCellWidget(d->brightnessInput, 0, 0, 1, 2); + advExposureLayout->addMultiCellWidget(d->contrastLabel, 1, 1, 0, 0); + advExposureLayout->addMultiCellWidget(d->contrastInput, 1, 1, 1, 2); + advExposureLayout->addMultiCellWidget(d->gammaLabel, 2, 2, 0, 0); + advExposureLayout->addMultiCellWidget(d->gammaInput, 2, 2, 1, 2); + advExposureLayout->addMultiCellWidget(d->saturationLabel, 3, 3, 0, 0); + advExposureLayout->addMultiCellWidget(d->saturationInput, 3, 3, 1, 2); + advExposureLayout->addMultiCellWidget(d->fineExposureLabel, 4, 4, 0, 0); + advExposureLayout->addMultiCellWidget(d->fineExposureInput, 4, 4, 1, 2); + advExposureLayout->setRowStretch(5, 10); + advExposureLayout->setSpacing(0); + advExposureLayout->setMargin(spacingHint()); + + // --------------------------------------------------------------- + + d->curveBox = new TQWidget(d->postProcessSettingsBox); + TQGridLayout* curveLayout = new TQGridLayout(d->curveBox, 3, 2); + + ColorGradientWidget* vGradient = new ColorGradientWidget(ColorGradientWidget::Vertical, 10, d->curveBox); + vGradient->setColors( TQColor( "white" ), TQColor( "black" ) ); + + TQLabel *spacev = new TQLabel(d->curveBox); + spacev->setFixedWidth(1); + + d->curveWidget = new CurvesWidget(256, 192, d->curveBox); + TQWhatsThis::add(d->curveWidget, i18n("<p>This is the curve adjustment of the image luminosity")); + + d->resetCurveBtn = new TQToolButton(d->curveBox); + d->resetCurveBtn->setFixedSize(11, 11); + d->resetCurveBtn->setIconSet(SmallIconSet("reload_page", 8)); + d->resetCurveBtn->setFocusPolicy(TQWidget::NoFocus); + d->resetCurveBtn->setAutoRaise(true); + TQToolTip::add(d->resetCurveBtn, i18n("Reset curve to linear")); + + TQLabel *spaceh = new TQLabel(d->curveBox); + spaceh->setFixedHeight(1); + + ColorGradientWidget *hGradient = new ColorGradientWidget(ColorGradientWidget::Horizontal, 10, d->curveBox); + hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + curveLayout->addMultiCellWidget(vGradient, 0, 0, 0, 0); + curveLayout->addMultiCellWidget(spacev, 0, 0, 1, 1); + curveLayout->addMultiCellWidget(d->curveWidget, 0, 0, 2, 2); + curveLayout->addMultiCellWidget(spaceh, 1, 1, 2, 2); + curveLayout->addMultiCellWidget(d->resetCurveBtn, 1, 2, 0, 1); + curveLayout->addMultiCellWidget(hGradient, 2, 2, 2, 2); + curveLayout->setRowStretch(3, 10); + curveLayout->setSpacing(0); + curveLayout->setMargin(spacingHint()); + + // --------------------------------------------------------------- + + d->postProcessSettingsBox->addItem(d->advExposureBox, i18n("Exposure")); + d->postProcessSettingsBox->addItem(d->curveBox, i18n("Luminosity Curve")); + d->postProcessSettingsBox->setItemIconSet(0, SmallIconSet("contrast")); + d->postProcessSettingsBox->setItemIconSet(1, SmallIconSet("adjustcurves")); + + d->decodingSettingsBox->setItemIconSet(DcrawSettingsWidget::DEMOSAICING, SmallIconSet("kdcraw")); + d->decodingSettingsBox->setItemIconSet(DcrawSettingsWidget::WHITEBALANCE, SmallIconSet("whitebalance")); + d->decodingSettingsBox->setItemIconSet(DcrawSettingsWidget::CORRECTIONS, SmallIconSet("lensdistortion")); + d->decodingSettingsBox->setItemIconSet(DcrawSettingsWidget::COLORMANAGEMENT, SmallIconSet("colormanagement")); + d->decodingSettingsBox->updateMinimumWidth(); + + d->tabView->insertTab(d->rawdecodingBox, i18n("Raw Decoding"), 0); + d->tabView->insertTab(d->postProcessSettingsBox, i18n("Post Processing"), 1); + d->tabView->insertTab(d->infoBox, i18n("Info"), 2); + + // --------------------------------------------------------------- + + button(Default)->setText(i18n("Reset")); + button(Default)->setIconSet(SmallIconSet("reload_page")); + TQToolTip::add(button(Default), i18n("<p>Reset all settings to default values.")); + + button(Ok)->setText(i18n("Import")); + button(Ok)->setIconSet(SmallIconSet("ok")); + TQToolTip::add(button(Ok), i18n("<p>Import image to editor using current settings.")); + + button(Cancel)->setText(i18n("Use Default")); + button(Cancel)->setIconSet(SmallIconSet("go-home")); + TQToolTip::add(button(Cancel), i18n("<p>Use general Raw decoding settings to load this image in editor.")); + + // --------------------------------------------------------------- + + gridSettings->addMultiCellWidget(label1, 0, 0, 0, 0); + gridSettings->addMultiCellWidget(d->channelCB, 0, 0, 1, 1); + gridSettings->addMultiCellWidget(d->scaleBG, 0, 0, 4, 4); + gridSettings->addMultiCellWidget(label10, 1, 1, 0, 0); + gridSettings->addMultiCellWidget(d->colorsCB, 1, 1, 1, 1); + gridSettings->addMultiCellWidget(histoBox, 2, 3, 0, 4); + gridSettings->addMultiCellWidget(d->tabView, 4, 4, 0, 4); + gridSettings->setRowStretch(5, 10); + gridSettings->setColStretch(2, 10); + gridSettings->setSpacing(spacingHint()); + gridSettings->setMargin(0); + + // --------------------------------------------------------------- + + connect(d->channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(d->scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(d->colorsCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotColorsChanged(int))); + + connect(d->resetCurveBtn, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotResetCurve())); + + connect(d->updateBtn, TQ_SIGNAL(clicked()), + this, TQ_SIGNAL(signalUpdatePreview())); + + connect(d->abortBtn, TQ_SIGNAL(clicked()), + this, TQ_SIGNAL(signalAbortPreview())); + + connect(d->decodingSettingsBox, TQ_SIGNAL(signalSettingsChanged()), + this, TQ_SIGNAL(signalDemosaicingChanged())); + + connect(d->curveWidget, TQ_SIGNAL(signalCurvesChanged()), + this, TQ_SIGNAL(signalPostProcessingChanged())); + + connect(d->brightnessInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SIGNAL(signalPostProcessingChanged())); + + connect(d->contrastInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SIGNAL(signalPostProcessingChanged())); + + connect(d->gammaInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SIGNAL(signalPostProcessingChanged())); + + connect(d->saturationInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SIGNAL(signalPostProcessingChanged())); + + connect(d->fineExposureInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SIGNAL(signalPostProcessingChanged())); +} + +RawSettingsBox::~RawSettingsBox() +{ + delete d->curveWidget; + delete d; +} + +void RawSettingsBox::enableUpdateBtn(bool b) +{ + d->updateBtn->setEnabled(b); +} + +void RawSettingsBox::setBusy(bool b) +{ + d->decodingSettingsBox->setEnabled(!b); + d->abortBtn->setEnabled(b); +} + +void RawSettingsBox::setDemosaicedImage(DImg& img) +{ + d->curveWidget->stopHistogramComputation(); + d->curveWidget->updateData(img.bits(), img.width(), img.height(), img.sixteenBit()); +} + +void RawSettingsBox::setPostProcessedImage(DImg& img) +{ + d->histogramWidget->stopHistogramComputation(); + d->histogramWidget->updateData(img.bits(), img.width(), img.height(), img.sixteenBit()); +} + +void RawSettingsBox::resetSettings() +{ + d->decodingSettingsBox->setDefaultSettings(); + d->brightnessInput->slotReset(); + d->contrastInput->slotReset(); + d->gammaInput->slotReset(); + d->saturationInput->slotReset(); + d->fineExposureInput->slotReset(); + slotResetCurve(); +} + +void RawSettingsBox::slotResetCurve() +{ + d->curveWidget->reset(); + emit signalPostProcessingChanged(); +} + +HistogramWidget* RawSettingsBox::histogram() const +{ + return d->histogramWidget; +} + +CurvesWidget* RawSettingsBox::curve() const +{ + return d->curveWidget; +} + +void RawSettingsBox::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("RAW Import Settings"); + + d->channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", RawSettingsBoxPriv::LuminosityChannel)); + d->scaleBG->setButton(config->readNumEntry("Histogram Scale", HistogramWidget::LogScaleHistogram)); + d->colorsCB->setCurrentItem(config->readNumEntry("Histogram Color", RawSettingsBoxPriv::AllColorsRed)); + + d->decodingSettingsBox->setSixteenBits(config->readBoolEntry("SixteenBitsImage", false)); + d->decodingSettingsBox->setWhiteBalance((DRawDecoding::WhiteBalance) + config->readNumEntry("White Balance", + DRawDecoding::CAMERA)); + d->decodingSettingsBox->setCustomWhiteBalance(config->readNumEntry("Custom White Balance", 6500)); + d->decodingSettingsBox->setCustomWhiteBalanceGreen(config->readDoubleNumEntry("Custom White Balance Green", 1.0)); + d->decodingSettingsBox->setFourColor(config->readBoolEntry("Four Color RGB", false)); + d->decodingSettingsBox->setUnclipColor(config->readNumEntry("Unclip Color", 0)); + d->decodingSettingsBox->setDontStretchPixels(config->readBoolEntry("Dont Stretch Pixels", false)); + d->decodingSettingsBox->setNoiseReduction(config->readBoolEntry("Use Noise Reduction", false)); + d->decodingSettingsBox->setUseBlackPoint(config->readBoolEntry("Use Black Point", false)); + d->decodingSettingsBox->setBlackPoint(config->readNumEntry("Black Point", 0)); + d->decodingSettingsBox->setUseWhitePoint(config->readBoolEntry("Use White Point", false)); + d->decodingSettingsBox->setWhitePoint(config->readNumEntry("White Point", 0)); + d->decodingSettingsBox->setMedianFilterPasses(config->readNumEntry("Median Filter Passes", 0)); + d->decodingSettingsBox->setNRThreshold(config->readNumEntry("NR Threshold", 100)); + d->decodingSettingsBox->setUseCACorrection(config->readBoolEntry("EnableCACorrection", false)); + d->decodingSettingsBox->setcaRedMultiplier(config->readDoubleNumEntry("caRedMultiplier", 1.0)); + d->decodingSettingsBox->setcaBlueMultiplier(config->readDoubleNumEntry("caBlueMultiplier", 1.0)); + + d->decodingSettingsBox->setQuality( + (DRawDecoding::DecodingQuality)config->readNumEntry("Decoding Quality", + (int)(DRawDecoding::BILINEAR))); + + d->decodingSettingsBox->setInputColorSpace( + (DRawDecoding::InputColorSpace)config->readNumEntry("Input Color Space", + (int)(DRawDecoding::NOINPUTCS))); + + d->decodingSettingsBox->setOutputColorSpace( + (DRawDecoding::OutputColorSpace)config->readNumEntry("Output Color Space", + (int)(DRawDecoding::SRGB))); + + d->decodingSettingsBox->setInputColorProfile(config->readPathEntry("Input Color Profile", TQString())); + d->decodingSettingsBox->setOutputColorProfile(config->readPathEntry("Output Color Profile", TQString())); + + d->brightnessInput->setValue(config->readNumEntry("Brightness", 0)); + d->contrastInput->setValue(config->readNumEntry("Contrast", 0)); + d->gammaInput->setValue(config->readDoubleNumEntry("Gamma", 1.0)); + d->saturationInput->setValue(config->readDoubleNumEntry("Saturation", 1.0)); + d->fineExposureInput->setValue(config->readDoubleNumEntry("FineExposure", 0.0)); + + d->curveWidget->reset(); + + for (int j = 0 ; j <= 17 ; j++) + { + TQPoint disable(-1, -1); + TQPoint p = config->readPointEntry(TQString("CurveAjustmentPoint%1").arg(j), &disable); + if (!d->decodingSettingsBox->sixteenBits() && p != disable) + { + // Restore point as 16 bits depth. + p.setX(p.x()/255); + p.setY(p.y()/255); + } + d->curveWidget->curves()->setCurvePoint(ImageHistogram::ValueChannel, j, p); + } + d->curveWidget->curves()->curvesCalculateCurve(ImageHistogram::ValueChannel); + + d->tabView->setCurrentPage(config->readNumEntry("Settings Page", 0)); + d->decodingSettingsBox->setCurrentIndex(config->readNumEntry("Decoding Settings Tab", DcrawSettingsWidget::DEMOSAICING)); + d->postProcessSettingsBox->setCurrentIndex(config->readNumEntry("Post Processing Settings Tab", 0)); + + slotChannelChanged(d->channelCB->currentItem()); + slotScaleChanged(d->scaleBG->selectedId()); + slotColorsChanged(d->colorsCB->currentItem()); +} + +void RawSettingsBox::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("RAW Import Settings"); + + config->writeEntry("Histogram Channel", d->channelCB->currentItem()); + config->writeEntry("Histogram Scale", d->scaleBG->selectedId()); + config->writeEntry("Histogram Color", d->colorsCB->currentItem()); + + config->writeEntry("SixteenBitsImage", d->decodingSettingsBox->sixteenBits()); + config->writeEntry("White Balance", d->decodingSettingsBox->whiteBalance()); + config->writeEntry("Custom White Balance", d->decodingSettingsBox->customWhiteBalance()); + config->writeEntry("Custom White Balance Green", d->decodingSettingsBox->customWhiteBalanceGreen()); + config->writeEntry("Four Color RGB", d->decodingSettingsBox->useFourColor()); + config->writeEntry("Unclip Color", d->decodingSettingsBox->unclipColor()); + config->writeEntry("Dont Stretch Pixels", d->decodingSettingsBox->useDontStretchPixels()); + config->writeEntry("Use Noise Reduction", d->decodingSettingsBox->useNoiseReduction()); + config->writeEntry("Use Black Point", d->decodingSettingsBox->useBlackPoint()); + config->writeEntry("Black Point", d->decodingSettingsBox->blackPoint()); + config->writeEntry("Use White Point", d->decodingSettingsBox->useWhitePoint()); + config->writeEntry("White Point", d->decodingSettingsBox->whitePoint()); + config->writeEntry("MedianFilterPasses", d->decodingSettingsBox->medianFilterPasses()); + config->writeEntry("NR Threshold", d->decodingSettingsBox->NRThreshold()); + config->writeEntry("EnableCACorrection", d->decodingSettingsBox->useCACorrection()); + config->writeEntry("caRedMultiplier", d->decodingSettingsBox->caRedMultiplier()); + config->writeEntry("caBlueMultiplier", d->decodingSettingsBox->caBlueMultiplier()); + config->writeEntry("Decoding Quality", (int)d->decodingSettingsBox->quality()); + config->writeEntry("Input Color Space", (int)d->decodingSettingsBox->inputColorSpace()); + config->writeEntry("Output Color Space", (int)d->decodingSettingsBox->outputColorSpace()); + config->writeEntry("Input Color Profile", d->decodingSettingsBox->inputColorProfile()); + config->writeEntry("Output Color Profile", d->decodingSettingsBox->outputColorProfile()); + + config->writeEntry("Brightness", d->brightnessInput->value()); + config->writeEntry("Contrast", d->contrastInput->value()); + config->writeEntry("Gamma", d->gammaInput->value()); + config->writeEntry("Saturation", d->saturationInput->value()); + config->writeEntry("FineExposure", d->fineExposureInput->value()); + + for (int j = 0 ; j <= 17 ; j++) + { + TQPoint p = d->curveWidget->curves()->getCurvePoint(ImageHistogram::ValueChannel, j); + if (!d->curveWidget->curves()->isSixteenBits()) + { + // Store point as 16 bits depth. + p.setX(p.x()*255); + p.setY(p.y()*255); + } + config->writeEntry(TQString("CurveAjustmentPoint%1").arg(j), p); + } + + config->writeEntry("Settings Page", d->tabView->currentPage()); + config->writeEntry("Decoding Settings Tab", d->decodingSettingsBox->currentIndex()); + config->writeEntry("Post Processing Settings Tab", d->postProcessSettingsBox->currentIndex()); + config->sync(); +} + +DRawDecoding RawSettingsBox::settings() +{ + DRawDecoding settings; + settings.sixteenBitsImage = d->decodingSettingsBox->sixteenBits(); + settings.whiteBalance = d->decodingSettingsBox->whiteBalance(); + settings.customWhiteBalance = d->decodingSettingsBox->customWhiteBalance(); + settings.customWhiteBalanceGreen = d->decodingSettingsBox->customWhiteBalanceGreen(); + settings.RGBInterpolate4Colors = d->decodingSettingsBox->useFourColor(); + settings.unclipColors = d->decodingSettingsBox->unclipColor(); + settings.DontStretchPixels = d->decodingSettingsBox->useDontStretchPixels(); + settings.enableNoiseReduction = d->decodingSettingsBox->useNoiseReduction(); + settings.enableBlackPoint = d->decodingSettingsBox->useBlackPoint(); + settings.blackPoint = d->decodingSettingsBox->blackPoint(); + settings.enableWhitePoint = d->decodingSettingsBox->useWhitePoint(); + settings.whitePoint = d->decodingSettingsBox->whitePoint(); + settings.medianFilterPasses = d->decodingSettingsBox->medianFilterPasses(); + settings.NRThreshold = d->decodingSettingsBox->NRThreshold(); + settings.enableCACorrection = d->decodingSettingsBox->useCACorrection(); + settings.caMultiplier[0] = d->decodingSettingsBox->caRedMultiplier(); + settings.caMultiplier[1] = d->decodingSettingsBox->caBlueMultiplier(); + settings.RAWQuality = d->decodingSettingsBox->quality(); + settings.inputColorSpace = d->decodingSettingsBox->inputColorSpace(); + settings.outputColorSpace = d->decodingSettingsBox->outputColorSpace(); + settings.inputProfile = d->decodingSettingsBox->inputColorProfile(); + settings.outputProfile = d->decodingSettingsBox->outputColorProfile(); + + settings.lightness = (double)d->brightnessInput->value()/250.0; + settings.contrast = (double)(d->contrastInput->value()/100.0) + 1.00; + settings.gamma = d->gammaInput->value(); + settings.saturation = d->saturationInput->value(); + settings.exposureComp = d->fineExposureInput->value(); + + if (d->curveWidget->curves()->isDirty()) + settings.curveAdjust = d->curveWidget->curves()->getCurvePoints(ImageHistogram::ValueChannel); + + return settings; +} + +void RawSettingsBox::slotChannelChanged(int channel) +{ + switch(channel) + { + case RawSettingsBoxPriv::LuminosityChannel: + d->histogramWidget->m_channelType = HistogramWidget::ValueHistogram; + d->hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + d->colorsCB->setEnabled(false); + break; + + case RawSettingsBoxPriv::RedChannel: + d->histogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + d->hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + d->colorsCB->setEnabled(false); + break; + + case RawSettingsBoxPriv::GreenChannel: + d->histogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + d->hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + d->colorsCB->setEnabled(false); + break; + + case RawSettingsBoxPriv::BlueChannel: + d->histogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + d->hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + d->colorsCB->setEnabled(false); + break; + + case RawSettingsBoxPriv::ColorChannels: + d->histogramWidget->m_channelType = HistogramWidget::ColorChannelsHistogram; + d->hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + d->colorsCB->setEnabled(true); + break; + } + + d->histogramWidget->repaint(false); +} + +void RawSettingsBox::slotScaleChanged(int scale) +{ + d->histogramWidget->m_scaleType = scale; + d->histogramWidget->repaint(false); +} + +void RawSettingsBox::slotColorsChanged(int color) +{ + switch(color) + { + case RawSettingsBoxPriv::AllColorsGreen: + d->histogramWidget->m_colorType = HistogramWidget::GreenColor; + break; + + case RawSettingsBoxPriv::AllColorsBlue: + d->histogramWidget->m_colorType = HistogramWidget::BlueColor; + break; + + default: // Red. + d->histogramWidget->m_colorType = HistogramWidget::RedColor; + break; + } + + d->histogramWidget->repaint(false); +} + +} // NameSpace Digikam diff --git a/src/utilities/imageeditor/rawimport/rawsettingsbox.h b/src/utilities/imageeditor/rawimport/rawsettingsbox.h new file mode 100644 index 00000000..546ce1e5 --- /dev/null +++ b/src/utilities/imageeditor/rawimport/rawsettingsbox.h @@ -0,0 +1,91 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-11 + * Description : Raw import settings box + * + * Copyright (C) 2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef RAWSETTINGSBOX_H +#define RAWSETTINGSBOX_H + +// KDE includes. + +#include <kurl.h> + +// Local includes. + +#include "editortoolsettings.h" +#include "dimg.h" +#include "digikam_export.h" + +namespace Digikam +{ + +class HistogramWidget; +class CurvesWidget; +class RawSettingsBoxPriv; + +class DIGIKAM_EXPORT RawSettingsBox : public EditorToolSettings +{ + TQ_OBJECT + + +public: + + RawSettingsBox(const KURL& url, TQWidget *parent); + ~RawSettingsBox(); + + void setBusy(bool b); + + HistogramWidget* histogram() const; + CurvesWidget* curve() const; + DRawDecoding settings(); + + void writeSettings(); + void readSettings(); + + void setDemosaicedImage(DImg& img); + void setPostProcessedImage(DImg& img); + + void enableUpdateBtn(bool b); + + void resetSettings(); + +signals: + + void signalUpdatePreview(); + void signalAbortPreview(); + void signalDemosaicingChanged(); + void signalPostProcessingChanged(); + +private slots: + + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotColorsChanged(int color); + + void slotResetCurve(); + +private: + + RawSettingsBoxPriv *d; +}; + +} // NameSpace Digikam + +#endif // RAWSETTINGSBOX_H diff --git a/src/utilities/imageeditor/tools/Makefile.am b/src/utilities/imageeditor/tools/Makefile.am new file mode 100644 index 00000000..b76207ff --- /dev/null +++ b/src/utilities/imageeditor/tools/Makefile.am @@ -0,0 +1,19 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libdimgeditortools.la + +libdimgeditortools_la_SOURCES = imageresize.cpp imageprint.cpp + +libdimgeditortools_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TDEPRINT) + +INCLUDES= -I$(top_srcdir)/src/digikam \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/greycstoration \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/utilities/imageeditor/editor \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + diff --git a/src/utilities/imageeditor/tools/imageprint.cpp b/src/utilities/imageeditor/tools/imageprint.cpp new file mode 100644 index 00000000..5cab236b --- /dev/null +++ b/src/utilities/imageeditor/tools/imageprint.cpp @@ -0,0 +1,814 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-13 + * Description : image editor printing interface. + * + * Copyright (C) 2006 by F.J. Cruz <fj.cruz@supercable.es> + * Copyright (C) 2004-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * KeepRatio and Alignment options imported from Gwenview program. + * Copyright (c) 2003 Angelo Naselli <anaselli at linux dot it> + * + * Original printing code from Kuickshow program. + * Copyright (C) 2002 Carsten Pfeiffer <pfeiffer at kde.org> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqobject.h> +#include <tqpixmap.h> +#include <tqlayout.h> +#include <tqgroupbox.h> +#include <tqbuttongroup.h> +#include <tqstring.h> +#include <tqsize.h> +#include <tqcursor.h> +#include <tqlabel.h> +#include <tqhbox.h> +#include <tqcheckbox.h> +#include <tqfont.h> +#include <tqgrid.h> +#include <tqimage.h> +#include <tqpaintdevicemetrics.h> +#include <tqpainter.h> +#include <tqradiobutton.h> +#include <tqvbuttongroup.h> +#include <tqcolor.h> +#include <tqcombobox.h> +#include <tqstyle.h> +#include <tqpushbutton.h> + +// KDE includes. + +#include <tdelocale.h> +#include <tdemessagebox.h> +#include <kstandarddirs.h> +#include <tdeapplication.h> +#include <tdeconfig.h> +#include <kimageio.h> +#include <kcombobox.h> +#include <tdeglobalsettings.h> +#include <knuminput.h> +#include <kprinter.h> +#include <tdetempfile.h> +#include <kpropertiesdialog.h> + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "editorwindow.h" +#include "icctransform.h" +#include "imageprint.h" +#include "imageprint.moc" + +namespace Digikam +{ + +class ImagePrintPrivate +{ + +public: + + ImagePrintPrivate(){} + + TQString filename; + TQString inProfilePath; + TQString outputProfilePath; + + DImg image; +}; + +ImagePrint::ImagePrint(DImg& image, KPrinter& printer, const TQString& filename) + : m_printer(printer) +{ + d = new ImagePrintPrivate(); + d->image = image; + d->filename = filename; +} + +ImagePrint::~ImagePrint() +{ + delete d; +} + +bool ImagePrint::printImageWithTQt() +{ + if ( d->image.isNull() ) + { + DWarning() << "Supplied Image for printing is null" << endl; + return false; + } + + TQString t = "true"; + TQString f = "false"; + + if (m_printer.option( "app-imageeditor-color-managed") != f) + { + IccTransform *transform = new IccTransform(); + readSettings(); + + if (d->image.getICCProfil().isNull()) + { + transform->setProfiles( d->inProfilePath, d->outputProfilePath ); + } + else + { + transform->setProfiles(d->outputProfilePath); + } + + transform->apply( d->image ); + } + + TQImage image2Print = d->image.copyTQImage(); + + // Black & white print ? + if ( m_printer.option( "app-imageeditor-blackwhite" ) != f) + { + image2Print = image2Print.convertDepth( 1, TQt::MonoOnly | + TQt::ThresholdDither | + TQt::AvoidDither ); + } + + TQPainter p; + p.begin( &m_printer ); + + TQPaintDeviceMetrics metrics( &m_printer ); + p.setFont( TDEGlobalSettings::generalFont() ); + TQFontMetrics fm = p.fontMetrics(); + + int w, h; // will be set to the width and height of the printer + // when the orientation is decided. + w = metrics.width(); + h = metrics.height(); + int filenameOffset = 0; + + TQSize size = image2Print.size(); + + bool printFilename = m_printer.option( "app-imageeditor-printFilename" ) != f; + if ( printFilename ) + { + // filename goes into one line! + filenameOffset = fm.lineSpacing() + 14; + h -= filenameOffset; + } + + if ( m_printer.option( "app-imageeditor-scaleToFit" ) != f ) + { + if ( m_printer.option( "app-imageeditor-auto-rotate" ) == t ) + m_printer.setOrientation( size.width() <= size.height() ? KPrinter::Portrait + : KPrinter::Landscape ); + + // Scale image to fit pagesize + size.scale( w, h, TQSize::ScaleMin ); + } + else + { + int unit = (m_printer.option("app-imageeditor-scale-unit").isEmpty() ? + ImageEditorPrintDialogPage::DK_INCHES : m_printer.option("app-imageeditor-scale-unit").toInt()); + double inches = 1; + + if (unit == ImageEditorPrintDialogPage::DK_MILLIMETERS) + { + inches = 1/25.4; + } + else if (unit == ImageEditorPrintDialogPage::DK_CENTIMETERS) + { + inches = 1/2.54; + } + + double wImg = (m_printer.option("app-imageeditor-scale-width").isEmpty() ? + 1 : m_printer.option("app-imageeditor-scale-width").toDouble()) * inches; + double hImg = (m_printer.option("app-imageeditor-scale-height").isEmpty() ? + 1 : m_printer.option("app-imageeditor-scale-height").toDouble()) * inches; + size.setWidth( int(wImg * m_printer.resolution()) ); + size.setHeight( int(hImg * m_printer.resolution()) ); + + if ( m_printer.option( "app-imageeditor-auto-rotate" ) == t ) + m_printer.setOrientation( wImg <= hImg ? KPrinter::Portrait : KPrinter::Landscape ); + + if (size.width() > w || size.height() > h) + { + int resp = KMessageBox::warningYesNoCancel(TDEApplication::kApplication()->mainWidget(), + i18n("The image will not fit on the page, what do you want to do?"), + TQString(),KStdGuiItem::cont(), + i18n("Shrink") ); + + if (resp==KMessageBox::Cancel) + { + m_printer.abort(); + // no need to return false, user decided to abort + return true; + } + else if (resp == KMessageBox::No) + { // Shrink + size.scale(w, h, TQSize::ScaleMin); + } + } + } + + // Align image. + int alignment = (m_printer.option("app-imageeditor-alignment").isEmpty() ? + TQt::AlignCenter : m_printer.option("app-imageeditor-alignment").toInt()); + + int x = 0; + int y = 0; + + // x - alignment + if ( alignment & TQt::AlignHCenter ) + x = (w - size.width())/2; + else if ( alignment & TQt::AlignLeft ) + x = 0; + else if ( alignment & TQt::AlignRight ) + x = w - size.width(); + + // y - alignment + if ( alignment & TQt::AlignVCenter ) + y = (h - size.height())/2; + else if ( alignment & TQt::AlignTop ) + y = 0; + else if ( alignment & TQt::AlignBottom ) + y = h - size.height(); + + // Perform the actual drawing. + p.drawImage( TQRect( x, y, size.width(), size.height()), image2Print ); + + if ( printFilename ) + { + TQString fname = minimizeString( d->filename, fm, w ); + + if ( !fname.isEmpty() ) + { + int fw = fm.width( fname ); + int x = (w - fw)/2; + int y = metrics.height() - filenameOffset/2; + p.drawText( x, y, fname ); + } + } + + p.end(); + + return true; +} + +TQString ImagePrint::minimizeString( TQString text, const TQFontMetrics& metrics, + int maxWidth ) +{ + // no sense to cut that tiny little string + if ( text.length() <= 5 ) + return TQString(); + + bool changed = false; + + while ( metrics.width( text ) > maxWidth ) + { + int mid = text.length() / 2; + // remove 2 characters in the middle + text.remove( mid, 2 ); + changed = true; + } + + // add "..." in the middle + if ( changed ) + { + int mid = text.length() / 2; + + // sanity check + if ( mid <= 5 ) + return TQString(); + + text.replace( mid - 1, 3, "..." ); + } + + return text; +} + +void ImagePrint::readSettings() +{ + TDEConfig* config = kapp->config(); + + config->setGroup("Color Management"); + + d->inProfilePath = config->readPathEntry("WorkSpaceProfile"); + d->outputProfilePath = config->readPathEntry("ProofProfileFile"); +} + +// Image print dialog class ------------------------------------------------------------- + +class ImageEditorPrintDialogPagePrivate +{ + +public: + + ImageEditorPrintDialogPagePrivate() + { + cmEnabled = false; + position = 0; + keepRatio = 0; + scaleToFit = 0; + scale = 0; + addFileName = 0; + blackwhite = 0; + autoRotate = 0; + colorManaged = 0; + cmPreferences = 0; + parent = 0; + width = 0; + height = 0; + units = 0; + } + + bool cmEnabled; + + TQRadioButton *scaleToFit; + TQRadioButton *scale; + + TQCheckBox *keepRatio; + TQCheckBox *addFileName; + TQCheckBox *blackwhite; + TQCheckBox *autoRotate; + TQCheckBox *colorManaged; + + TQPushButton *cmPreferences; + + TQWidget *parent; + + KDoubleNumInput *width; + KDoubleNumInput *height; + + KComboBox *position; + KComboBox *units; + + DImg image; + + ImageEditorPrintDialogPage::Unit previousUnit; +}; + +ImageEditorPrintDialogPage::ImageEditorPrintDialogPage(DImg& image, TQWidget *parent, const char *name ) + : KPrintDialogPage( parent, name ) +{ + d = new ImageEditorPrintDialogPagePrivate; + d->image = image; + d->parent = parent; + setTitle( i18n("Image Settings") ); + + readSettings(); + + TQVBoxLayout *layout = new TQVBoxLayout( this ); + layout->setMargin( KDialog::marginHint() ); + layout->setSpacing( KDialog::spacingHint() ); + + // ------------------------------------------------------------------------ + + TQHBoxLayout *layout2 = new TQHBoxLayout( layout ); + layout2->setSpacing(3); + + TQLabel* textLabel = new TQLabel( this, "Image position:" ); + textLabel->setText( i18n( "Image position:" ) ); + layout2->addWidget( textLabel ); + d->position = new KComboBox( false, this, "Print position" ); + d->position->clear(); + d->position->insertItem( i18n( "Top-Left" ) ); + d->position->insertItem( i18n( "Top-Central" ) ); + d->position->insertItem( i18n( "Top-Right" ) ); + d->position->insertItem( i18n( "Central-Left" ) ); + d->position->insertItem( i18n( "Central" ) ); + d->position->insertItem( i18n( "Central-Right" ) ); + d->position->insertItem( i18n( "Bottom-Left" ) ); + d->position->insertItem( i18n( "Bottom-Central" ) ); + d->position->insertItem( i18n( "Bottom-Right" ) ); + layout2->addWidget( d->position ); + TQSpacerItem *spacer1 = new TQSpacerItem( 101, 21, TQSizePolicy::Expanding, TQSizePolicy::Minimum ); + layout2->addItem( spacer1 ); + + d->addFileName = new TQCheckBox( i18n("Print fi&lename below image"), this); + d->addFileName->setChecked( false ); + layout->addWidget( d->addFileName ); + + d->blackwhite = new TQCheckBox ( i18n("Print image in &black and white"), this); + d->blackwhite->setChecked( false ); + layout->addWidget (d->blackwhite ); + + d->autoRotate = new TQCheckBox( i18n("&Auto-rotate page"), this ); + d->autoRotate->setChecked( false ); + layout->addWidget( d->autoRotate ); + + // ------------------------------------------------------------------------ + + TQHBox *cmbox = new TQHBox(this); + + d->colorManaged = new TQCheckBox(i18n("Use Color Management for Printing"), cmbox); + d->colorManaged->setChecked( false ); + + d->cmPreferences = new TQPushButton(i18n("Settings..."), cmbox); + + TQWidget *space = new TQWidget(cmbox); + cmbox->setStretchFactor(space, 10); + cmbox->setSpacing(KDialog::spacingHint()); + + layout->addWidget(cmbox); + + // ------------------------------------------------------------------------ + + TQVButtonGroup *group = new TQVButtonGroup( i18n("Scaling"), this ); + group->setRadioButtonExclusive( true ); + layout->addWidget( group ); + + d->scaleToFit = new TQRadioButton( i18n("Scale image to &fit"), group ); + d->scaleToFit->setChecked( true ); + + d->scale = new TQRadioButton( i18n("Print e&xact size: "), group ); + + TQHBox *hb = new TQHBox( group ); + hb->setSpacing( KDialog::spacingHint() ); + TQWidget *w = new TQWidget(hb); + w->setFixedWidth(d->scale->style().subRect( TQStyle::SR_RadioButtonIndicator, d->scale ).width()); + + d->width = new KDoubleNumInput( hb, "exact width" ); + d->width->setMinValue( 1 ); + + new TQLabel( "x", hb ); + + d->height = new KDoubleNumInput( hb, "exact height" ); + d->height->setMinValue( 1 ); + + d->units = new KComboBox( false, hb, "unit combobox" ); + d->units->insertItem( i18n("Millimeters") ); + d->units->insertItem( i18n("Centimeters") ); + d->units->insertItem( i18n("Inches") ); + + d->keepRatio = new TQCheckBox( i18n("Keep ratio"), hb); + + w = new TQWidget(hb); + hb->setStretchFactor( w, 1 ); + d->previousUnit = DK_MILLIMETERS; + + // ------------------------------------------------------------------------ + + connect( d->colorManaged, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotAlertSettings( bool )) ); + + connect( d->cmPreferences, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotSetupDlg()) ); + + connect( d->scale, TQ_SIGNAL( toggled( bool )), + this, TQ_SLOT( toggleScaling( bool ))); + + connect(d->width, TQ_SIGNAL( valueChanged( double )), + this, TQ_SLOT( slotWidthChanged( double ))); + + connect(d->height, TQ_SIGNAL( valueChanged( double )), + this, TQ_SLOT( slotHeightChanged( double ))); + + connect(d->keepRatio, TQ_SIGNAL( toggled( bool )), + this, TQ_SLOT( toggleRatio( bool ))); + + connect(d->units, TQ_SIGNAL(activated(const TQString &)), + this, TQ_SLOT(slotUnitChanged(const TQString& ))); +} + +ImageEditorPrintDialogPage::~ImageEditorPrintDialogPage() +{ + delete d; +} + +void ImageEditorPrintDialogPage::getOptions( TQMap<TQString,TQString>& opts, bool /*incldef*/ ) +{ + TQString t = "true"; + TQString f = "false"; + + opts["app-imageeditor-alignment"] = TQString::number(getPosition(d->position->currentText())); + opts["app-imageeditor-printFilename"] = d->addFileName->isChecked() ? t : f; + opts["app-imageeditor-blackwhite"] = d->blackwhite->isChecked() ? t : f; + opts["app-imageeditor-scaleToFit"] = d->scaleToFit->isChecked() ? t : f; + opts["app-imageeditor-scale"] = d->scale->isChecked() ? t : f; + opts["app-imageeditor-scale-unit"] = TQString::number(stringToUnit(d->units->currentText())); + opts["app-imageeditor-scale-width"] = TQString::number( d->width->value() ); + opts["app-imageeditor-scale-height"] = TQString::number( d->height->value() ); + opts["app-imageeditor-scale-KeepRatio"] = d->keepRatio->isChecked() ? t : f; + opts["app-imageeditor-auto-rotate"] = d->autoRotate->isChecked() ? t : f; + opts["app-imageeditor-color-managed"] = d->colorManaged->isChecked() ? t : f; +} + +void ImageEditorPrintDialogPage::setOptions( const TQMap<TQString,TQString>& opts ) +{ + TQString t = "true"; + TQString f = "false"; + TQString stVal; + bool ok; + double dVal; + int iVal; + + iVal = opts["app-imageeditor-alignment"].toInt( &ok ); + if (ok) + { + stVal = setPosition(iVal); + d->position->setCurrentItem(stVal); + } + + d->addFileName->setChecked( opts["app-imageeditor-printFilename"] != f ); + // This sound strange, but if I copy the code on the line above, the checkbox + // was always checked. And this is not the wanted behavior. So, with this works. + // KPrint magic ;-) + d->blackwhite->setChecked ( false ); + d->scaleToFit->setChecked( opts["app-imageeditor-scaleToFit"] != f ); + d->scale->setChecked( opts["app-imageeditor-scale"] == t ); + d->autoRotate->setChecked( opts["app-imageeditor-auto-rotate"] == t ); + + d->colorManaged->setChecked( false ); + + Unit unit = static_cast<Unit>( opts["app-imageeditor-scale-unit"].toInt( &ok ) ); + if (ok) + { + stVal = unitToString(unit); + d->units->setCurrentItem(stVal); + d->previousUnit = unit; + } + else + { + //for back compatibility + d->units->setCurrentItem(i18n("Millimeters")); + } + + dVal = opts["app-imageeditor-scale-width"].toDouble( &ok ); + + if ( ok ) + d->width->setValue( dVal ); + + dVal = opts["app-imageeditor-scale-height"].toDouble( &ok ); + + if ( ok ) + d->height->setValue( dVal ); + + if ( d->scale->isChecked() == d->scaleToFit->isChecked() ) + d->scaleToFit->setChecked( !d->scale->isChecked() ); + + d->keepRatio->setChecked( opts["app-imageeditor-scale-KeepRatio"] == t ); + +} +int ImageEditorPrintDialogPage::getPosition(const TQString& align) +{ + int alignment; + + if (align == i18n("Central-Left")) + { + alignment = TQt::AlignLeft | TQt::AlignVCenter; + } + else if (align == i18n("Central-Right")) + { + alignment = TQt::AlignRight | TQt::AlignVCenter; + } + else if (align == i18n("Top-Left")) + { + alignment = TQt::AlignTop | TQt::AlignLeft; + } + else if (align == i18n("Top-Right")) + { + alignment = TQt::AlignTop | TQt::AlignRight; + } + else if (align == i18n("Bottom-Left")) + { + alignment = TQt::AlignBottom | TQt::AlignLeft; + } + else if (align == i18n("Bottom-Right")) + { + alignment = TQt::AlignBottom | TQt::AlignRight; + } + else if (align == i18n("Top-Central")) + { + alignment = TQt::AlignTop | TQt::AlignHCenter; + } + else if (align == i18n("Bottom-Central")) + { + alignment = TQt::AlignBottom | TQt::AlignHCenter; + } + else + { + // Central + alignment = TQt::AlignCenter; // TQt::AlignHCenter || TQt::AlignVCenter + } + + return alignment; +} + +TQString ImageEditorPrintDialogPage::setPosition(int align) +{ + TQString alignment; + + if (align == (TQt::AlignLeft | TQt::AlignVCenter)) + { + alignment = i18n("Central-Left"); + } + else if (align == (TQt::AlignRight | TQt::AlignVCenter)) + { + alignment = i18n("Central-Right"); + } + else if (align == (TQt::AlignTop | TQt::AlignLeft)) + { + alignment = i18n("Top-Left"); + } + else if (align == (TQt::AlignTop | TQt::AlignRight)) + { + alignment = i18n("Top-Right"); + } + else if (align == (TQt::AlignBottom | TQt::AlignLeft)) + { + alignment = i18n("Bottom-Left"); + } + else if (align == (TQt::AlignBottom | TQt::AlignRight)) + { + alignment = i18n("Bottom-Right"); + } + else if (align == (TQt::AlignTop | TQt::AlignHCenter)) + { + alignment = i18n("Top-Central"); + } + else if (align == (TQt::AlignBottom | TQt::AlignHCenter)) + { + alignment = i18n("Bottom-Central"); + } + else + { + // Central: TQt::AlignCenter or (TQt::AlignHCenter || TQt::AlignVCenter) + alignment = i18n("Central"); + } + + return alignment; +} + +void ImageEditorPrintDialogPage::toggleScaling( bool enable ) +{ + d->width->setEnabled( enable ); + d->height->setEnabled( enable ); + d->units->setEnabled( enable ); + d->keepRatio->setEnabled( enable ); +} + +void ImageEditorPrintDialogPage::slotHeightChanged (double value) +{ + d->width->blockSignals(true); + d->height->blockSignals(true); + + if (d->keepRatio->isChecked()) + { + double width = (d->image.width() * value) / d->image.height(); + d->width->setValue( width ? width : 1.); + } + d->height->setValue(value); + + d->width->blockSignals(false); + d->height->blockSignals(false); +} + +void ImageEditorPrintDialogPage::slotWidthChanged (double value) +{ + d->width->blockSignals(true); + d->height->blockSignals(true); + + if (d->keepRatio->isChecked()) + { + double height = (d->image.height() * value) / d->image.width(); + d->height->setValue( height ? height : 1); + } + d->width->setValue(value); + + d->width->blockSignals(false); + d->height->blockSignals(false); +} + +void ImageEditorPrintDialogPage::toggleRatio( bool enable ) +{ + if (!enable) return; + // choosing a startup value of 15x10 cm (common photo dimention) + // mContent->mHeight->value() or mContent->mWidth->value() + // are usually empty at startup and hxw (0x0) is not good IMO keeping ratio + double hValue, wValue; + if (d->image.height() > d->image.width()) + { + hValue = d->height->value(); + if (!hValue) hValue = 150*unitToMM(d->previousUnit); + wValue = (d->image.width() * hValue)/ d->image.height(); + } + else + { + wValue = d->width->value(); + if (!wValue) wValue = 150*unitToMM(d->previousUnit); + hValue = (d->image.height() * wValue)/ d->image.width(); + } + + d->width->blockSignals(true); + d->height->blockSignals(true); + + d->width->setValue(wValue); + d->height->setValue(hValue); + + d->width->blockSignals(false); + d->height->blockSignals(false); +} + +void ImageEditorPrintDialogPage::slotUnitChanged(const TQString& string) +{ + Unit newUnit = stringToUnit(string); + double ratio = unitToMM(d->previousUnit) / unitToMM(newUnit); + + d->width->blockSignals(true); + d->height->blockSignals(true); + + d->width->setValue( d->width->value() * ratio); + d->height->setValue( d->height->value() * ratio); + + d->width->blockSignals(false); + d->height->blockSignals(false); + + d->previousUnit = newUnit; +} + +void ImageEditorPrintDialogPage::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("Color Management"); + d->cmEnabled = config->readBoolEntry("EnableCM", false); +} + +void ImageEditorPrintDialogPage::slotSetupDlg() +{ + EditorWindow* editor = dynamic_cast<EditorWindow*>(d->parent); + editor->setup(true); +} + +void ImageEditorPrintDialogPage::slotAlertSettings( bool t) +{ + if (t && !d->cmEnabled) + { + TQString message = i18n("<p>Color Management is disabled.</p> \ + <p>You can enable it now by clicking on the \"Settings\" button.</p>"); + KMessageBox::information(this, message); + d->colorManaged->setChecked(!t); + } +} + +double ImageEditorPrintDialogPage::unitToMM(Unit unit) +{ + if (unit == DK_MILLIMETERS) + { + return 1.; + } + else if (unit == DK_CENTIMETERS) + { + return 10.; + } + else + { //DK_INCHES + return 25.4; + } +} + +ImageEditorPrintDialogPage::Unit ImageEditorPrintDialogPage::stringToUnit(const TQString& unit) +{ + if (unit == i18n("Millimeters")) + { + return DK_MILLIMETERS; + } + else if (unit == i18n("Centimeters")) + { + return DK_CENTIMETERS; + } + else + {//Inches + return DK_INCHES; + } +} + +TQString ImageEditorPrintDialogPage::unitToString(Unit unit) +{ + if (unit == DK_MILLIMETERS) + { + return i18n("Millimeters"); + } + else if (unit == DK_CENTIMETERS) + { + return i18n("Centimeters"); + } + else + { //DK_INCHES + return i18n("Inches"); + } +} + +} // namespace Digikam + diff --git a/src/utilities/imageeditor/tools/imageprint.h b/src/utilities/imageeditor/tools/imageprint.h new file mode 100644 index 00000000..66276701 --- /dev/null +++ b/src/utilities/imageeditor/tools/imageprint.h @@ -0,0 +1,122 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-13 + * Description : image editor printing interface. + * + * Copyright (C) 2006 by F.J. Cruz <fj.cruz@supercable.es> + * Copyright (C) 2004-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef IMAGEPRINT_H +#define IMAGEPRINT_H + +// TQt includes. + +#include <tqwidget.h> +#include <tqstring.h> + +// KDE includes. + +#include <kurl.h> +#include <kprinter.h> +#include <tdeprint/kprintdialogpage.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class ImagePrintPrivate; + +class DIGIKAM_EXPORT ImagePrint +{ +public: + + ImagePrint(DImg& image, KPrinter& printer, const TQString& fileName); + ~ImagePrint(); + + bool printImageWithTQt(); + +private: + + TQString minimizeString(TQString text, const TQFontMetrics& metrics, int maxWidth); + void readSettings(); + +private: + + KPrinter& m_printer; + + ImagePrintPrivate *d; +}; + +//----------------------------------------------------------------------------- + +class ImageEditorPrintDialogPagePrivate; + +class DIGIKAM_EXPORT ImageEditorPrintDialogPage : public KPrintDialogPage +{ + TQ_OBJECT + + +public: + + enum Unit + { + DK_MILLIMETERS = 1, + DK_CENTIMETERS, + DK_INCHES + }; + + static inline double unitToMM(Unit unit); + static inline Unit stringToUnit(const TQString& unit); + static inline TQString unitToString(Unit unit); + +public: + + ImageEditorPrintDialogPage(DImg& image, TQWidget *parent=0L, const char *name=0); + ~ImageEditorPrintDialogPage(); + + virtual void getOptions(TQMap<TQString,TQString>& opts, bool incldef = false); + virtual void setOptions(const TQMap<TQString,TQString>& opts); + +private slots: + + void toggleScaling( bool enable ); + void toggleRatio( bool enable ); + void slotUnitChanged(const TQString& string); + void slotHeightChanged(double value); + void slotWidthChanged(double value); + void slotSetupDlg(); + void slotAlertSettings(bool t); + +private: + + void readSettings(); + int getPosition(const TQString& align); + TQString setPosition(int align); + +private: + + ImageEditorPrintDialogPagePrivate *d; +}; + +} // namespace Digikam + +#endif // IMAGEPRINT_H diff --git a/src/utilities/imageeditor/tools/imageresize.cpp b/src/utilities/imageeditor/tools/imageresize.cpp new file mode 100644 index 00000000..c779b71e --- /dev/null +++ b/src/utilities/imageeditor/tools/imageresize.cpp @@ -0,0 +1,650 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-07 + * Description : a tool to resize an image + * + * Copyright (C) 2005-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// C++ includes. + +#include <cmath> + +// TQt includes. + +#include <tqvgroupbox.h> +#include <tqlabel.h> +#include <tqpushbutton.h> +#include <tqtooltip.h> +#include <tqwhatsthis.h> +#include <tqlayout.h> +#include <tqframe.h> +#include <tqcheckbox.h> +#include <tqcombobox.h> +#include <tqtabwidget.h> +#include <tqtimer.h> +#include <tqevent.h> +#include <tqpixmap.h> +#include <tqbrush.h> +#include <tqfile.h> +#include <tqimage.h> + +// KDE includes. + +#include <kseparator.h> +#include <kcursor.h> +#include <kurllabel.h> +#include <tdelocale.h> +#include <kiconloader.h> +#include <tdeapplication.h> +#include <tdefiledialog.h> +#include <kstandarddirs.h> +#include <kprogress.h> +#include <tdemessagebox.h> +#include <knuminput.h> +#include <tdeglobalsettings.h> + +// LibKDcraw includes. + +#include <libkdcraw/rnuminput.h> + +// Digikam includes. + +#include "dimg.h" +#include "ddebug.h" +#include "imageiface.h" +#include "dimgthreadedfilter.h" +#include "greycstorationiface.h" +#include "greycstorationwidget.h" +#include "greycstorationsettings.h" + +// Local includes. + +#include "imageresize.h" +#include "imageresize.moc" + +using namespace KDcrawIface; + +namespace Digikam +{ + +class ImageResizePriv +{ +public: + + enum RunningMode + { + NoneRendering=0, + FinalRendering + }; + + ImageResizePriv() + { + currentRenderingMode = NoneRendering; + parent = 0; + preserveRatioBox = 0; + useGreycstorationBox = 0; + mainTab = 0; + wInput = 0; + hInput = 0; + wpInput = 0; + hpInput = 0; + progressBar = 0; + greycstorationIface = 0; + settingsWidget = 0; + cimgLogoLabel = 0; + restorationTips = 0; + } + + int currentRenderingMode; + int orgWidth; + int orgHeight; + int prevW; + int prevH; + + double prevWP; + double prevHP; + + TQWidget *parent; + + TQLabel *restorationTips; + + TQCheckBox *preserveRatioBox; + TQCheckBox *useGreycstorationBox; + + TQTabWidget *mainTab; + + RIntNumInput *wInput; + RIntNumInput *hInput; + + RDoubleNumInput *wpInput; + RDoubleNumInput *hpInput; + + KProgress *progressBar; + + KURLLabel *cimgLogoLabel; + + GreycstorationIface *greycstorationIface; + GreycstorationWidget *settingsWidget; +}; + +ImageResize::ImageResize(TQWidget* parent) + : KDialogBase(Plain, i18n("Resize Image"), + Help|Default|User2|User3|Ok|Cancel, Ok, + parent, 0, true, false, + TQString(), + i18n("&Save As..."), + i18n("&Load...")) +{ + d = new ImageResizePriv; + d->parent = parent; + setHelp("resizetool.anchor", "digikam"); + TQString whatsThis; + setButtonWhatsThis( Default, i18n("<p>Reset all filter parameters to their default values.") ); + setButtonWhatsThis( User3, i18n("<p>Load all filter parameters from settings text file.") ); + setButtonWhatsThis( User2, i18n("<p>Save all filter parameters to settings text file.") ); + enableButton(Ok, false); + + ImageIface iface(0, 0); + d->orgWidth = iface.originalWidth(); + d->orgHeight = iface.originalHeight(); + d->prevW = d->orgWidth; + d->prevH = d->orgHeight; + d->prevWP = 100.0; + d->prevHP = 100.0; + + // ------------------------------------------------------------- + + TQVBoxLayout *vlay = new TQVBoxLayout(plainPage(), 0, spacingHint()); + d->mainTab = new TQTabWidget( plainPage() ); + + TQWidget* firstPage = new TQWidget( d->mainTab ); + TQGridLayout* grid = new TQGridLayout( firstPage, 8, 2, spacingHint()); + d->mainTab->addTab( firstPage, i18n("New Size") ); + + TQLabel *label1 = new TQLabel(i18n("Width:"), firstPage); + d->wInput = new RIntNumInput(firstPage); + d->wInput->setRange(1, TQMAX(d->orgWidth * 10, 9999), 1); + d->wInput->setName("d->wInput"); + d->wInput->setDefaultValue(d->orgWidth); + TQWhatsThis::add( d->wInput, i18n("<p>Set here the new image width in pixels.")); + + TQLabel *label2 = new TQLabel(i18n("Height:"), firstPage); + d->hInput = new RIntNumInput(firstPage); + d->hInput->setRange(1, TQMAX(d->orgHeight * 10, 9999), 1); + d->hInput->setName("d->hInput"); + d->hInput->setDefaultValue(d->orgHeight); + TQWhatsThis::add( d->hInput, i18n("<p>Set here the new image height in pixels.")); + + TQLabel *label3 = new TQLabel(i18n("Width (%):"), firstPage); + d->wpInput = new RDoubleNumInput(firstPage); + d->wpInput->setRange(1.0, 999.0, 1.0); + d->wpInput->setName("d->wpInput"); + d->wpInput->setDefaultValue(100.0); + TQWhatsThis::add( d->wpInput, i18n("<p>Set here the new image width in percent.")); + + TQLabel *label4 = new TQLabel(i18n("Height (%):"), firstPage); + d->hpInput = new RDoubleNumInput(firstPage); + d->hpInput->setRange(1.0, 999.0, 1.0); + d->hpInput->setName("d->hpInput"); + d->hpInput->setDefaultValue(100.0); + TQWhatsThis::add( d->hpInput, i18n("<p>Set here the new image height in percent.")); + + d->preserveRatioBox = new TQCheckBox(i18n("Maintain aspect ratio"), firstPage); + TQWhatsThis::add( d->preserveRatioBox, i18n("<p>Enable this option to maintain aspect " + "ratio with new image sizes.")); + + d->cimgLogoLabel = new KURLLabel(firstPage); + d->cimgLogoLabel->setText(TQString()); + d->cimgLogoLabel->setURL("http://cimg.sourceforge.net"); + TDEGlobal::dirs()->addResourceType("logo-cimg", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("logo-cimg", "logo-cimg.png"); + d->cimgLogoLabel->setPixmap( TQPixmap( directory + "logo-cimg.png" ) ); + TQToolTip::add(d->cimgLogoLabel, i18n("Visit CImg library website")); + + d->useGreycstorationBox = new TQCheckBox(i18n("Restore photograph"), firstPage); + TQWhatsThis::add( d->useGreycstorationBox, i18n("<p>Enable this option to restore photograph content. " + "This way is usefull to scale-up an image to an huge size. " + "Warning: this process can take a while.")); + + d->restorationTips = new TQLabel(i18n("<b>Note: use Restoration Mode to only scale-up an image to huge size. " + "Warning, this process can take a while.</b>"), firstPage); + + d->progressBar = new KProgress(100, firstPage); + d->progressBar->setValue(0); + TQWhatsThis::add(d->progressBar, i18n("<p>This shows the current progress when you use Restoration mode.")); + + grid->addMultiCellWidget(d->preserveRatioBox, 0, 0, 0, 2); + grid->addMultiCellWidget(label1, 1, 1, 0, 0); + grid->addMultiCellWidget(d->wInput, 1, 1, 1, 2); + grid->addMultiCellWidget(label2, 2, 2, 0, 0); + grid->addMultiCellWidget(d->hInput, 2, 2, 1, 2); + grid->addMultiCellWidget(label3, 3, 3, 0, 0); + grid->addMultiCellWidget(d->wpInput, 3, 3, 1, 2); + grid->addMultiCellWidget(label4, 4, 4, 0, 0); + grid->addMultiCellWidget(d->hpInput, 4, 4, 1, 2); + grid->addMultiCellWidget(new KSeparator(firstPage), 5, 5, 0, 2); + grid->addMultiCellWidget(d->cimgLogoLabel, 6, 8, 0, 0); + grid->addMultiCellWidget(d->useGreycstorationBox, 6, 6, 1, 2); + grid->addMultiCellWidget(d->restorationTips, 7, 7, 1, 2); + grid->addMultiCellWidget(d->progressBar, 8, 8, 1, 2); + grid->setRowStretch(8, 10); + + // ------------------------------------------------------------- + + d->settingsWidget = new GreycstorationWidget(d->mainTab); + vlay->addWidget(d->mainTab); + + // ------------------------------------------------------------- + + adjustSize(); + disableResize(); + TQTimer::singleShot(0, this, TQ_SLOT(readUserSettings())); + + // ------------------------------------------------------------- + + connect(d->cimgLogoLabel, TQ_SIGNAL(leftClickedURL(const TQString&)), + this, TQ_SLOT(processCImgURL(const TQString&))); + + connect(d->wInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotValuesChanged())); + + connect(d->hInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotValuesChanged())); + + connect(d->wpInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotValuesChanged())); + + connect(d->hpInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotValuesChanged())); + + connect(d->useGreycstorationBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotRestorationToggled(bool)) ); + + // ------------------------------------------------------------- + + Digikam::GreycstorationSettings defaults; + defaults.setResizeDefaultSettings(); + d->settingsWidget->setDefaultSettings(defaults); +} + +ImageResize::~ImageResize() +{ + if (d->greycstorationIface) + delete d->greycstorationIface; + + delete d; +} + +void ImageResize::slotRestorationToggled(bool b) +{ + d->settingsWidget->setEnabled(b); + d->progressBar->setEnabled(b); + d->cimgLogoLabel->setEnabled(b); + enableButton(User2, b); + enableButton(User3, b); +} + +void ImageResize::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("resize Tool Dialog"); + + GreycstorationSettings settings; + GreycstorationSettings defaults; + defaults.setResizeDefaultSettings(); + + settings.fastApprox = config->readBoolEntry("FastApprox", defaults.fastApprox); + settings.interp = config->readNumEntry("Interpolation", defaults.interp); + settings.amplitude = config->readDoubleNumEntry("Amplitude", defaults.amplitude); + settings.sharpness = config->readDoubleNumEntry("Sharpness", defaults.sharpness); + settings.anisotropy = config->readDoubleNumEntry("Anisotropy", defaults.anisotropy); + settings.alpha = config->readDoubleNumEntry("Alpha", defaults.alpha); + settings.sigma = config->readDoubleNumEntry("Sigma", defaults.sigma); + settings.gaussPrec = config->readDoubleNumEntry("GaussPrec", defaults.gaussPrec); + settings.dl = config->readDoubleNumEntry("Dl", defaults.dl); + settings.da = config->readDoubleNumEntry("Da", defaults.da); + settings.nbIter = config->readNumEntry("Iteration", defaults.nbIter); + settings.tile = config->readNumEntry("Tile", defaults.tile); + settings.btile = config->readNumEntry("BTile", defaults.btile); + d->settingsWidget->setSettings(settings); + + d->useGreycstorationBox->setChecked(config->readBoolEntry("RestorePhotograph", false)); + slotRestorationToggled(d->useGreycstorationBox->isChecked()); + + d->preserveRatioBox->blockSignals(true); + d->wInput->blockSignals(true); + d->hInput->blockSignals(true); + d->wpInput->blockSignals(true); + d->hpInput->blockSignals(true); + + d->preserveRatioBox->setChecked(true); + d->wInput->slotReset(); + d->hInput->slotReset(); + d->wpInput->slotReset(); + d->hpInput->slotReset(); + + d->preserveRatioBox->blockSignals(false); + d->wInput->blockSignals(false); + d->hInput->blockSignals(false); + d->wpInput->blockSignals(false); + d->hpInput->blockSignals(false); +} + +void ImageResize::writeUserSettings() +{ + GreycstorationSettings settings = d->settingsWidget->getSettings(); + TDEConfig* config = kapp->config(); + config->setGroup("resize Tool Dialog"); + config->writeEntry("FastApprox", settings.fastApprox); + config->writeEntry("Interpolation", settings.interp); + config->writeEntry("Amplitude", settings.amplitude); + config->writeEntry("Sharpness", settings.sharpness); + config->writeEntry("Anisotropy", settings.anisotropy); + config->writeEntry("Alpha", settings.alpha); + config->writeEntry("Sigma", settings.sigma); + config->writeEntry("GaussPrec", settings.gaussPrec); + config->writeEntry("Dl", settings.dl); + config->writeEntry("Da", settings.da); + config->writeEntry("Iteration", settings.nbIter); + config->writeEntry("Tile", settings.tile); + config->writeEntry("BTile", settings.btile); + config->writeEntry("RestorePhotograph", d->useGreycstorationBox->isChecked()); + config->sync(); +} + +void ImageResize::slotDefault() +{ + GreycstorationSettings settings; + settings.setResizeDefaultSettings(); + + d->settingsWidget->setSettings(settings); + d->useGreycstorationBox->setChecked(false); + slotRestorationToggled(d->useGreycstorationBox->isChecked()); + + d->preserveRatioBox->blockSignals(true); + d->wInput->blockSignals(true); + d->hInput->blockSignals(true); + d->wpInput->blockSignals(true); + d->hpInput->blockSignals(true); + d->preserveRatioBox->setChecked(true); + d->wInput->setValue(d->orgWidth); + d->hInput->setValue(d->orgHeight); + d->wpInput->setValue(100.0); + d->hpInput->setValue(100.0); + d->preserveRatioBox->blockSignals(false); + d->wInput->blockSignals(false); + d->hInput->blockSignals(false); + d->wpInput->blockSignals(false); + d->hpInput->blockSignals(false); +} + +void ImageResize::slotValuesChanged() +{ + enableButton(Ok, true); + d->wInput->blockSignals(true); + d->hInput->blockSignals(true); + d->wpInput->blockSignals(true); + d->hpInput->blockSignals(true); + + TQString s(sender()->name()); + + if (s == "d->wInput") + { + double val = d->wInput->value(); + double wp = val/(double)(d->orgWidth) * 100.0; + d->wpInput->setValue(wp); + + if (d->preserveRatioBox->isChecked()) + { + d->hpInput->setValue(wp); + int h = (int)(wp*d->orgHeight/100); + d->hInput->setValue(h); + } + } + else if (s == "d->hInput") + { + double val = d->hInput->value(); + double hp = val/(double)(d->orgHeight) * 100.0; + d->hpInput->setValue(hp); + + if (d->preserveRatioBox->isChecked()) + { + d->wpInput->setValue(hp); + int w = (int)(hp*d->orgWidth/100); + d->wInput->setValue(w); + } + } + else if (s == "d->wpInput") + { + double val = d->wpInput->value(); + int w = (int)(val*d->orgWidth/100); + d->wInput->setValue(w); + + if (d->preserveRatioBox->isChecked()) + { + d->hpInput->setValue(val); + int h = (int)(val*d->orgHeight/100); + d->hInput->setValue(h); + } + } + else if (s == "d->hpInput") + { + double val = d->hpInput->value(); + int h = (int)(val*d->orgHeight/100); + d->hInput->setValue(h); + + if (d->preserveRatioBox->isChecked()) + { + d->wpInput->setValue(val); + int w = (int)(val*d->orgWidth/100); + d->wInput->setValue(w); + } + } + + d->prevW = d->wInput->value(); + d->prevH = d->hInput->value(); + d->prevWP = d->wpInput->value(); + d->prevHP = d->hpInput->value(); + + d->wInput->blockSignals(false); + d->hInput->blockSignals(false); + d->wpInput->blockSignals(false); + d->hpInput->blockSignals(false); +} + +void ImageResize::slotCancel() +{ + if (d->currentRenderingMode != ImageResizePriv::NoneRendering) + { + d->greycstorationIface->stopComputation(); + d->parent->unsetCursor(); + } + + done(Cancel); +} + +void ImageResize::processCImgURL(const TQString& url) +{ + TDEApplication::kApplication()->invokeBrowser(url); +} + +void ImageResize::closeEvent(TQCloseEvent *e) +{ + if (d->currentRenderingMode != ImageResizePriv::NoneRendering) + { + d->greycstorationIface->stopComputation(); + d->parent->unsetCursor(); + } + + e->accept(); +} + +void ImageResize::slotOk() +{ + if (d->prevW != d->wInput->value() || d->prevH != d->hInput->value() || + d->prevWP != d->wpInput->value() || d->prevHP != d->hpInput->value()) + slotValuesChanged(); + + d->currentRenderingMode = ImageResizePriv::FinalRendering; + d->mainTab->setCurrentPage(0); + d->settingsWidget->setEnabled(false); + d->preserveRatioBox->setEnabled(false); + d->useGreycstorationBox->setEnabled(false); + d->wInput->setEnabled(false); + d->hInput->setEnabled(false); + d->wpInput->setEnabled(false); + d->hpInput->setEnabled(false); + enableButton(Ok, false); + enableButton(Default, false); + enableButton(User2, false); + enableButton(User3, false); + + d->parent->setCursor( KCursor::waitCursor() ); + writeUserSettings(); + ImageIface iface(0, 0); + uchar *data = iface.getOriginalImage(); + DImg image = DImg(iface.originalWidth(), iface.originalHeight(), + iface.originalSixteenBit(), iface.originalHasAlpha(), data); + delete [] data; + + if (d->useGreycstorationBox->isChecked()) + { + d->progressBar->setValue(0); + d->progressBar->setEnabled(true); + + if (d->greycstorationIface) + { + delete d->greycstorationIface; + d->greycstorationIface = 0; + } + + d->greycstorationIface = new GreycstorationIface( + &image, d->settingsWidget->getSettings(), + GreycstorationIface::Resize, + d->wInput->value(), + d->hInput->value(), + 0, this); + } + else + { + // See B.K.O #152192: CImg resize() sound like bugous or unadapted + // to resize image without good quality. + + image.resize(d->wInput->value(), d->hInput->value()); + iface.putOriginalImage(i18n("Resize"), image.bits(), + image.width(), image.height()); + d->parent->unsetCursor(); + accept(); + } +} + +void ImageResize::customEvent(TQCustomEvent *event) +{ + if (!event) return; + + GreycstorationIface::EventData *data = (GreycstorationIface::EventData*) event->data(); + + if (!data) return; + + if (data->starting) // Computation in progress ! + { + d->progressBar->setValue(data->progress); + } + else + { + if (data->success) // Computation Completed ! + { + switch (d->currentRenderingMode) + { + case ImageResizePriv::FinalRendering: + { + DDebug() << "Final resizing completed..." << endl; + + ImageIface iface(0, 0); + DImg resizedImage = d->greycstorationIface->getTargetImage(); + + iface.putOriginalImage(i18n("Resize"), resizedImage.bits(), + resizedImage.width(), resizedImage.height()); + d->parent->unsetCursor(); + accept(); + break; + } + } + } + else // Computation Failed ! + { + switch (d->currentRenderingMode) + { + case ImageResizePriv::FinalRendering: + break; + } + } + } + + delete data; +} + +void ImageResize::slotUser3() +{ + KURL loadBlowupFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Photograph Resizing Settings File to Load")) ); + if( loadBlowupFile.isEmpty() ) + return; + + TQFile file(loadBlowupFile.path()); + + if ( file.open(IO_ReadOnly) ) + { + if (!d->settingsWidget->loadSettings(file, TQString("# Photograph Resizing Configuration File"))) + { + KMessageBox::error(this, + i18n("\"%1\" is not a Photograph Resizing settings text file.") + .arg(loadBlowupFile.fileName())); + file.close(); + return; + } + } + else + KMessageBox::error(this, i18n("Cannot load settings from the Photograph Resizing text file.")); + + file.close(); +} + +void ImageResize::slotUser2() +{ + KURL saveBlowupFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Photograph Resizing Settings File to Save")) ); + if( saveBlowupFile.isEmpty() ) + return; + + TQFile file(saveBlowupFile.path()); + + if ( file.open(IO_WriteOnly) ) + d->settingsWidget->saveSettings(file, TQString("# Photograph Resizing Configuration File")); + else + KMessageBox::error(this, i18n("Cannot save settings to the Photograph Resizing text file.")); + + file.close(); +} + +} // NameSpace Digikam + diff --git a/src/utilities/imageeditor/tools/imageresize.h b/src/utilities/imageeditor/tools/imageresize.h new file mode 100644 index 00000000..3f8b1c23 --- /dev/null +++ b/src/utilities/imageeditor/tools/imageresize.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-07 + * Description : a tool to resize an image + * + * Copyright (C) 2005-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef IMAGE_RESIZE_H +#define IMAGE_RESIZE_H + +// TQt includes. + +#include <tqstring.h> + +// KDE includes. + +#include <kdialogbase.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class ImageResizePriv; + +class DIGIKAM_EXPORT ImageResize : public KDialogBase +{ + TQ_OBJECT + + +public: + + ImageResize(TQWidget* parent); + ~ImageResize(); + +protected: + + void closeEvent(TQCloseEvent *e); + +private: + + void customEvent(TQCustomEvent *event); + void writeUserSettings(); + +private slots: + + void slotOk(); + void slotCancel(); + void slotDefault(); + void slotUser2(); + void slotUser3(); + void processCImgURL(const TQString&); + void slotValuesChanged(); + void readUserSettings(); + void slotRestorationToggled(bool); + +private: + + ImageResizePriv *d; +}; + +} // NameSpace Digikam + +#endif /* IMAGE_RESIZE_H */ diff --git a/src/utilities/lighttable/Makefile.am b/src/utilities/lighttable/Makefile.am new file mode 100644 index 00000000..df38b5f9 --- /dev/null +++ b/src/utilities/lighttable/Makefile.am @@ -0,0 +1,28 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/digikam \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/thumbbar \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/themeengine \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/libs/imageproperties \ + -I$(top_srcdir)/src/libs/threadimageio \ + -I$(top_srcdir)/src/utilities/setup \ + -I$(top_srcdir)/src/utilities/slideshow \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_builddir)/src/libs/dialogs \ + $(LIBKEXIV2_CFLAGS) $(LIBKDCRAW_CFLAGS) $(all_includes) + +noinst_LTLIBRARIES = liblighttable.la + +liblighttable_la_SOURCES = lighttablebar.cpp lighttablewindow.cpp lighttablepreview.cpp \ + lighttableview.cpp + +liblighttable_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +rcdir = $(kde_datadir)/digikam +rc_DATA = lighttablewindowui.rc diff --git a/src/utilities/lighttable/lighttablebar.cpp b/src/utilities/lighttable/lighttablebar.cpp new file mode 100644 index 00000000..cfb935ab --- /dev/null +++ b/src/utilities/lighttable/lighttablebar.cpp @@ -0,0 +1,881 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-04-11 + * Description : light table thumbs bar + * + * Copyright (C) 2007-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqpixmap.h> +#include <tqpainter.h> +#include <tqimage.h> +#include <tqcursor.h> + +// KDE includes. + +#include <tdeglobal.h> +#include <kiconloader.h> +#include <tdelocale.h> +#include <tdepopupmenu.h> +#include <kstandarddirs.h> + +// Local includes. + +#include "ddebug.h" +#include "album.h" +#include "albumdb.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "dragobjects.h" +#include "imageattributeswatch.h" +#include "metadatahub.h" +#include "ratingpopupmenu.h" +#include "themeengine.h" +#include "lighttablebar.h" +#include "lighttablebar.moc" + +namespace Digikam +{ + +class LightTableBarPriv +{ + +public: + + LightTableBarPriv() + { + navigateByPair = false; + toolTip = 0; + } + + bool navigateByPair; + + TQPixmap ratingPixmap; + + LightTableBarToolTip *toolTip; +}; + +class LightTableBarItemPriv +{ + +public: + + LightTableBarItemPriv() + { + onLeftPanel = false; + onRightPanel = false; + info = 0; + } + + bool onLeftPanel; + bool onRightPanel; + + ImageInfo *info; +}; + +LightTableBar::LightTableBar(TQWidget* parent, int orientation, bool exifRotate) + : ThumbBarView(parent, orientation, exifRotate) +{ + d = new LightTableBarPriv; + setMouseTracking(true); + readToolTipSettings(); + d->toolTip = new LightTableBarToolTip(this); + + // -- Load rating Pixmap ------------------------------------------ + + TDEGlobal::dirs()->addResourceType("digikam_rating", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + TQString ratingPixPath = TDEGlobal::dirs()->findResourceDir("digikam_rating", "rating.png"); + ratingPixPath += "/rating.png"; + d->ratingPixmap = TQPixmap(ratingPixPath); + + TQPainter painter(&d->ratingPixmap); + painter.fillRect(0, 0, d->ratingPixmap.width(), d->ratingPixmap.height(), + ThemeEngine::instance()->textSpecialRegColor()); + painter.end(); + + if (orientation ==TQt::Vertical) + setMinimumWidth(d->ratingPixmap.width()*5 + 6 + 2*getMargin()); + else + setMinimumHeight(d->ratingPixmap.width()*5 + 6 + 2*getMargin()); + + // ---------------------------------------------------------------- + + ImageAttributesWatch *watch = ImageAttributesWatch::instance(); + + connect(watch, TQ_SIGNAL(signalImageRatingChanged(TQ_LLONG)), + this, TQ_SLOT(slotImageRatingChanged(TQ_LLONG))); + + connect(ThemeEngine::instance(), TQ_SIGNAL(signalThemeChanged()), + this, TQ_SLOT(slotThemeChanged())); + + connect(this, TQ_SIGNAL(signalItemSelected(ThumbBarItem*)), + this, TQ_SLOT(slotItemSelected(ThumbBarItem*))); +} + +LightTableBar::~LightTableBar() +{ + delete d->toolTip; + delete d; +} + +void LightTableBar::setNavigateByPair(bool b) +{ + d->navigateByPair = b; +} + +void LightTableBar::slotImageRatingChanged(TQ_LLONG imageId) +{ + for (ThumbBarItem *item = firstItem(); item; item = item->next()) + { + LightTableBarItem *ltItem = dynamic_cast<LightTableBarItem*>(item); + if (ltItem->info()->id() == imageId) + { + triggerUpdate(); + return; + } + } +} + +void LightTableBar::contentsMouseReleaseEvent(TQMouseEvent *e) +{ + if (!e) return; + + ThumbBarView::contentsMouseReleaseEvent(e); + + TQPoint pos = TQCursor::pos(); + LightTableBarItem *item = findItemByPos(e->pos()); + + RatingPopupMenu *ratingMenu = 0; + + if (e->button() == TQt::RightButton) + { + TDEPopupMenu popmenu(this); + + if (item) + { + popmenu.insertItem(SmallIcon("go-previous"), i18n("Show on left panel"), 10); + popmenu.insertItem(SmallIcon("go-next"), i18n("Show on right panel"), 11); + popmenu.insertItem(SmallIcon("editimage"), i18n("Edit"), 12); + + if (d->navigateByPair) + { + popmenu.setItemEnabled(10, false); + popmenu.setItemEnabled(11, false); + } + + popmenu.insertSeparator(); + popmenu.insertItem(SmallIcon("window-close"), i18n("Remove item"), 13); + } + + int totalItems = itemsURLs().count(); + popmenu.insertItem(SmallIcon("editshred"), i18n("Clear all"), 14); + popmenu.setItemEnabled(14, totalItems ? true : false); + + if (item) + { + popmenu.insertSeparator(); + + // Assign Star Rating ------------------------------------------- + + ratingMenu = new RatingPopupMenu(); + + connect(ratingMenu, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotAssignRating(int))); + + popmenu.insertItem(i18n("Assign Rating"), ratingMenu); + } + + switch(popmenu.exec(pos)) + { + case 10: // Left panel + { + emit signalSetItemOnLeftPanel(item->info()); + break; + } + case 11: // Right panel + { + emit signalSetItemOnRightPanel(item->info()); + break; + } + case 12: // Edit + { + emit signalEditItem(item->info()); + break; + } + case 13: // Remove + { + emit signalRemoveItem(item->info()); + break; + } + case 14: // Clear All + { + emit signalClearAll(); + break; + } + default: + break; + } + } + + delete ratingMenu; +} + +void LightTableBar::slotAssignRating(int rating) +{ + rating = TQMIN(5, TQMAX(0, rating)); + ImageInfo *info = currentItemImageInfo(); + if (info) + { + MetadataHub hub; + hub.load(info); + hub.setRating(rating); + hub.write(info, MetadataHub::PartialWrite); + hub.write(info->filePath(), MetadataHub::FullWriteIfChanged); + } +} + +void LightTableBar::slotAssignRatingNoStar() +{ + slotAssignRating(0); +} + +void LightTableBar::slotAssignRatingOneStar() +{ + slotAssignRating(1); +} + +void LightTableBar::slotAssignRatingTwoStar() +{ + slotAssignRating(2); +} + +void LightTableBar::slotAssignRatingThreeStar() +{ + slotAssignRating(3); +} + +void LightTableBar::slotAssignRatingFourStar() +{ + slotAssignRating(4); +} + +void LightTableBar::slotAssignRatingFiveStar() +{ + slotAssignRating(5); +} + +void LightTableBar::setOnLeftPanel(const ImageInfo* info) +{ + for (ThumbBarItem *item = firstItem(); item; item = item->next()) + { + LightTableBarItem *ltItem = dynamic_cast<LightTableBarItem*>(item); + if (ltItem) + { + if (info) + { + if (ltItem->info()->id() == info->id()) + { + ltItem->setOnLeftPanel(true); + repaintItem(item); + } + else if (ltItem->isOnLeftPanel() == true) + { + ltItem->setOnLeftPanel(false); + repaintItem(item); + } + } + else if (ltItem->isOnLeftPanel() == true) + { + ltItem->setOnLeftPanel(false); + repaintItem(item); + } + } + } +} + +void LightTableBar::setOnRightPanel(const ImageInfo* info) +{ + for (ThumbBarItem *item = firstItem(); item; item = item->next()) + { + LightTableBarItem *ltItem = dynamic_cast<LightTableBarItem*>(item); + if (ltItem) + { + if (info) + { + if (ltItem->info()->id() == info->id()) + { + ltItem->setOnRightPanel(true); + repaintItem(item); + } + else if (ltItem->isOnRightPanel() == true) + { + ltItem->setOnRightPanel(false); + repaintItem(item); + } + } + else if (ltItem->isOnRightPanel() == true) + { + ltItem->setOnRightPanel(false); + repaintItem(item); + } + } + } +} + +void LightTableBar::slotItemSelected(ThumbBarItem* item) +{ + if (item) + { + LightTableBarItem *ltItem = dynamic_cast<LightTableBarItem*>(item); + if (ltItem) + { + emit signalLightTableBarItemSelected(ltItem->info()); + return; + } + } + + emit signalLightTableBarItemSelected(0); +} + +ImageInfo* LightTableBar::currentItemImageInfo() const +{ + if (currentItem()) + { + LightTableBarItem *item = dynamic_cast<LightTableBarItem*>(currentItem()); + return item->info(); + } + + return 0; +} + +ImageInfoList LightTableBar::itemsImageInfoList() +{ + ImageInfoList list; + + for (ThumbBarItem *item = firstItem(); item; item = item->next()) + { + LightTableBarItem *ltItem = dynamic_cast<LightTableBarItem*>(item); + if (ltItem) + { + ImageInfo *info = new ImageInfo(*(ltItem->info())); + list.append(info); + } + } + + return list; +} + +void LightTableBar::setSelectedItem(LightTableBarItem* ltItem) +{ + ThumbBarItem *item = static_cast<ThumbBarItem*>(ltItem); + if (item) ThumbBarView::setSelected(item); +} + +void LightTableBar::removeItem(const ImageInfo* info) +{ + if (!info) return; + + LightTableBarItem* ltItem = findItemByInfo(info); + ThumbBarItem *item = static_cast<ThumbBarItem*>(ltItem); + if (item) ThumbBarView::removeItem(item); +} + +LightTableBarItem* LightTableBar::findItemByInfo(const ImageInfo* info) const +{ + if (info) + { + for (ThumbBarItem *item = firstItem(); item; item = item->next()) + { + LightTableBarItem *ltItem = dynamic_cast<LightTableBarItem*>(item); + if (ltItem) + { + if (ltItem->info()->id() == info->id()) + return ltItem; + } + } + } + return 0; +} + +LightTableBarItem* LightTableBar::findItemByPos(const TQPoint& pos) const +{ + ThumbBarItem *item = findItem(pos); + if (item) + { + LightTableBarItem *ltItem = dynamic_cast<LightTableBarItem*>(item); + return ltItem; + } + + return 0; +} + +void LightTableBar::readToolTipSettings() +{ + AlbumSettings* albumSettings = AlbumSettings::instance(); + if (!albumSettings) return; + + Digikam::ThumbBarToolTipSettings settings; + settings.showToolTips = albumSettings->getShowToolTips(); + settings.showFileName = albumSettings->getToolTipsShowFileName(); + settings.showFileDate = albumSettings->getToolTipsShowFileDate(); + settings.showFileSize = albumSettings->getToolTipsShowFileSize(); + settings.showImageType = albumSettings->getToolTipsShowImageType(); + settings.showImageDim = albumSettings->getToolTipsShowImageDim(); + settings.showPhotoMake = albumSettings->getToolTipsShowPhotoMake(); + settings.showPhotoDate = albumSettings->getToolTipsShowPhotoDate(); + settings.showPhotoFocal = albumSettings->getToolTipsShowPhotoFocal(); + settings.showPhotoExpo = albumSettings->getToolTipsShowPhotoExpo(); + settings.showPhotoMode = albumSettings->getToolTipsShowPhotoMode(); + settings.showPhotoFlash = albumSettings->getToolTipsShowPhotoFlash(); + settings.showPhotoWB = albumSettings->getToolTipsShowPhotoWB(); + setToolTipSettings(settings); +} + +void LightTableBar::viewportPaintEvent(TQPaintEvent* e) +{ + ThemeEngine* te = ThemeEngine::instance(); + TQRect er(e->rect()); + TQPixmap bgPix; + + if (countItems() > 0) + { + int cy, cx, ts, y1, y2, x1, x2; + TQPixmap tile; + + if (getOrientation() ==TQt::Vertical) + { + cy = viewportToContents(er.topLeft()).y(); + + bgPix.resize(contentsRect().width(), er.height()); + + ts = getTileSize() + 2*getMargin(); + tile.resize(visibleWidth(), ts); + + y1 = (cy/ts)*ts; + y2 = ((y1 + er.height())/ts +1)*ts; + } + else + { + cx = viewportToContents(er.topLeft()).x(); + + bgPix.resize(er.width(), contentsRect().height()); + + ts = getTileSize() + 2*getMargin(); + tile.resize(ts, visibleHeight()); + + x1 = (cx/ts)*ts; + x2 = ((x1 + er.width())/ts +1)*ts; + } + + bgPix.fill(te->baseColor()); + + for (ThumbBarItem *item = firstItem(); item; item = item->next()) + { + if (getOrientation() ==TQt::Vertical) + { + if (y1 <= item->position() && item->position() <= y2) + { + if (item == currentItem()) + tile = te->thumbSelPixmap(tile.width(), tile.height()); + else + tile = te->thumbRegPixmap(tile.width(), tile.height()); + + TQPainter p(&tile); + if (item == currentItem()) + { + p.setPen(TQPen(te->textSelColor(), 3)); + p.drawRect(2, 2, tile.width()-2, tile.height()-2); + } + else + { + p.setPen(TQPen(te->textRegColor(), 1)); + p.drawRect(0, 0, tile.width(), tile.height()); + } + p.end(); + + if (item->pixmap()) + { + TQPixmap pix; + pix.convertFromImage(TQImage(item->pixmap()->convertToImage()). + smoothScale(getTileSize(), getTileSize(), TQImage::ScaleMin)); + int x = (tile.width() - pix.width())/2; + int y = (tile.height() - pix.height())/2; + bitBlt(&tile, x, y, &pix); + + LightTableBarItem *ltItem = dynamic_cast<LightTableBarItem*>(item); + + if (ltItem->isOnLeftPanel()) + { + TQPixmap lPix = SmallIcon("go-previous"); + bitBlt(&tile, getMargin(), getMargin(), &lPix); + } + if (ltItem->isOnRightPanel()) + { + TQPixmap rPix = SmallIcon("go-next"); + bitBlt(&tile, tile.width() - getMargin() - rPix.width(), getMargin(), &rPix); + } + + TQRect r(0, tile.height()-getMargin()-d->ratingPixmap.height(), + tile.width(), d->ratingPixmap.height()); + int rating = ltItem->info()->rating(); + int xr = (r.width() - rating * d->ratingPixmap.width())/2; + int wr = rating * d->ratingPixmap.width(); + TQPainter p(&tile); + p.drawTiledPixmap(xr, r.y(), wr, r.height(), d->ratingPixmap); + } + + bitBlt(&bgPix, 0, item->position() - cy, &tile); + } + } + else + { + if (x1 <= item->position() && item->position() <= x2) + { + if (item == currentItem()) + tile = te->thumbSelPixmap(tile.width(), tile.height()); + else + tile = te->thumbRegPixmap(tile.width(), tile.height()); + + TQPainter p(&tile); + if (item == currentItem()) + { + p.setPen(TQPen(te->textSelColor(), 2)); + p.drawRect(1, 1, tile.width()-1, tile.height()-1); + } + else + { + p.setPen(TQPen(te->textRegColor(), 1)); + p.drawRect(0, 0, tile.width(), tile.height()); + } + p.end(); + + if (item->pixmap()) + { + TQPixmap pix; + pix.convertFromImage(TQImage(item->pixmap()->convertToImage()). + smoothScale(getTileSize(), getTileSize(), TQImage::ScaleMin)); + int x = (tile.width() - pix.width())/2; + int y = (tile.height()- pix.height())/2; + bitBlt(&tile, x, y, &pix); + + LightTableBarItem *ltItem = dynamic_cast<LightTableBarItem*>(item); + + if (ltItem->isOnLeftPanel()) + { + TQPixmap lPix = SmallIcon("go-previous"); + bitBlt(&tile, getMargin(), getMargin(), &lPix); + } + if (ltItem->isOnRightPanel()) + { + TQPixmap rPix = SmallIcon("go-next"); + bitBlt(&tile, tile.width() - getMargin() - rPix.width(), getMargin(), &rPix); + } + + TQRect r(0, tile.height()-getMargin()-d->ratingPixmap.height(), + tile.width(), d->ratingPixmap.height()); + int rating = ltItem->info()->rating(); + int xr = (r.width() - rating * d->ratingPixmap.width())/2; + int wr = rating * d->ratingPixmap.width(); + TQPainter p(&tile); + p.drawTiledPixmap(xr, r.y(), wr, r.height(), d->ratingPixmap); + } + + bitBlt(&bgPix, item->position() - cx, 0, &tile); + } + } + } + + if (getOrientation() ==TQt::Vertical) + bitBlt(viewport(), 0, er.y(), &bgPix); + else + bitBlt(viewport(), er.x(), 0, &bgPix); + } + else + { + bgPix.resize(contentsRect().width(), contentsRect().height()); + bgPix.fill(te->baseColor()); + TQPainter p(&bgPix); + p.setPen(TQPen(te->textRegColor())); + p.drawText(0, 0, bgPix.width(), bgPix.height(), + TQt::AlignCenter|TQt::WordBreak, + i18n("Drag and drop images here")); + p.end(); + bitBlt(viewport(), 0, 0, &bgPix); + } +} + +void LightTableBar::startDrag() +{ + if (!currentItem()) return; + + KURL::List urls; + KURL::List kioURLs; + TQValueList<int> albumIDs; + TQValueList<int> imageIDs; + + LightTableBarItem *item = dynamic_cast<LightTableBarItem*>(currentItem()); + + urls.append(item->info()->kurl()); + kioURLs.append(item->info()->kurlForKIO()); + imageIDs.append(item->info()->id()); + albumIDs.append(item->info()->albumID()); + + TQPixmap icon(DesktopIcon("image-x-generic", 48)); + int w = icon.width(); + int h = icon.height(); + + TQPixmap pix(w+4,h+4); + TQPainter p(&pix); + p.fillRect(0, 0, w+4, h+4, TQColor(TQt::white)); + p.setPen(TQPen(TQt::black, 1)); + p.drawRect(0, 0, w+4, h+4); + p.drawPixmap(2, 2, icon); + p.end(); + + TQDragObject* drag = 0; + + drag = new ItemDrag(urls, kioURLs, albumIDs, imageIDs, this); + if (drag) + { + drag->setPixmap(pix); + drag->drag(); + } +} + +void LightTableBar::contentsDragMoveEvent(TQDragMoveEvent *e) +{ + int albumID; + TQValueList<int> albumIDs; + TQValueList<int> imageIDs; + KURL::List urls; + KURL::List kioURLs; + + if (ItemDrag::decode(e, urls, kioURLs, albumIDs, imageIDs) || + AlbumDrag::decode(e, urls, albumID) || + TagDrag::canDecode(e)) + { + e->accept(); + return; + } + + e->ignore(); +} + +void LightTableBar::contentsDropEvent(TQDropEvent *e) +{ + int albumID; + TQValueList<int> albumIDs; + TQValueList<int> imageIDs; + KURL::List urls; + KURL::List kioURLs; + + if (ItemDrag::decode(e, urls, kioURLs, albumIDs, imageIDs)) + { + ImageInfoList imageInfoList; + + for (TQValueList<int>::const_iterator it = imageIDs.begin(); + it != imageIDs.end(); ++it) + { + ImageInfo *info = new ImageInfo(*it); + if (!findItemByInfo(info)) + { + imageInfoList.append(info); + } + else + { + delete info; + } + } + + emit signalDroppedItems(imageInfoList); + e->accept(); + } + else if (AlbumDrag::decode(e, urls, albumID)) + { + TQValueList<TQ_LLONG> itemIDs = AlbumManager::instance()->albumDB()->getItemIDsInAlbum(albumID); + ImageInfoList imageInfoList; + + for (TQValueList<TQ_LLONG>::const_iterator it = itemIDs.begin(); + it != itemIDs.end(); ++it) + { + ImageInfo *info = new ImageInfo(*it); + if (!findItemByInfo(info)) + { + imageInfoList.append(info); + } + else + { + delete info; + } + } + + emit signalDroppedItems(imageInfoList); + e->accept(); + } + else if(TagDrag::canDecode(e)) + { + TQByteArray ba = e->encodedData("digikam/tag-id"); + TQDataStream ds(ba, IO_ReadOnly); + int tagID; + ds >> tagID; + + AlbumManager* man = AlbumManager::instance(); + TQValueList<TQ_LLONG> itemIDs = man->albumDB()->getItemIDsInTag(tagID, true); + ImageInfoList imageInfoList; + + for (TQValueList<TQ_LLONG>::const_iterator it = itemIDs.begin(); + it != itemIDs.end(); ++it) + { + ImageInfo *info = new ImageInfo(*it); + if (!findItemByInfo(info)) + { + imageInfoList.append(info); + } + else + { + delete info; + } + } + + emit signalDroppedItems(imageInfoList); + e->accept(); + } + else + { + e->ignore(); + } +} + +void LightTableBar::slotThemeChanged() +{ + TDEGlobal::dirs()->addResourceType("digikam_rating", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + TQString ratingPixPath = TDEGlobal::dirs()->findResourceDir("digikam_rating", "rating.png"); + ratingPixPath += "/rating.png"; + d->ratingPixmap = TQPixmap(ratingPixPath); + + TQPainter painter(&d->ratingPixmap); + painter.fillRect(0, 0, d->ratingPixmap.width(), d->ratingPixmap.height(), + ThemeEngine::instance()->textSpecialRegColor()); + painter.end(); + + slotUpdate(); +} + +// ------------------------------------------------------------------------- + +LightTableBarItem::LightTableBarItem(LightTableBar *view, ImageInfo *info) + : ThumbBarItem(view, info->kurl()) +{ + d = new LightTableBarItemPriv; + d->info = info; +} + +LightTableBarItem::~LightTableBarItem() +{ + delete d; +} + +ImageInfo* LightTableBarItem::info() const +{ + return d->info; +} + +void LightTableBarItem::setOnLeftPanel(bool on) +{ + d->onLeftPanel = on; +} + +void LightTableBarItem::setOnRightPanel(bool on) +{ + d->onRightPanel = on; +} + +bool LightTableBarItem::isOnLeftPanel() const +{ + return d->onLeftPanel; +} + +bool LightTableBarItem::isOnRightPanel() const +{ + return d->onRightPanel; +} + +// ------------------------------------------------------------------------- + +LightTableBarToolTip::LightTableBarToolTip(ThumbBarView *parent) + : ThumbBarToolTip(parent) +{ +} + +TQString LightTableBarToolTip::tipContentExtraData(ThumbBarItem *item) +{ + TQString tip, str; + AlbumSettings* settings = AlbumSettings::instance(); + ImageInfo* info = static_cast<LightTableBarItem *>(item)->info(); + + if (settings) + { + if (settings->getToolTipsShowAlbumName() || + settings->getToolTipsShowComments() || + settings->getToolTipsShowTags() || + settings->getToolTipsShowRating()) + { + tip += m_headBeg + i18n("digiKam Properties") + m_headEnd; + + if (settings->getToolTipsShowAlbumName()) + { + PAlbum* album = info->album(); + if (album) + tip += m_cellSpecBeg + i18n("Album:") + m_cellSpecMid + + album->url().remove(0, 1) + m_cellSpecEnd; + } + + if (settings->getToolTipsShowComments()) + { + str = info->caption(); + if (str.isEmpty()) str = TQString("---"); + tip += m_cellSpecBeg + i18n("Caption:") + m_cellSpecMid + breakString(str) + m_cellSpecEnd; + } + + if (settings->getToolTipsShowTags()) + { + TQStringList tagPaths = info->tagPaths(false); + + str = tagPaths.join(", "); + if (str.isEmpty()) str = TQString("---"); + if (str.length() > m_maxStringLen) str = str.left(m_maxStringLen-3) + "..."; + tip += m_cellSpecBeg + i18n("Tags:") + m_cellSpecMid + str + m_cellSpecEnd; + } + + if (settings->getToolTipsShowRating()) + { + str.fill( '*', info->rating() ); + if (str.isEmpty()) str = TQString("---"); + tip += m_cellSpecBeg + i18n("Rating:") + m_cellSpecMid + str + m_cellSpecEnd; + } + } + } + + return tip; +} + +} // NameSpace Digikam diff --git a/src/utilities/lighttable/lighttablebar.h b/src/utilities/lighttable/lighttablebar.h new file mode 100644 index 00000000..b64e93a6 --- /dev/null +++ b/src/utilities/lighttable/lighttablebar.h @@ -0,0 +1,153 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-04-11 + * Description : light table thumbs bar + * + * Copyright (C) 2007-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef LIGHTTABLEBAR_H +#define LIGHTTABLEBAR_H + +// Local includes. + +#include "thumbbar.h" +#include "imageinfo.h" +#include "digikam_export.h" + +class TQDragMoveEvent; +class TQDropEvent; +class TQMouseEvent; +class TQPaintEvent; + +class KURL; + +namespace Digikam +{ + +class LightTableBarItem; +class LightTableBarItemPriv; +class LightTableBarPriv; + +class DIGIKAM_EXPORT LightTableBar : public ThumbBarView +{ + TQ_OBJECT + + +public: + + LightTableBar(TQWidget* parent, int orientation=Vertical, bool exifRotate=true); + ~LightTableBar(); + + ImageInfo* currentItemImageInfo() const; + ImageInfoList itemsImageInfoList(); + + void setSelectedItem(LightTableBarItem* ltItem); + + LightTableBarItem* findItemByInfo(const ImageInfo* info) const; + LightTableBarItem* findItemByPos(const TQPoint& pos) const; + + /** Read tool tip settings from Album Settings instance */ + void readToolTipSettings(); + + void setOnLeftPanel(const ImageInfo* info); + void setOnRightPanel(const ImageInfo* info); + + void removeItem(const ImageInfo* info); + + void setNavigateByPair(bool b); + +signals: + + void signalLightTableBarItemSelected(ImageInfo*); + void signalSetItemOnLeftPanel(ImageInfo*); + void signalSetItemOnRightPanel(ImageInfo*); + void signalEditItem(ImageInfo*); + void signalRemoveItem(ImageInfo*); + void signalClearAll(); + void signalDroppedItems(const ImageInfoList&); + +private: + + void viewportPaintEvent(TQPaintEvent*); + void contentsMouseReleaseEvent(TQMouseEvent*); + void startDrag(); + void contentsDragMoveEvent(TQDragMoveEvent*); + void contentsDropEvent(TQDropEvent*); + +private slots: + + void slotImageRatingChanged(TQ_LLONG); + void slotItemSelected(ThumbBarItem*); + + void slotAssignRatingNoStar(); + void slotAssignRatingOneStar(); + void slotAssignRatingTwoStar(); + void slotAssignRatingThreeStar(); + void slotAssignRatingFourStar(); + void slotAssignRatingFiveStar(); + void slotAssignRating(int); + + void slotThemeChanged(); + +private: + + LightTableBarPriv *d; + + friend class LightTableBarItem; +}; + +// ------------------------------------------------------------------------- + +class DIGIKAM_EXPORT LightTableBarItem : public ThumbBarItem +{ +public: + + LightTableBarItem(LightTableBar *view, ImageInfo *info); + ~LightTableBarItem(); + + ImageInfo* info() const; + + void setOnLeftPanel(bool on); + void setOnRightPanel(bool on); + bool isOnLeftPanel() const; + bool isOnRightPanel() const; + +private: + + LightTableBarItemPriv *d; + + friend class LightTableBar; +}; + +// ------------------------------------------------------------------------- + +class DIGIKAM_EXPORT LightTableBarToolTip : public ThumbBarToolTip +{ +public: + + LightTableBarToolTip(ThumbBarView *parent); + +private: + + TQString tipContentExtraData(ThumbBarItem *item); +}; + +} // NameSpace Digikam + +#endif /* LIGHTTABLEBAR_H */ diff --git a/src/utilities/lighttable/lighttablepreview.cpp b/src/utilities/lighttable/lighttablepreview.cpp new file mode 100644 index 00000000..100427ec --- /dev/null +++ b/src/utilities/lighttable/lighttablepreview.cpp @@ -0,0 +1,777 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-21-12 + * Description : digiKam light table preview item. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqpainter.h> +#include <tqcursor.h> +#include <tqstring.h> +#include <tqvaluevector.h> +#include <tqfileinfo.h> +#include <tqtoolbutton.h> +#include <tqtooltip.h> +#include <tqpixmap.h> +#include <tqdrawutil.h> + +// KDE includes. + +#include <kdialogbase.h> +#include <tdelocale.h> +#include <kservice.h> +#include <krun.h> +#include <ktrader.h> +#include <kmimetype.h> +#include <kcursor.h> +#include <kdatetbl.h> +#include <kiconloader.h> +#include <kprocess.h> +#include <tdeapplication.h> + +// Local includes. + +#include "dimg.h" +#include "ddebug.h" +#include "albumdb.h" +#include "constants.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "dragobjects.h" +#include "dmetadata.h" +#include "dpopupmenu.h" +#include "metadatahub.h" +#include "paniconwidget.h" +#include "previewloadthread.h" +#include "loadingdescription.h" +#include "tagspopupmenu.h" +#include "ratingpopupmenu.h" +#include "themeengine.h" +#include "lighttablepreview.h" +#include "lighttablepreview.moc" + +namespace Digikam +{ + +class LightTablePreviewPriv +{ +public: + + LightTablePreviewPriv() + { + panIconPopup = 0; + panIconWidget = 0; + cornerButton = 0; + previewThread = 0; + previewPreloadThread = 0; + imageInfo = 0; + hasPrev = false; + hasNext = false; + selected = false; + dragAndDropEnabled = true; + loadFullImageSize = false; + currentFitWindowZoom = 0; + previewSize = 1024; + } + + bool hasPrev; + bool hasNext; + bool selected; + bool dragAndDropEnabled; + bool loadFullImageSize; + + int previewSize; + + double currentFitWindowZoom; + + TQString path; + TQString nextPath; + TQString previousPath; + + TQToolButton *cornerButton; + + TDEPopupFrame *panIconPopup; + + PanIconWidget *panIconWidget; + + DImg preview; + + ImageInfo *imageInfo; + + PreviewLoadThread *previewThread; + PreviewLoadThread *previewPreloadThread; +}; + +LightTablePreview::LightTablePreview(TQWidget *parent) + : PreviewWidget(parent) +{ + d = new LightTablePreviewPriv; + + // get preview size from screen size, but limit from VGA to WTQXGA + d->previewSize = TQMAX(TDEApplication::desktop()->height(), + TDEApplication::desktop()->width()); + if (d->previewSize < 640) + d->previewSize = 640; + if (d->previewSize > 2560) + d->previewSize = 2560; + + viewport()->setAcceptDrops(true); + setAcceptDrops(true); + + slotThemeChanged(); + setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding); + + d->cornerButton = new TQToolButton(this); + d->cornerButton->setIconSet(SmallIcon("move")); + d->cornerButton->hide(); + TQToolTip::add(d->cornerButton, i18n("Pan the image")); + setCornerWidget(d->cornerButton); + + setLineWidth(5); + setSelected(false); + + // ------------------------------------------------------------ + + connect(d->cornerButton, TQ_SIGNAL(pressed()), + this, TQ_SLOT(slotCornerButtonPressed())); + + connect(this, TQ_SIGNAL(signalRightButtonClicked()), + this, TQ_SLOT(slotContextMenu())); + + connect(ThemeEngine::instance(), TQ_SIGNAL(signalThemeChanged()), + this, TQ_SLOT(slotThemeChanged())); + + // ------------------------------------------------------------ + + slotReset(); +} + +LightTablePreview::~LightTablePreview() +{ + delete d->previewThread; + delete d->previewPreloadThread; + delete d; +} + +void LightTablePreview::setLoadFullImageSize(bool b) +{ + d->loadFullImageSize = b; + reload(); +} + +void LightTablePreview::setDragAndDropEnabled(bool b) +{ + d->dragAndDropEnabled = b; +} + +void LightTablePreview::setDragAndDropMessage() +{ + if (d->dragAndDropEnabled) + { + TQPixmap pix(visibleWidth(), visibleHeight()); + pix.fill(ThemeEngine::instance()->baseColor()); + TQPainter p(&pix); + p.setPen(TQPen(ThemeEngine::instance()->textRegColor())); + p.drawText(0, 0, pix.width(), pix.height(), + TQt::AlignCenter|TQt::WordBreak, + i18n("Drag and drop an image here")); + p.end(); + setImage(pix.convertToImage()); + } +} + +void LightTablePreview::setImage(const DImg& image) +{ + d->preview = image; + + updateZoomAndSize(true); + + viewport()->setUpdatesEnabled(true); + viewport()->update(); +} + +DImg& LightTablePreview::getImage() const +{ + return d->preview; +} + +TQSize LightTablePreview::getImageSize() +{ + return d->preview.size(); +} + +void LightTablePreview::reload() +{ + // cache is cleaned from AlbumIconView::refreshItems + setImagePath(d->path); +} + +void LightTablePreview::setPreviousNextPaths(const TQString& previous, const TQString &next) +{ + d->nextPath = next; + d->previousPath = previous; +} + +void LightTablePreview::setImagePath(const TQString& path) +{ + setCursor( KCursor::waitCursor() ); + + d->path = path; + d->nextPath = TQString(); + d->previousPath = TQString(); + + if (d->path.isEmpty()) + { + slotReset(); + unsetCursor(); + return; + } + + if (!d->previewThread) + { + d->previewThread = new PreviewLoadThread(); + connect(d->previewThread, TQ_SIGNAL(signalImageLoaded(const LoadingDescription &, const DImg &)), + this, TQ_SLOT(slotGotImagePreview(const LoadingDescription &, const DImg&))); + } + if (!d->previewPreloadThread) + { + d->previewPreloadThread = new PreviewLoadThread(); + connect(d->previewPreloadThread, TQ_SIGNAL(signalImageLoaded(const LoadingDescription &, const DImg &)), + this, TQ_SLOT(slotNextPreload())); + } + + if (d->loadFullImageSize) + d->previewThread->loadHighQuality(LoadingDescription(path, 0, AlbumSettings::instance()->getExifRotate())); + else + d->previewThread->load(LoadingDescription(path, d->previewSize, AlbumSettings::instance()->getExifRotate())); +} + +void LightTablePreview::slotGotImagePreview(const LoadingDescription &description, const DImg& preview) +{ + if (description.filePath != d->path) + return; + + if (preview.isNull()) + { + TQPixmap pix(visibleWidth(), visibleHeight()); + pix.fill(ThemeEngine::instance()->baseColor()); + TQPainter p(&pix); + TQFileInfo info(d->path); + p.setPen(TQPen(ThemeEngine::instance()->textRegColor())); + p.drawText(0, 0, pix.width(), pix.height(), + TQt::AlignCenter|TQt::WordBreak, + i18n("Unable to display preview for\n\"%1\"") + .arg(info.fileName())); + p.end(); + setImage(DImg(pix.convertToImage())); + + emit signalPreviewLoaded(false); + } + else + { + DImg img(preview); + if (AlbumSettings::instance()->getExifRotate()) + d->previewThread->exifRotate(img, description.filePath); + setImage(img); + emit signalPreviewLoaded(true); + } + + unsetCursor(); + slotNextPreload(); +} + +void LightTablePreview::slotNextPreload() +{ + TQString loadPath; + if (!d->nextPath.isNull()) + { + loadPath = d->nextPath; + d->nextPath = TQString(); + } + else if (!d->previousPath.isNull()) + { + loadPath = d->previousPath; + d->previousPath = TQString(); + } + else + return; + + d->previewPreloadThread->load(LoadingDescription(loadPath, d->previewSize, + AlbumSettings::instance()->getExifRotate())); +} + +void LightTablePreview::setImageInfo(ImageInfo* info, ImageInfo *previous, ImageInfo *next) +{ + d->imageInfo = info; + d->hasPrev = previous; + d->hasNext = next; + + if (d->imageInfo) + setImagePath(info->filePath()); + else + { + setImagePath(); + setSelected(false); + } + + setPreviousNextPaths(previous ? previous->filePath() : TQString(), + next ? next->filePath() : TQString()); +} + +ImageInfo* LightTablePreview::getImageInfo() const +{ + return d->imageInfo; +} + +void LightTablePreview::slotContextMenu() +{ + RatingPopupMenu *ratingMenu = 0; + TagsPopupMenu *assignTagsMenu = 0; + TagsPopupMenu *removeTagsMenu = 0; + + if (!d->imageInfo) + return; + + //-- Open With Actions ------------------------------------ + + KURL url(d->imageInfo->kurl().path()); + KMimeType::Ptr mimePtr = KMimeType::findByURL(url, 0, true, true); + + TQValueVector<KService::Ptr> serviceVector; + TDETrader::OfferList offers = TDETrader::self()->query(mimePtr->name(), "Type == 'Application'"); + + TQPopupMenu openWithMenu; + + TDETrader::OfferList::Iterator iter; + KService::Ptr ptr; + int index = 100; + + for( iter = offers.begin(); iter != offers.end(); ++iter ) + { + ptr = *iter; + openWithMenu.insertItem( ptr->pixmap(TDEIcon::Small), ptr->name(), index++); + serviceVector.push_back(ptr); + } + + DPopupMenu popmenu(this); + + //-- Zoom actions ----------------------------------------------- + + popmenu.insertItem(SmallIcon("viewmag"), i18n("Zoom in"), 17); + popmenu.insertItem(SmallIcon("zoom-out"), i18n("Zoom out"), 18); + popmenu.insertItem(SmallIcon("view_fit_window"), i18n("Fit to &Window"), 19); + + //-- Edit actions ----------------------------------------------- + + popmenu.insertSeparator(); + popmenu.insertItem(SmallIcon("slideshow"), i18n("SlideShow"), 16); + popmenu.insertItem(SmallIcon("editimage"), i18n("Edit..."), 12); + popmenu.insertItem(i18n("Open With"), &openWithMenu, 13); + + //-- Trash action ------------------------------------------- + + popmenu.insertSeparator(); + popmenu.insertItem(SmallIcon("edittrash"), i18n("Move to Trash"), 14); + + // Bulk assignment/removal of tags -------------------------- + + TQ_LLONG id = d->imageInfo->id(); + TQValueList<TQ_LLONG> idList; + idList.append(id); + + assignTagsMenu = new TagsPopupMenu(idList, 1000, TagsPopupMenu::ASSIGN); + removeTagsMenu = new TagsPopupMenu(idList, 2000, TagsPopupMenu::REMOVE); + + popmenu.insertSeparator(); + + popmenu.insertItem(i18n("Assign Tag"), assignTagsMenu); + int i = popmenu.insertItem(i18n("Remove Tag"), removeTagsMenu); + + connect(assignTagsMenu, TQ_SIGNAL(signalTagActivated(int)), + this, TQ_SLOT(slotAssignTag(int))); + + connect(removeTagsMenu, TQ_SIGNAL(signalTagActivated(int)), + this, TQ_SLOT(slotRemoveTag(int))); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + if (!db->hasTags( idList )) + popmenu.setItemEnabled(i, false); + + popmenu.insertSeparator(); + + // Assign Star Rating ------------------------------------------- + + ratingMenu = new RatingPopupMenu(); + + connect(ratingMenu, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotAssignRating(int))); + + popmenu.insertItem(i18n("Assign Rating"), ratingMenu); + + // -------------------------------------------------------- + + int idm = popmenu.exec(TQCursor::pos()); + + switch(idm) + { + case 12: // Edit... + { + emit signalEditItem(d->imageInfo); + break; + } + + case 14: // Move to trash + { + emit signalDeleteItem(d->imageInfo); + break; + } + + case 16: // SlideShow + { + emit signalSlideShow(); + break; + } + + case 17: // Zoom in + { + slotIncreaseZoom(); + break; + } + + case 18: // Zoom out + { + slotDecreaseZoom(); + break; + } + + case 19: // Fit to window + { + fitToWindow(); + break; + } + + default: + break; + } + + // Open With... + if (idm >= 100 && idm < 1000) + { + KService::Ptr imageServicePtr = serviceVector[idm-100]; + KRun::run(*imageServicePtr, url); + } + + serviceVector.clear(); + delete assignTagsMenu; + delete removeTagsMenu; + delete ratingMenu; +} + +void LightTablePreview::slotAssignTag(int tagID) +{ + if (d->imageInfo) + { + MetadataHub hub; + hub.load(d->imageInfo); + hub.setTag(tagID, true); + hub.write(d->imageInfo, MetadataHub::PartialWrite); + hub.write(d->imageInfo->filePath(), MetadataHub::FullWriteIfChanged); + } +} + +void LightTablePreview::slotRemoveTag(int tagID) +{ + if (d->imageInfo) + { + MetadataHub hub; + hub.load(d->imageInfo); + hub.setTag(tagID, false); + hub.write(d->imageInfo, MetadataHub::PartialWrite); + hub.write(d->imageInfo->filePath(), MetadataHub::FullWriteIfChanged); + } +} + +void LightTablePreview::slotAssignRating(int rating) +{ + rating = TQMIN(RatingMax, TQMAX(RatingMin, rating)); + if (d->imageInfo) + { + MetadataHub hub; + hub.load(d->imageInfo); + hub.setRating(rating); + hub.write(d->imageInfo, MetadataHub::PartialWrite); + hub.write(d->imageInfo->filePath(), MetadataHub::FullWriteIfChanged); + } +} + +void LightTablePreview::slotThemeChanged() +{ + setBackgroundColor(ThemeEngine::instance()->baseColor()); + frameChanged(); +} + +void LightTablePreview::slotCornerButtonPressed() +{ + if (d->panIconPopup) + { + d->panIconPopup->hide(); + delete d->panIconPopup; + d->panIconPopup = 0; + } + + d->panIconPopup = new TDEPopupFrame(this); + PanIconWidget *pan = new PanIconWidget(d->panIconPopup); + pan->setImage(180, 120, getImage()); + d->panIconPopup->setMainWidget(pan); + + TQRect r((int)(contentsX() / zoomFactor()), (int)(contentsY() / zoomFactor()), + (int)(visibleWidth() / zoomFactor()), (int)(visibleHeight() / zoomFactor())); + pan->setRegionSelection(r); + pan->setMouseFocus(); + + connect(pan, TQ_SIGNAL(signalSelectionMoved(const TQRect&, bool)), + this, TQ_SLOT(slotPanIconSelectionMoved(const TQRect&, bool))); + + connect(pan, TQ_SIGNAL(signalHiden()), + this, TQ_SLOT(slotPanIconHiden())); + + TQPoint g = mapToGlobal(viewport()->pos()); + g.setX(g.x()+ viewport()->size().width()); + g.setY(g.y()+ viewport()->size().height()); + d->panIconPopup->popup(TQPoint(g.x() - d->panIconPopup->width(), + g.y() - d->panIconPopup->height())); + + pan->setCursorToLocalRegionSelectionCenter(); +} + +void LightTablePreview::slotPanIconHiden() +{ + d->cornerButton->blockSignals(true); + d->cornerButton->animateClick(); + d->cornerButton->blockSignals(false); +} + +void LightTablePreview::slotPanIconSelectionMoved(const TQRect& r, bool b) +{ + setContentsPos((int)(r.x()*zoomFactor()), (int)(r.y()*zoomFactor())); + + if (b) + { + d->panIconPopup->hide(); + delete d->panIconPopup; + d->panIconPopup = 0; + slotPanIconHiden(); + } +} + +void LightTablePreview::zoomFactorChanged(double zoom) +{ + updateScrollBars(); + + if (horizontalScrollBar()->isVisible() || verticalScrollBar()->isVisible()) + d->cornerButton->show(); + else + d->cornerButton->hide(); + + PreviewWidget::zoomFactorChanged(zoom); +} + +void LightTablePreview::resizeEvent(TQResizeEvent* e) +{ + if (!e) return; + + TQScrollView::resizeEvent(e); + + if (!d->imageInfo) + { + d->cornerButton->hide(); + setDragAndDropMessage(); + } + + updateZoomAndSize(false); +} + +void LightTablePreview::updateZoomAndSize(bool alwaysFitToWindow) +{ + // Set zoom for fit-in-window as minimum, but dont scale up images + // that are smaller than the available space, only scale down. + double zoom = calcAutoZoomFactor(ZoomInOnly); + setZoomMin(zoom); + setZoomMax(zoom*12.0); + + // Is currently the zoom factor set to fit to window? Then set it again to fit the new size. + if (zoomFactor() < zoom || alwaysFitToWindow || zoomFactor() == d->currentFitWindowZoom) + { + setZoomFactor(zoom); + } + + // store which zoom factor means it is fit to window + d->currentFitWindowZoom = zoom; + + updateContentsSize(); +} + +int LightTablePreview::previewWidth() +{ + return d->preview.width(); +} + +int LightTablePreview::previewHeight() +{ + return d->preview.height(); +} + +bool LightTablePreview::previewIsNull() +{ + return d->preview.isNull(); +} + +void LightTablePreview::resetPreview() +{ + d->preview = DImg(); + d->path = TQString(); + d->imageInfo = 0; + + setDragAndDropMessage(); + updateZoomAndSize(true); + viewport()->setUpdatesEnabled(true); + viewport()->update(); + emit signalPreviewLoaded(false); +} + +void LightTablePreview::paintPreview(TQPixmap *pix, int sx, int sy, int sw, int sh) +{ + DImg img = d->preview.smoothScaleSection(sx, sy, sw, sh, tileSize(), tileSize()); + TQPixmap pix2 = img.convertToPixmap(); + bitBlt(pix, 0, 0, &pix2, 0, 0); +} + +void LightTablePreview::contentsDragMoveEvent(TQDragMoveEvent *e) +{ + if (d->dragAndDropEnabled) + { + int albumID; + TQValueList<int> albumIDs; + TQValueList<int> imageIDs; + KURL::List urls; + KURL::List kioURLs; + + if (ItemDrag::decode(e, urls, kioURLs, albumIDs, imageIDs) || + AlbumDrag::decode(e, urls, albumID) || + TagDrag::canDecode(e)) + { + e->accept(); + return; + } + } + + e->ignore(); +} + +void LightTablePreview::contentsDropEvent(TQDropEvent *e) +{ + if (d->dragAndDropEnabled) + { + int albumID; + TQValueList<int> albumIDs; + TQValueList<int> imageIDs; + KURL::List urls; + KURL::List kioURLs; + ImageInfoList list; + + if (ItemDrag::decode(e, urls, kioURLs, albumIDs, imageIDs)) + { + for (TQValueList<int>::const_iterator it = imageIDs.begin(); + it != imageIDs.end(); ++it) + { + list.append(new ImageInfo(*it)); + } + + emit signalDroppedItems(list); + e->accept(); + return; + } + else if (AlbumDrag::decode(e, urls, albumID)) + { + TQValueList<TQ_LLONG> itemIDs = AlbumManager::instance()->albumDB()->getItemIDsInAlbum(albumID); + + for (TQValueList<TQ_LLONG>::const_iterator it = itemIDs.begin(); + it != itemIDs.end(); ++it) + { + list.append(new ImageInfo(*it)); + } + + emit signalDroppedItems(list); + e->accept(); + return; + } + else if(TagDrag::canDecode(e)) + { + TQByteArray ba = e->encodedData("digikam/tag-id"); + TQDataStream ds(ba, IO_ReadOnly); + int tagID; + ds >> tagID; + + AlbumManager* man = AlbumManager::instance(); + TQValueList<TQ_LLONG> itemIDs = man->albumDB()->getItemIDsInTag(tagID, true); + ImageInfoList imageInfoList; + + for (TQValueList<TQ_LLONG>::const_iterator it = itemIDs.begin(); + it != itemIDs.end(); ++it) + { + list.append(new ImageInfo(*it)); + } + + emit signalDroppedItems(list); + e->accept(); + return; + } + } + + e->ignore(); +} + +void LightTablePreview::setSelected(bool sel) +{ + if (d->selected != sel) + { + d->selected = sel; + frameChanged(); + } +} + +bool LightTablePreview::isSelected() +{ + return d->selected; +} + +void LightTablePreview::drawFrame(TQPainter *p) +{ + if (d->selected) + { + qDrawPlainRect(p, frameRect(), ThemeEngine::instance()->thumbSelColor(), lineWidth()); + qDrawPlainRect(p, frameRect(), ThemeEngine::instance()->textSelColor(), 2); + } + else + qDrawPlainRect(p, frameRect(), ThemeEngine::instance()->baseColor(), lineWidth()); +} + +} // NameSpace Digikam diff --git a/src/utilities/lighttable/lighttablepreview.h b/src/utilities/lighttable/lighttablepreview.h new file mode 100644 index 00000000..3549fa1c --- /dev/null +++ b/src/utilities/lighttable/lighttablepreview.h @@ -0,0 +1,125 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-21-12 + * Description : digiKam light table preview item. + * + * Copyright (C) 2006-2008 Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef LIGHTTABLEPREVIEW_H +#define LIGHTTABLEPREVIEW_H + +// TQt includes. + +#include <tqstring.h> +#include <tqimage.h> +#include <tqsize.h> + +// Local includes. + +#include "imageinfo.h" +#include "previewwidget.h" +#include "digikam_export.h" + +class TQPixmap; + +namespace Digikam +{ + +class DImg; +class LoadingDescription; +class LightTablePreviewPriv; + +class DIGIKAM_EXPORT LightTablePreview : public PreviewWidget +{ + +TQ_OBJECT + + +public: + + LightTablePreview(TQWidget *parent=0); + ~LightTablePreview(); + + void setLoadFullImageSize(bool b); + + void setImage(const DImg& image); + DImg& getImage() const; + + TQSize getImageSize(); + + void setImageInfo(ImageInfo* info=0, ImageInfo *previous=0, ImageInfo *next=0); + ImageInfo* getImageInfo() const; + + void reload(); + void setImagePath(const TQString& path=TQString()); + void setPreviousNextPaths(const TQString& previous, const TQString &next); + + void setSelected(bool sel); + bool isSelected(); + + void setDragAndDropEnabled(bool b); + void setDragAndDropMessage(); + +signals: + + void signalDroppedItems(const ImageInfoList&); + void signalDeleteItem(ImageInfo*); + void signalEditItem(ImageInfo*); + void signalPreviewLoaded(bool success); + void signalSlideShow(); + +protected: + + void resizeEvent(TQResizeEvent* e); + void drawFrame(TQPainter *p); + +private slots: + + void slotGotImagePreview(const LoadingDescription &loadingDescription, const DImg &image); + void slotNextPreload(); + void slotContextMenu(); + void slotAssignTag(int tagID); + void slotRemoveTag(int tagID); + void slotAssignRating(int rating); + void slotThemeChanged(); + void slotCornerButtonPressed(); + void slotPanIconSelectionMoved(const TQRect&, bool); + void slotPanIconHiden(); + +private: + + int previewWidth(); + int previewHeight(); + bool previewIsNull(); + void resetPreview(); + void zoomFactorChanged(double zoom); + void updateZoomAndSize(bool alwaysFitToWindow); + inline void paintPreview(TQPixmap *pix, int sx, int sy, int sw, int sh); + + void contentsDragMoveEvent(TQDragMoveEvent*); + void contentsDropEvent(TQDropEvent*); + +private: + + LightTablePreviewPriv* d; +}; + +} // NameSpace Digikam + +#endif /* LIGHTTABLEPREVIEW_H */ diff --git a/src/utilities/lighttable/lighttableview.cpp b/src/utilities/lighttable/lighttableview.cpp new file mode 100644 index 00000000..51f45ab5 --- /dev/null +++ b/src/utilities/lighttable/lighttableview.cpp @@ -0,0 +1,446 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-03-05 + * Description : a widget to display 2 preview image on + * lightable to compare pictures. + * + * Copyright (C) 2007-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlayout.h> + +// KDE includes. + +#include <kdialogbase.h> + +// Local includes. + +#include "ddebug.h" +#include "thumbnailsize.h" +#include "lighttablepreview.h" +#include "lighttableview.h" +#include "lighttableview.moc" + +namespace Digikam +{ + +class LightTableViewPriv +{ +public: + + LightTableViewPriv() + { + syncPreview = false; + leftLoading = false; + rightLoading = false; + leftPreview = 0; + rightPreview = 0; + grid = 0; + } + + bool syncPreview; + bool leftLoading; // To not sync right panel during left loading. + bool rightLoading; // To not sync left panel during right loading. + + TQGridLayout *grid; + + LightTablePreview *leftPreview; + LightTablePreview *rightPreview; +}; + +LightTableView::LightTableView(TQWidget *parent) + : TQFrame(parent, 0, TQt::WDestructiveClose) +{ + d = new LightTableViewPriv; + + setFrameStyle(TQFrame::NoFrame); + setMargin(0); + setLineWidth(0); + + d->grid = new TQGridLayout(this, 1, 1, 0, 1); + d->leftPreview = new LightTablePreview(this); + d->rightPreview = new LightTablePreview(this); + + d->grid->addMultiCellWidget(d->leftPreview, 0, 0, 0, 0); + d->grid->addMultiCellWidget(d->rightPreview, 0, 0, 1, 1); + + d->grid->setColStretch(0, 10), + d->grid->setColStretch(1, 10), + d->grid->setRowStretch(0, 10), + + // Left panel connections ------------------------------------------------ + + connect(d->leftPreview, TQ_SIGNAL(signalZoomFactorChanged(double)), + this, TQ_SIGNAL(signalLeftZoomFactorChanged(double))); + + connect(d->leftPreview, TQ_SIGNAL(contentsMoving(int, int)), + this, TQ_SLOT(slotLeftContentsMoved(int, int))); + + connect(d->leftPreview, TQ_SIGNAL(signalSlideShow()), + this, TQ_SIGNAL(signalSlideShow())); + + connect(d->leftPreview, TQ_SIGNAL(signalDeleteItem(ImageInfo*)), + this, TQ_SIGNAL(signalDeleteItem(ImageInfo*))); + + connect(d->leftPreview, TQ_SIGNAL(signalEditItem(ImageInfo*)), + this, TQ_SIGNAL(signalEditItem(ImageInfo*))); + + connect(d->leftPreview, TQ_SIGNAL(signalDroppedItems(const ImageInfoList&)), + this, TQ_SIGNAL(signalLeftDroppedItems(const ImageInfoList&))); + + connect(d->leftPreview, TQ_SIGNAL(signalPreviewLoaded(bool)), + this, TQ_SLOT(slotLeftPreviewLoaded(bool))); + + connect(d->leftPreview, TQ_SIGNAL(signalLeftButtonClicked()), + this, TQ_SIGNAL(signalLeftPanelLeftButtonClicked())); + + // Right panel connections ------------------------------------------------ + + connect(d->rightPreview, TQ_SIGNAL(signalZoomFactorChanged(double)), + this, TQ_SIGNAL(signalRightZoomFactorChanged(double))); + + connect(d->rightPreview, TQ_SIGNAL(contentsMoving(int, int)), + this, TQ_SLOT(slotRightContentsMoved(int, int))); + + connect(d->rightPreview, TQ_SIGNAL(signalDeleteItem(ImageInfo*)), + this, TQ_SIGNAL(signalDeleteItem(ImageInfo*))); + + connect(d->rightPreview, TQ_SIGNAL(signalEditItem(ImageInfo*)), + this, TQ_SIGNAL(signalEditItem(ImageInfo*))); + + connect(d->rightPreview, TQ_SIGNAL(signalDroppedItems(const ImageInfoList&)), + this, TQ_SIGNAL(signalRightDroppedItems(const ImageInfoList&))); + + connect(d->rightPreview, TQ_SIGNAL(signalSlideShow()), + this, TQ_SIGNAL(signalSlideShow())); + + connect(d->rightPreview, TQ_SIGNAL(signalPreviewLoaded(bool)), + this, TQ_SLOT(slotRightPreviewLoaded(bool))); + + connect(d->rightPreview, TQ_SIGNAL(signalLeftButtonClicked()), + this, TQ_SIGNAL(signalRightPanelLeftButtonClicked())); +} + +LightTableView::~LightTableView() +{ + delete d; +} + +void LightTableView::setLoadFullImageSize(bool b) +{ + d->leftPreview->setLoadFullImageSize(b); + d->rightPreview->setLoadFullImageSize(b); +} + +void LightTableView::setSyncPreview(bool sync) +{ + d->syncPreview = sync; + + // Left panel like a reference to resync preview. + if (d->syncPreview) + slotLeftContentsMoved(d->leftPreview->contentsX(), d->leftPreview->contentsY()); +} + +void LightTableView::setNavigateByPair(bool b) +{ + d->leftPreview->setDragAndDropEnabled(!b); + d->rightPreview->setDragAndDropEnabled(!b); +} + +void LightTableView::slotDecreaseZoom() +{ + if (d->syncPreview) + { + slotDecreaseLeftZoom(); + return; + } + + if (d->leftPreview->isSelected()) + slotDecreaseLeftZoom(); + else if (d->rightPreview->isSelected()) + slotDecreaseRightZoom(); +} + +void LightTableView::slotIncreaseZoom() +{ + if (d->syncPreview) + { + slotIncreaseLeftZoom(); + return; + } + + if (d->leftPreview->isSelected()) + slotIncreaseLeftZoom(); + else if (d->rightPreview->isSelected()) + slotIncreaseRightZoom(); +} + +void LightTableView::slotDecreaseLeftZoom() +{ + d->leftPreview->slotDecreaseZoom(); +} + +void LightTableView::slotIncreaseLeftZoom() +{ + d->leftPreview->slotIncreaseZoom(); +} + +void LightTableView::slotDecreaseRightZoom() +{ + d->rightPreview->slotDecreaseZoom(); +} + +void LightTableView::slotIncreaseRightZoom() +{ + d->rightPreview->slotIncreaseZoom(); +} + +void LightTableView::setLeftZoomFactor(double z) +{ + d->leftPreview->setZoomFactor(z); +} + +void LightTableView::setRightZoomFactor(double z) +{ + d->rightPreview->setZoomFactor(z); +} + +void LightTableView::fitToWindow() +{ + d->leftPreview->fitToWindow(); + d->rightPreview->fitToWindow(); +} + +void LightTableView::toggleFitToWindowOr100() +{ + // If we are currently precisely at 100%, then fit to window, + // otherwise zoom to a centered 100% view. + if ((d->leftPreview->zoomFactor()==1.0) && + (d->rightPreview->zoomFactor()==1.0)) + { + fitToWindow(); + } + else + { + d->leftPreview->setZoomFactor(1.0, true); + d->rightPreview->setZoomFactor(1.0, true); + } +} + +double LightTableView::leftZoomMax() +{ + return d->leftPreview->zoomMax(); +} + +double LightTableView::leftZoomMin() +{ + return d->leftPreview->zoomMin(); +} + +bool LightTableView::leftMaxZoom() +{ + return d->leftPreview->maxZoom(); +} + +bool LightTableView::leftMinZoom() +{ + return d->leftPreview->minZoom(); +} + +double LightTableView::rightZoomMax() +{ + return d->rightPreview->zoomMax(); +} + +double LightTableView::rightZoomMin() +{ + return d->rightPreview->zoomMin(); +} + +bool LightTableView::rightMaxZoom() +{ + return d->rightPreview->maxZoom(); +} + +bool LightTableView::rightMinZoom() +{ + return d->rightPreview->minZoom(); +} + +void LightTableView::slotLeftZoomSliderChanged(int size) +{ + double h = (double)ThumbnailSize::Huge; + double s = (double)ThumbnailSize::Small; + double zmin = d->leftPreview->zoomMin(); + double zmax = d->leftPreview->zoomMax(); + double b = (zmin-(zmax*s/h))/(1-s/h); + double a = (zmax-b)/h; + double z = a*size+b; + + d->leftPreview->setZoomFactorSnapped(z); +} + +void LightTableView::slotRightZoomSliderChanged(int size) +{ + double h = (double)ThumbnailSize::Huge; + double s = (double)ThumbnailSize::Small; + double zmin = d->rightPreview->zoomMin(); + double zmax = d->rightPreview->zoomMax(); + double b = (zmin-(zmax*s/h))/(1-s/h); + double a = (zmax-b)/h; + double z = a*size+b; + + d->rightPreview->setZoomFactorSnapped(z); +} + +void LightTableView::leftReload() +{ + d->leftPreview->reload(); +} + +void LightTableView::rightReload() +{ + d->rightPreview->reload(); +} + +void LightTableView::slotLeftContentsMoved(int x, int y) +{ + if (d->syncPreview && !d->leftLoading) + { + disconnect(d->rightPreview, TQ_SIGNAL(signalZoomFactorChanged(double)), + this, TQ_SIGNAL(signalRightZoomFactorChanged(double))); + + disconnect(d->rightPreview, TQ_SIGNAL(contentsMoving(int, int)), + this, TQ_SLOT(slotRightContentsMoved(int, int))); + + setRightZoomFactor(d->leftPreview->zoomFactor()); + emit signalRightZoomFactorChanged(d->leftPreview->zoomFactor()); + d->rightPreview->setContentsPos(x, y); + + connect(d->rightPreview, TQ_SIGNAL(signalZoomFactorChanged(double)), + this, TQ_SIGNAL(signalRightZoomFactorChanged(double))); + + connect(d->rightPreview, TQ_SIGNAL(contentsMoving(int, int)), + this, TQ_SLOT(slotRightContentsMoved(int, int))); + } +} + +void LightTableView::slotRightContentsMoved(int x, int y) +{ + if (d->syncPreview && !d->rightLoading) + { + disconnect(d->leftPreview, TQ_SIGNAL(signalZoomFactorChanged(double)), + this, TQ_SIGNAL(signalLeftZoomFactorChanged(double))); + + disconnect(d->leftPreview, TQ_SIGNAL(contentsMoving(int, int)), + this, TQ_SLOT(slotLeftContentsMoved(int, int))); + + + setLeftZoomFactor(d->rightPreview->zoomFactor()); + emit signalLeftZoomFactorChanged(d->rightPreview->zoomFactor()); + d->leftPreview->setContentsPos(x, y); + + connect(d->leftPreview, TQ_SIGNAL(signalZoomFactorChanged(double)), + this, TQ_SIGNAL(signalLeftZoomFactorChanged(double))); + + connect(d->leftPreview, TQ_SIGNAL(contentsMoving(int, int)), + this, TQ_SLOT(slotLeftContentsMoved(int, int))); + } +} + +ImageInfo* LightTableView::leftImageInfo() const +{ + return d->leftPreview->getImageInfo(); +} + +ImageInfo* LightTableView::rightImageInfo() const +{ + return d->rightPreview->getImageInfo(); +} + +void LightTableView::setLeftImageInfo(ImageInfo* info) +{ + d->leftLoading = true; + d->leftPreview->setImageInfo(info); +} + +void LightTableView::setRightImageInfo(ImageInfo* info) +{ + d->rightLoading = true; + d->rightPreview->setImageInfo(info); +} + +void LightTableView::slotLeftPreviewLoaded(bool success) +{ + checkForSyncPreview(); + d->leftLoading = false; + slotRightContentsMoved(d->rightPreview->contentsX(), + d->rightPreview->contentsY()); + + emit signalLeftPreviewLoaded(success); +} + +void LightTableView::slotRightPreviewLoaded(bool success) +{ + checkForSyncPreview(); + d->rightLoading = false; + slotLeftContentsMoved(d->leftPreview->contentsX(), + d->leftPreview->contentsY()); + + emit signalRightPreviewLoaded(success); +} + +void LightTableView::checkForSyncPreview() +{ + if (d->leftPreview->getImageInfo() && d->rightPreview->getImageInfo() && + d->leftPreview->getImageSize() == d->rightPreview->getImageSize()) + { + d->syncPreview = true; + } + else + { + d->syncPreview = false; + } + + emit signalToggleOnSyncPreview(d->syncPreview); +} + +void LightTableView::checkForSelection(ImageInfo* info) +{ + if (!info) + { + d->leftPreview->setSelected(false); + d->rightPreview->setSelected(false); + return; + } + + if (d->leftPreview->getImageInfo()) + { + d->leftPreview->setSelected(d->leftPreview->getImageInfo()->id() == info->id()); + } + + if (d->rightPreview->getImageInfo()) + { + d->rightPreview->setSelected(d->rightPreview->getImageInfo()->id() == info->id()); + } +} + +} // namespace Digikam + diff --git a/src/utilities/lighttable/lighttableview.h b/src/utilities/lighttable/lighttableview.h new file mode 100644 index 00000000..273b7072 --- /dev/null +++ b/src/utilities/lighttable/lighttableview.h @@ -0,0 +1,137 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-03-05 + * Description : a widget to display 2 preview image on + * lightable to compare pictures. + * + * Copyright (C) 2007-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef LIGHTTABLEVIEW_H +#define LIGHTTABLEVIEW_H + +// TQt includes. + +#include <tqframe.h> +#include <tqstring.h> + +// Local includes. + +#include "imageinfo.h" +#include "digikam_export.h" + +namespace Digikam +{ + +class LightTableViewPriv; + +class DIGIKAM_EXPORT LightTableView : public TQFrame +{ + +TQ_OBJECT + + +public: + + LightTableView(TQWidget *parent=0); + ~LightTableView(); + + void setSyncPreview(bool sync); + void setNavigateByPair(bool b); + + void setLeftImageInfo(ImageInfo* info=0); + void setRightImageInfo(ImageInfo* info=0); + + ImageInfo* leftImageInfo() const; + ImageInfo* rightImageInfo() const; + + void setLoadFullImageSize(bool b); + + void setLeftZoomFactor(double z); + void setRightZoomFactor(double z); + + void checkForSelection(ImageInfo* info); + + double leftZoomMax(); + double leftZoomMin(); + + double rightZoomMax(); + double rightZoomMin(); + + bool leftMaxZoom(); + bool leftMinZoom(); + + bool rightMaxZoom(); + bool rightMinZoom(); + + void leftReload(); + void rightReload(); + + void fitToWindow(); + void toggleFitToWindowOr100(); + +signals: + + void signalLeftPreviewLoaded(bool); + void signalRightPreviewLoaded(bool); + + void signalLeftZoomFactorChanged(double); + void signalRightZoomFactorChanged(double); + + void signalLeftDroppedItems(const ImageInfoList&); + void signalRightDroppedItems(const ImageInfoList&); + + void signalLeftPanelLeftButtonClicked(); + void signalRightPanelLeftButtonClicked(); + + void signalSlideShow(); + void signalDeleteItem(ImageInfo*); + void signalEditItem(ImageInfo*); + void signalToggleOnSyncPreview(bool); + +public slots: + + void slotDecreaseZoom(); + void slotIncreaseZoom(); + void slotDecreaseLeftZoom(); + void slotIncreaseLeftZoom(); + void slotLeftZoomSliderChanged(int); + + void slotDecreaseRightZoom(); + void slotIncreaseRightZoom(); + void slotRightZoomSliderChanged(int); + +private slots: + + void slotLeftContentsMoved(int, int); + void slotRightContentsMoved(int, int); + void slotLeftPreviewLoaded(bool); + void slotRightPreviewLoaded(bool); + +private : + + void checkForSyncPreview(); + +private : + + LightTableViewPriv* d; +}; + +} // namespace Digikam + +#endif /* LIGHTTABLEVIEW_H */ diff --git a/src/utilities/lighttable/lighttablewindow.cpp b/src/utilities/lighttable/lighttablewindow.cpp new file mode 100644 index 00000000..58409aef --- /dev/null +++ b/src/utilities/lighttable/lighttablewindow.cpp @@ -0,0 +1,1676 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-03-05 + * Description : digiKam light table GUI + * + * Copyright (C) 2007-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqdockarea.h> + +// KDE includes. + +#include <kkeydialog.h> +#include <kedittoolbar.h> +#include <tdeversion.h> +#include <tdelocale.h> +#include <twin.h> +#include <tdemessagebox.h> +#include <tdeapplication.h> +#include <tdeconfig.h> +#include <kstatusbar.h> +#include <tdemenubar.h> + +// LibKDcraw includes. + +#include <libkdcraw/version.h> +#include <libkdcraw/kdcraw.h> + +#if KDCRAW_VERSION < 0x000106 +#include <libkdcraw/dcrawbinary.h> +#endif + +// Local includes. + +#include "ddebug.h" +#include "dlogoaction.h" +#include "themeengine.h" +#include "dimg.h" +#include "dmetadata.h" +#include "albumsettings.h" +#include "albummanager.h" +#include "deletedialog.h" +#include "imagewindow.h" +#include "slideshow.h" +#include "setup.h" +#include "syncjob.h" +#include "thumbnailsize.h" +#include "rawcameradlg.h" +#include "lighttablepreview.h" +#include "lighttablewindowprivate.h" +#include "lighttablewindow.h" +#include "lighttablewindow.moc" + +namespace Digikam +{ + +LightTableWindow* LightTableWindow::m_instance = 0; + +LightTableWindow* LightTableWindow::lightTableWindow() +{ + if (!m_instance) + new LightTableWindow(); + + return m_instance; +} + +bool LightTableWindow::lightTableWindowCreated() +{ + return m_instance; +} + +LightTableWindow::LightTableWindow() + : TDEMainWindow(0, "lighttable", WType_TopLevel) +{ + d = new LightTableWindowPriv; + m_instance = this; + + setCaption(i18n("Light Table")); + + // -- Build the GUI ------------------------------- + + setupUserArea(); + setupStatusBar(); + setupActions(); + setupAccelerators(); + + // Make signals/slots connections + + setupConnections(); + + //------------------------------------------------------------- + + d->leftSidebar->loadViewState(); + d->rightSidebar->loadViewState(); + d->leftSidebar->populateTags(); + d->rightSidebar->populateTags(); + + readSettings(); + applySettings(); + setAutoSaveSettings("LightTable Settings"); +} + +LightTableWindow::~LightTableWindow() +{ + m_instance = 0; + + delete d->barView; + delete d->rightSidebar; + delete d->leftSidebar; + delete d; +} + +void LightTableWindow::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("LightTable Settings"); + + if(config->hasKey("Vertical Splitter Sizes")) + d->vSplitter->setSizes(config->readIntListEntry("Vertical Splitter Sizes")); + + if(config->hasKey("Horizontal Splitter Sizes")) + d->hSplitter->setSizes(config->readIntListEntry("Horizontal Splitter Sizes")); + + d->navigateByPairAction->setChecked(config->readBoolEntry("Navigate By Pair", false)); + slotToggleNavigateByPair(); +} + +void LightTableWindow::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("LightTable Settings"); + config->writeEntry("Vertical Splitter Sizes", d->vSplitter->sizes()); + config->writeEntry("Horizontal Splitter Sizes", d->hSplitter->sizes()); + config->writeEntry("Navigate By Pair", d->navigateByPairAction->isChecked()); + config->sync(); +} + +void LightTableWindow::applySettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("LightTable Settings"); + + d->autoLoadOnRightPanel = config->readBoolEntry("Auto Load Right Panel", true); + d->autoSyncPreview = config->readBoolEntry("Auto Sync Preview", true); + d->fullScreenHideToolBar = config->readBoolEntry("FullScreen Hide ToolBar", false); + d->previewView->setLoadFullImageSize(config->readBoolEntry("Load Full Image size", false)); + refreshView(); +} + +void LightTableWindow::refreshView() +{ + d->leftSidebar->refreshTagsView(); + d->rightSidebar->refreshTagsView(); +} + +void LightTableWindow::closeEvent(TQCloseEvent* e) +{ + if (!e) return; + + writeSettings(); + + e->accept(); +} + +void LightTableWindow::setupUserArea() +{ + TQWidget* mainW = new TQWidget(this); + d->hSplitter = new TQSplitter(TQt::Horizontal, mainW); + TQHBoxLayout *hlay = new TQHBoxLayout(mainW); + d->leftSidebar = new ImagePropertiesSideBarDB(mainW, + "LightTable Left Sidebar", d->hSplitter, + Sidebar::Left, true); + + TQWidget* centralW = new TQWidget(d->hSplitter); + TQVBoxLayout *vlay = new TQVBoxLayout(centralW); + d->vSplitter = new TQSplitter(TQt::Vertical, centralW); + d->barView = new LightTableBar(d->vSplitter, ThumbBarView::Horizontal, + AlbumSettings::instance()->getExifRotate()); + d->previewView = new LightTableView(d->vSplitter); + vlay->addWidget(d->vSplitter); + + d->rightSidebar = new ImagePropertiesSideBarDB(mainW, + "LightTable Right Sidebar", d->hSplitter, + Sidebar::Right, true); + + hlay->addWidget(d->leftSidebar); + hlay->addWidget(d->hSplitter); + hlay->addWidget(d->rightSidebar); + + d->hSplitter->setFrameStyle( TQFrame::NoFrame ); + d->hSplitter->setFrameShadow( TQFrame::Plain ); + d->hSplitter->setFrameShape( TQFrame::NoFrame ); + d->hSplitter->setOpaqueResize(false); + d->vSplitter->setFrameStyle( TQFrame::NoFrame ); + d->vSplitter->setFrameShadow( TQFrame::Plain ); + d->vSplitter->setFrameShape( TQFrame::NoFrame ); + d->vSplitter->setOpaqueResize(false); + + setCentralWidget(mainW); +} + +void LightTableWindow::setupStatusBar() +{ + d->leftZoomBar = new StatusZoomBar(statusBar()); + d->leftZoomBar->setMaximumHeight(fontMetrics().height()+2); + statusBar()->addWidget(d->leftZoomBar, 1); + d->leftZoomBar->setEnabled(false); + + d->statusProgressBar = new StatusProgressBar(statusBar()); + d->statusProgressBar->setAlignment(TQt::AlignCenter); + d->statusProgressBar->setMaximumHeight(fontMetrics().height()+2); + statusBar()->addWidget(d->statusProgressBar, 100); + + d->rightZoomBar = new StatusZoomBar(statusBar()); + d->rightZoomBar->setMaximumHeight(fontMetrics().height()+2); + statusBar()->addWidget(d->rightZoomBar, 1); + d->rightZoomBar->setEnabled(false); +} + +void LightTableWindow::setupConnections() +{ + connect(d->statusProgressBar, TQ_SIGNAL(signalCancelButtonPressed()), + this, TQ_SLOT(slotProgressBarCancelButtonPressed())); + + connect(ThemeEngine::instance(), TQ_SIGNAL(signalThemeChanged()), + this, TQ_SLOT(slotThemeChanged())); + + // Thumbs bar connections --------------------------------------- + + connect(d->barView, TQ_SIGNAL(signalSetItemOnLeftPanel(ImageInfo*)), + this, TQ_SLOT(slotSetItemOnLeftPanel(ImageInfo*))); + + connect(d->barView, TQ_SIGNAL(signalSetItemOnRightPanel(ImageInfo*)), + this, TQ_SLOT(slotSetItemOnRightPanel(ImageInfo*))); + + connect(d->barView, TQ_SIGNAL(signalRemoveItem(ImageInfo*)), + this, TQ_SLOT(slotRemoveItem(ImageInfo*))); + + connect(d->barView, TQ_SIGNAL(signalEditItem(ImageInfo*)), + this, TQ_SLOT(slotEditItem(ImageInfo*))); + + connect(d->barView, TQ_SIGNAL(signalClearAll()), + this, TQ_SLOT(slotClearItemsList())); + + connect(d->barView, TQ_SIGNAL(signalLightTableBarItemSelected(ImageInfo*)), + this, TQ_SLOT(slotItemSelected(ImageInfo*))); + + connect(d->barView, TQ_SIGNAL(signalDroppedItems(const ImageInfoList&)), + this, TQ_SLOT(slotThumbbarDroppedItems(const ImageInfoList&))); + + // Zoom bars connections ----------------------------------------- + + connect(d->leftZoomBar, TQ_SIGNAL(signalZoomMinusClicked()), + d->previewView, TQ_SLOT(slotDecreaseLeftZoom())); + + connect(d->leftZoomBar, TQ_SIGNAL(signalZoomPlusClicked()), + d->previewView, TQ_SLOT(slotIncreaseLeftZoom())); + + connect(d->leftZoomBar, TQ_SIGNAL(signalZoomSliderChanged(int)), + d->previewView, TQ_SLOT(slotLeftZoomSliderChanged(int))); + + connect(d->rightZoomBar, TQ_SIGNAL(signalZoomMinusClicked()), + d->previewView, TQ_SLOT(slotDecreaseRightZoom())); + + connect(d->rightZoomBar, TQ_SIGNAL(signalZoomPlusClicked()), + d->previewView, TQ_SLOT(slotIncreaseRightZoom())); + + connect(d->rightZoomBar, TQ_SIGNAL(signalZoomSliderChanged(int)), + d->previewView, TQ_SLOT(slotRightZoomSliderChanged(int))); + + // View connections --------------------------------------------- + + connect(d->previewView, TQ_SIGNAL(signalLeftZoomFactorChanged(double)), + this, TQ_SLOT(slotLeftZoomFactorChanged(double))); + + connect(d->previewView, TQ_SIGNAL(signalRightZoomFactorChanged(double)), + this, TQ_SLOT(slotRightZoomFactorChanged(double))); + + connect(d->previewView, TQ_SIGNAL(signalEditItem(ImageInfo*)), + this, TQ_SLOT(slotEditItem(ImageInfo*))); + + connect(d->previewView, TQ_SIGNAL(signalDeleteItem(ImageInfo*)), + this, TQ_SLOT(slotDeleteItem(ImageInfo*))); + + connect(d->previewView, TQ_SIGNAL(signalSlideShow()), + this, TQ_SLOT(slotToggleSlideShow())); + + connect(d->previewView, TQ_SIGNAL(signalLeftDroppedItems(const ImageInfoList&)), + this, TQ_SLOT(slotLeftDroppedItems(const ImageInfoList&))); + + connect(d->previewView, TQ_SIGNAL(signalRightDroppedItems(const ImageInfoList&)), + this, TQ_SLOT(slotRightDroppedItems(const ImageInfoList&))); + + connect(d->previewView, TQ_SIGNAL(signalToggleOnSyncPreview(bool)), + this, TQ_SLOT(slotToggleOnSyncPreview(bool))); + + connect(d->previewView, TQ_SIGNAL(signalLeftPreviewLoaded(bool)), + this, TQ_SLOT(slotLeftPreviewLoaded(bool))); + + connect(d->previewView, TQ_SIGNAL(signalRightPreviewLoaded(bool)), + this, TQ_SLOT(slotRightPreviewLoaded(bool))); + + connect(d->previewView, TQ_SIGNAL(signalLeftPanelLeftButtonClicked()), + this, TQ_SLOT(slotLeftPanelLeftButtonClicked())); + + connect(d->previewView, TQ_SIGNAL(signalRightPanelLeftButtonClicked()), + this, TQ_SLOT(slotRightPanelLeftButtonClicked())); +} + +void LightTableWindow::setupActions() +{ + // -- Standard 'File' menu actions --------------------------------------------- + + d->backwardAction = KStdAction::back(this, TQ_SLOT(slotBackward()), + actionCollection(), "lighttable_backward"); + d->backwardAction->setEnabled(false); + + d->forwardAction = KStdAction::forward(this, TQ_SLOT(slotForward()), + actionCollection(), "lighttable_forward"); + d->forwardAction->setEnabled(false); + + d->firstAction = new TDEAction(i18n("&First"), "go-first", + TDEStdAccel::shortcut( TDEStdAccel::Home), + this, TQ_SLOT(slotFirst()), + actionCollection(), "lighttable_first"); + d->firstAction->setEnabled(false); + + d->lastAction = new TDEAction(i18n("&Last"), "go-last", + TDEStdAccel::shortcut( TDEStdAccel::End), + this, TQ_SLOT(slotLast()), + actionCollection(), "lighttable_last"); + d->lastAction->setEnabled(false); + + d->setItemLeftAction = new TDEAction(i18n("On Left"), "go-previous", + CTRL+Key_L, this, TQ_SLOT(slotSetItemLeft()), + actionCollection(), "lighttable_setitemleft"); + d->setItemLeftAction->setEnabled(false); + d->setItemLeftAction->setWhatsThis(i18n("Show item on left panel")); + + d->setItemRightAction = new TDEAction(i18n("On Right"), "go-next", + CTRL+Key_R, this, TQ_SLOT(slotSetItemRight()), + actionCollection(), "lighttable_setitemright"); + d->setItemRightAction->setEnabled(false); + d->setItemRightAction->setWhatsThis(i18n("Show item on right panel")); + + d->editItemAction = new TDEAction(i18n("Edit"), "editimage", + Key_F4, this, TQ_SLOT(slotEditItem()), + actionCollection(), "lighttable_edititem"); + d->editItemAction->setEnabled(false); + + d->removeItemAction = new TDEAction(i18n("Remove item from LightTable"), "window-close", + CTRL+Key_K, this, TQ_SLOT(slotRemoveItem()), + actionCollection(), "lighttable_removeitem"); + d->removeItemAction->setEnabled(false); + + d->clearListAction = new TDEAction(i18n("Remove all items from LightTable"), "editshred", + CTRL+SHIFT+Key_K, this, TQ_SLOT(slotClearItemsList()), + actionCollection(), "lighttable_clearlist"); + d->clearListAction->setEnabled(false); + + d->fileDeleteAction = new TDEAction(i18n("Move to Trash"), "edittrash", + Key_Delete, + this, TQ_SLOT(slotDeleteItem()), + actionCollection(), "lighttable_filedelete"); + d->fileDeleteAction->setEnabled(false); + + KStdAction::close(this, TQ_SLOT(close()), actionCollection(), "lighttable_close"); + + // -- Standard 'View' menu actions --------------------------------------------- + + d->syncPreviewAction = new TDEToggleAction(i18n("Synchronize"), "goto", + CTRL+SHIFT+Key_Y, this, + TQ_SLOT(slotToggleSyncPreview()), + actionCollection(), "lighttable_syncpreview"); + d->syncPreviewAction->setEnabled(false); + d->syncPreviewAction->setWhatsThis(i18n("Synchronize preview from left and right panels")); + + d->navigateByPairAction = new TDEToggleAction(i18n("By Pair"), "preferences-system", + CTRL+SHIFT+Key_P, this, + TQ_SLOT(slotToggleNavigateByPair()), + actionCollection(), "lighttable_navigatebypair"); + d->navigateByPairAction->setEnabled(false); + d->navigateByPairAction->setWhatsThis(i18n("Navigate by pair with all items")); + + d->zoomPlusAction = KStdAction::zoomIn(d->previewView, TQ_SLOT(slotIncreaseZoom()), + actionCollection(), "lighttable_zoomplus"); + d->zoomPlusAction->setEnabled(false); + + d->zoomMinusAction = KStdAction::zoomOut(d->previewView, TQ_SLOT(slotDecreaseZoom()), + actionCollection(), "lighttable_zoomminus"); + d->zoomMinusAction->setEnabled(false); + + d->zoomTo100percents = new TDEAction(i18n("Zoom to 100%"), "zoom-original", + ALT+CTRL+Key_0, // NOTE: Photoshop 7 use ALT+CTRL+0. + this, TQ_SLOT(slotZoomTo100Percents()), + actionCollection(), "lighttable_zoomto100percents"); + + d->zoomFitToWindowAction = new TDEAction(i18n("Fit to &Window"), "view_fit_window", + CTRL+SHIFT+Key_E, this, TQ_SLOT(slotFitToWindow()), + actionCollection(), "lighttable_zoomfit2window"); + + // Do not use std KDE action for full screen because action text is too large for app. toolbar. + d->fullScreenAction = new TDEToggleAction(i18n("Full Screen"), "view-fullscreen", + CTRL+SHIFT+Key_F, this, + TQ_SLOT(slotToggleFullScreen()), + actionCollection(), "lighttable_fullscreen"); + d->fullScreenAction->setWhatsThis(i18n("Toggle the window to full screen mode")); + + d->slideShowAction = new TDEAction(i18n("Slideshow"), "slideshow", Key_F9, + this, TQ_SLOT(slotToggleSlideShow()), + actionCollection(),"lighttable_slideshow"); + + // -- Standard 'Configure' menu actions ---------------------------------------- + + d->showMenuBarAction = KStdAction::showMenubar(this, TQ_SLOT(slotShowMenuBar()), actionCollection()); + + KStdAction::keyBindings(this, TQ_SLOT(slotEditKeys()), actionCollection()); + KStdAction::configureToolbars(this, TQ_SLOT(slotConfToolbars()), actionCollection()); + KStdAction::preferences(this, TQ_SLOT(slotSetup()), actionCollection()); + + // ----------------------------------------------------------------------------------------- + + d->themeMenuAction = new TDESelectAction(i18n("&Themes"), 0, actionCollection(), "theme_menu"); + connect(d->themeMenuAction, TQ_SIGNAL(activated(const TQString&)), + this, TQ_SLOT(slotChangeTheme(const TQString&))); + + d->themeMenuAction->setItems(ThemeEngine::instance()->themeNames()); + slotThemeChanged(); + + // -- Standard 'Help' menu actions --------------------------------------------- + + + d->donateMoneyAction = new TDEAction(i18n("Donate..."), + 0, 0, + this, TQ_SLOT(slotDonateMoney()), + actionCollection(), + "lighttable_donatemoney"); + + d->contributeAction = new TDEAction(i18n("Contribute..."), + 0, 0, + this, TQ_SLOT(slotContribute()), + actionCollection(), + "lighttable_contribute"); + + d->rawCameraListAction = new TDEAction(i18n("Supported RAW Cameras"), + "kdcraw", + 0, + this, + TQ_SLOT(slotRawCameraList()), + actionCollection(), + "lighttable_rawcameralist"); + + + // Provides a menu entry that allows showing/hiding the toolbar(s) + setStandardToolBarMenuEnabled(true); + + // Provides a menu entry that allows showing/hiding the statusbar + createStandardStatusBarAction(); + + // -- Rating actions --------------------------------------------------------------- + + d->star0 = new TDEAction(i18n("Assign Rating \"No Stars\""), CTRL+Key_0, + d->barView, TQ_SLOT(slotAssignRatingNoStar()), + actionCollection(), "lighttable_ratenostar"); + d->star1 = new TDEAction(i18n("Assign Rating \"One Star\""), CTRL+Key_1, + d->barView, TQ_SLOT(slotAssignRatingOneStar()), + actionCollection(), "lighttable_rateonestar"); + d->star2 = new TDEAction(i18n("Assign Rating \"Two Stars\""), CTRL+Key_2, + d->barView, TQ_SLOT(slotAssignRatingTwoStar()), + actionCollection(), "lighttable_ratetwostar"); + d->star3 = new TDEAction(i18n("Assign Rating \"Three Stars\""), CTRL+Key_3, + d->barView, TQ_SLOT(slotAssignRatingThreeStar()), + actionCollection(), "lighttable_ratethreestar"); + d->star4 = new TDEAction(i18n("Assign Rating \"Four Stars\""), CTRL+Key_4, + d->barView, TQ_SLOT(slotAssignRatingFourStar()), + actionCollection(), "lighttable_ratefourstar"); + d->star5 = new TDEAction(i18n("Assign Rating \"Five Stars\""), CTRL+Key_5, + d->barView, TQ_SLOT(slotAssignRatingFiveStar()), + actionCollection(), "lighttable_ratefivestar"); + + // --------------------------------------------------------------------------------- + + new DLogoAction(actionCollection(), "logo_action"); + + createGUI("lighttablewindowui.rc", false); +} + +void LightTableWindow::setupAccelerators() +{ + d->accelerators = new TDEAccel(this); + + d->accelerators->insert("Exit fullscreen", i18n("Exit Fullscreen mode"), + i18n("Exit fullscreen viewing mode"), + Key_Escape, this, TQ_SLOT(slotEscapePressed()), + false, true); + + d->accelerators->insert("Next Image Key_Space", i18n("Next Image"), + i18n("Load Next Image"), + Key_Space, this, TQ_SLOT(slotForward()), + false, true); + + d->accelerators->insert("Previous Image SHIFT+Key_Space", i18n("Previous Image"), + i18n("Load Previous Image"), + SHIFT+Key_Space, this, TQ_SLOT(slotBackward()), + false, true); + + d->accelerators->insert("Previous Image Key_Backspace", i18n("Previous Image"), + i18n("Load Previous Image"), + Key_Backspace, this, TQ_SLOT(slotBackward()), + false, true); + + d->accelerators->insert("Next Image Key_Next", i18n("Next Image"), + i18n("Load Next Image"), + Key_Next, this, TQ_SLOT(slotForward()), + false, true); + + d->accelerators->insert("Previous Image Key_Prior", i18n("Previous Image"), + i18n("Load Previous Image"), + Key_Prior, this, TQ_SLOT(slotBackward()), + false, true); + + d->accelerators->insert("Zoom Plus Key_Plus", i18n("Zoom in"), + i18n("Zoom in on image"), + Key_Plus, d->previewView, TQ_SLOT(slotIncreaseZoom()), + false, true); + + d->accelerators->insert("Zoom Plus Key_Minus", i18n("Zoom out"), + i18n("Zoom out from image"), + Key_Minus, d->previewView, TQ_SLOT(slotDecreaseZoom()), + false, true); +} + +// Deal with items dropped onto the thumbbar (e.g. from the Album view) +void LightTableWindow::slotThumbbarDroppedItems(const ImageInfoList& list) +{ + // Setting the third parameter of loadImageInfos to true + // means that the images are added to the presently available images. + loadImageInfos(list, 0, true); +} + +// We get here either +// - via CTRL+L (from the albumview) +// a) digikamapp.cpp: CTRL+key_L leads to slotImageLightTable()) +// b) digikamview.cpp: void DigikamView::slotImageLightTable() +// calls d->iconView->insertToLightTable(list, info); +// c) albumiconview.cpp: AlbumIconView::insertToLightTable +// calls ltview->loadImageInfos(list, current); +// - via drag&drop, i.e. calls issued by the ...Dropped... routines +void LightTableWindow::loadImageInfos(const ImageInfoList &list, + ImageInfo *imageInfoCurrent, + bool addTo) +{ + // Clear all items before adding new images to the light table. + if (!addTo) + slotClearItemsList(); + + ImageInfoList l = list; + + if (!imageInfoCurrent) + imageInfoCurrent = l.first(); + + AlbumSettings *settings = AlbumSettings::instance(); + if (!settings) return; + + TQString imagefilter = settings->getImageFileFilter().lower() + + settings->getImageFileFilter().upper(); + +#if KDCRAW_VERSION < 0x000106 + if (KDcrawIface::DcrawBinary::instance()->versionIsRight()) + { + // add raw files only if dcraw is available + imagefilter += settings->getRawFileFilter().lower() + + settings->getRawFileFilter().upper(); + } +#else + imagefilter += settings->getRawFileFilter().lower() + + settings->getRawFileFilter().upper(); +#endif + + d->barView->blockSignals(true); + for (TQPtrList<ImageInfo>::const_iterator it = l.begin(); it != l.end(); ++it) + { + TQString fileExtension = (*it)->kurl().fileName().section( '.', -1 ); + + if ( imagefilter.find(fileExtension) != -1 && + !d->barView->findItemByInfo(*it) ) + { + new LightTableBarItem(d->barView, *it); + } + } + d->barView->blockSignals(false); + + // if window is iconified, show it + if (isMinimized()) + { + KWin::deIconifyWindow(winId()); + } + + refreshStatusBar(); +} + +void LightTableWindow::refreshStatusBar() +{ + switch (d->barView->countItems()) + { + case 0: + d->statusProgressBar->progressBarMode(StatusProgressBar::TextMode, + i18n("No item on Light Table")); + break; + case 1: + d->statusProgressBar->progressBarMode(StatusProgressBar::TextMode, + i18n("1 item on Light Table")); + break; + default: + d->statusProgressBar->progressBarMode(StatusProgressBar::TextMode, + i18n("%1 items on Light Table") + .arg(d->barView->countItems())); + break; + } +} + +void LightTableWindow::slotItemsUpdated(const KURL::List& urls) +{ + d->barView->refreshThumbs(urls); + + for (KURL::List::const_iterator it = urls.begin() ; it != urls.end() ; ++it) + { + if (d->previewView->leftImageInfo()) + { + if (d->previewView->leftImageInfo()->kurl() == *it) + { + d->previewView->leftReload(); + d->leftSidebar->itemChanged(d->previewView->leftImageInfo()); + } + } + + if (d->previewView->rightImageInfo()) + { + if (d->previewView->rightImageInfo()->kurl() == *it) + { + d->previewView->rightReload(); + d->rightSidebar->itemChanged(d->previewView->rightImageInfo()); + } + } + } +} + +void LightTableWindow::slotLeftPanelLeftButtonClicked() +{ + if (d->navigateByPairAction->isChecked()) return; + + d->barView->setSelectedItem(d->barView->findItemByInfo(d->previewView->leftImageInfo())); +} + +void LightTableWindow::slotRightPanelLeftButtonClicked() +{ + // With navigate by pair option, only the left panel can be selected. + if (d->navigateByPairAction->isChecked()) return; + + d->barView->setSelectedItem(d->barView->findItemByInfo(d->previewView->rightImageInfo())); +} + +void LightTableWindow::slotLeftPreviewLoaded(bool b) +{ + d->leftZoomBar->setEnabled(b); + + if (b) + { + d->previewView->checkForSelection(d->barView->currentItemImageInfo()); + d->barView->setOnLeftPanel(d->previewView->leftImageInfo()); + + LightTableBarItem *item = d->barView->findItemByInfo(d->previewView->leftImageInfo()); + if (item) item->setOnLeftPanel(true); + + if (d->navigateByPairAction->isChecked() && item) + { + LightTableBarItem* next = dynamic_cast<LightTableBarItem*>(item->next()); + if (next) + { + d->barView->setOnRightPanel(next->info()); + slotSetItemOnRightPanel(next->info()); + } + else + { + LightTableBarItem* first = dynamic_cast<LightTableBarItem*>(d->barView->firstItem()); + slotSetItemOnRightPanel(first ? first->info() : 0); + } + } + } +} + +void LightTableWindow::slotRightPreviewLoaded(bool b) +{ + d->rightZoomBar->setEnabled(b); + if (b) + { + d->previewView->checkForSelection(d->barView->currentItemImageInfo()); + d->barView->setOnRightPanel(d->previewView->rightImageInfo()); + + LightTableBarItem *item = d->barView->findItemByInfo(d->previewView->rightImageInfo()); + if (item) item->setOnRightPanel(true); + } +} + +void LightTableWindow::slotItemSelected(ImageInfo* info) +{ + if (info) + { + d->setItemLeftAction->setEnabled(true); + d->setItemRightAction->setEnabled(true); + d->editItemAction->setEnabled(true); + d->removeItemAction->setEnabled(true); + d->clearListAction->setEnabled(true); + d->fileDeleteAction->setEnabled(true); + d->backwardAction->setEnabled(true); + d->forwardAction->setEnabled(true); + d->firstAction->setEnabled(true); + d->lastAction->setEnabled(true); + d->syncPreviewAction->setEnabled(true); + d->zoomPlusAction->setEnabled(true); + d->zoomMinusAction->setEnabled(true); + d->navigateByPairAction->setEnabled(true); + d->slideShowAction->setEnabled(true); + + LightTableBarItem* curr = d->barView->findItemByInfo(info); + if (curr) + { + if (!curr->prev()) + { +// d->backwardAction->setEnabled(false); + d->firstAction->setEnabled(false); + } + + if (!curr->next()) + { +// d->forwardAction->setEnabled(false); + d->lastAction->setEnabled(false); + } + + if (d->navigateByPairAction->isChecked()) + { + d->setItemLeftAction->setEnabled(false); + d->setItemRightAction->setEnabled(false); + + d->barView->setOnLeftPanel(info); + slotSetItemOnLeftPanel(info); + } + else if (d->autoLoadOnRightPanel && !curr->isOnLeftPanel()) + { + d->barView->setOnRightPanel(info); + slotSetItemOnRightPanel(info); + } + } + } + else + { + d->setItemLeftAction->setEnabled(false); + d->setItemRightAction->setEnabled(false); + d->editItemAction->setEnabled(false); + d->removeItemAction->setEnabled(false); + d->clearListAction->setEnabled(false); + d->fileDeleteAction->setEnabled(false); + d->backwardAction->setEnabled(false); + d->forwardAction->setEnabled(false); + d->firstAction->setEnabled(false); + d->lastAction->setEnabled(false); + d->zoomPlusAction->setEnabled(false); + d->zoomMinusAction->setEnabled(false); + d->syncPreviewAction->setEnabled(false); + d->navigateByPairAction->setEnabled(false); + d->slideShowAction->setEnabled(false); + } + + d->previewView->checkForSelection(info); +} + +// Deal with one (or more) items dropped onto the left panel +void LightTableWindow::slotLeftDroppedItems(const ImageInfoList& list) +{ + ImageInfo *info = *(list.begin()); + // add the image to the existing images + loadImageInfos(list, info, true); + + // We will check if first item from list is already stored in thumbbar + // Note that the thumbbar stores all ImageInfo reference + // in memory for preview object. + LightTableBarItem *item = d->barView->findItemByInfo(info); + if (item) + { + slotSetItemOnLeftPanel(item->info()); + // One approach is to make this item the current one, via + // d->barView->setSelectedItem(item); + // However, this is not good, because this also sets + // the right thumb to the same image. + // Therefore we use setLeftRightItems if there is more than + // one item in the list of dropped images. + } +} + +// Deal with one (or more) items dropped onto the right panel +void LightTableWindow::slotRightDroppedItems(const ImageInfoList& list) +{ + ImageInfo *info = *(list.begin()); + // add the image to the existing images + loadImageInfos(list, info, true); + + // We will check if first item from list is already stored in thumbbar + // Note that the thumbbar stores all ImageInfo reference + // in memory for preview object. + LightTableBarItem *item = d->barView->findItemByInfo(info); + if (item) + { + slotSetItemOnRightPanel(item->info()); + // Make this item the current one. + d->barView->setSelectedItem(item); + } +} + +// Set the images for the left and right panel. +void LightTableWindow::setLeftRightItems(const ImageInfoList &list, bool addTo) +{ + ImageInfoList l = list; + + if (l.count() == 0) + return; + + ImageInfo *info = l.first(); + LightTableBarItem *ltItem = d->barView->findItemByInfo(info); + + if (l.count() == 1 && !addTo) + { + // Just one item; this is used for the left panel. + d->barView->setOnLeftPanel(info); + slotSetItemOnLeftPanel(info); + d->barView->setSelectedItem(ltItem); + d->barView->ensureItemVisible(ltItem); + return; + } + + if (ltItem) + { + // The first item is used for the left panel. + if (!addTo) + { + d->barView->setOnLeftPanel(info); + slotSetItemOnLeftPanel(info); + } + + // The subsequent item is used for the right panel. + LightTableBarItem* next = dynamic_cast<LightTableBarItem*>(ltItem->next()); + if (next && !addTo) + { + d->barView->setOnRightPanel(next->info()); + slotSetItemOnRightPanel(next->info()); + if (!d->navigateByPairAction->isChecked()) + { + d->barView->setSelectedItem(next); + // ensure that the selected item is visible + // FIXME: this does not work: + d->barView->ensureItemVisible(next); + } + } + + // If navigate by pairs is active, the left panel item is selected. + // (Fixes parts of bug #150296) + if (d->navigateByPairAction->isChecked()) + { + d->barView->setSelectedItem(ltItem); + d->barView->ensureItemVisible(ltItem); + } + } +} + +void LightTableWindow::slotSetItemLeft() +{ + if (d->barView->currentItemImageInfo()) + { + slotSetItemOnLeftPanel(d->barView->currentItemImageInfo()); + } +} + +void LightTableWindow::slotSetItemRight() +{ + if (d->barView->currentItemImageInfo()) + { + slotSetItemOnRightPanel(d->barView->currentItemImageInfo()); + } +} + +void LightTableWindow::slotSetItemOnLeftPanel(ImageInfo* info) +{ + d->previewView->setLeftImageInfo(info); + if (info) + d->leftSidebar->itemChanged(info); + else + d->leftSidebar->slotNoCurrentItem(); +} + +void LightTableWindow::slotSetItemOnRightPanel(ImageInfo* info) +{ + d->previewView->setRightImageInfo(info); + if (info) + d->rightSidebar->itemChanged(info); + else + d->rightSidebar->slotNoCurrentItem(); +} + +void LightTableWindow::slotClearItemsList() +{ + if (d->previewView->leftImageInfo()) + { + d->previewView->setLeftImageInfo(); + d->leftSidebar->slotNoCurrentItem(); + } + + if (d->previewView->rightImageInfo()) + { + d->previewView->setRightImageInfo(); + d->rightSidebar->slotNoCurrentItem(); + } + + d->barView->clear(); + refreshStatusBar(); +} + +void LightTableWindow::slotDeleteItem() +{ + if (d->barView->currentItemImageInfo()) + slotDeleteItem(d->barView->currentItemImageInfo()); +} + +void LightTableWindow::slotDeleteItem(ImageInfo* info) +{ + bool ask = true; + bool permanently = false; + + KURL u = info->kurl(); + PAlbum *palbum = AlbumManager::instance()->findPAlbum(u.directory()); + if (!palbum) + return; + + // Provide a digikamalbums:// URL to TDEIO + KURL kioURL = info->kurlForKIO(); + KURL fileURL = u; + + bool useTrash; + + if (ask) + { + bool preselectDeletePermanently = permanently; + + DeleteDialog dialog(this); + + KURL::List urlList; + urlList.append(u); + if (!dialog.confirmDeleteList(urlList, + DeleteDialogMode::Files, + preselectDeletePermanently ? + DeleteDialogMode::NoChoiceDeletePermanently : DeleteDialogMode::NoChoiceTrash)) + return; + + useTrash = !dialog.shouldDelete(); + } + else + { + useTrash = !permanently; + } + + // trash does not like non-local URLs, put is not implemented + if (useTrash) + kioURL = fileURL; + + if (!SyncJob::del(kioURL, useTrash)) + { + TQString errMsg(SyncJob::lastErrorMsg()); + KMessageBox::error(this, errMsg, errMsg); + return; + } + + emit signalFileDeleted(u); + + slotRemoveItem(info); +} + +void LightTableWindow::slotRemoveItem() +{ + if (d->barView->currentItemImageInfo()) + slotRemoveItem(d->barView->currentItemImageInfo()); +} + +void LightTableWindow::slotRemoveItem(ImageInfo* info) +{ + // When either the image from the left or right panel is removed, + // there are various situations to account for. + // To describe them, 4 images A B C D are used + // and the subscript _L and _ R mark the currently + // active item on the left and right panel + + bool leftPanelActive = false; + ImageInfo *curr_linfo = d->previewView->leftImageInfo(); + ImageInfo *curr_rinfo = d->previewView->rightImageInfo(); + ImageInfo *new_linfo = 0; + ImageInfo *new_rinfo = 0; + + TQ_LLONG infoId = info->id(); + + // First determine the next images to the current left and right image: + ImageInfo *next_linfo = 0; + ImageInfo *next_rinfo = 0; + + if (curr_linfo) + { + LightTableBarItem *ltItem = d->barView->findItemByInfo(curr_linfo); + if (ltItem) + { + LightTableBarItem* next = dynamic_cast<LightTableBarItem*>(ltItem->next()); + if (next) + { + next_linfo = next->info(); + } + } + } + + if (curr_rinfo) + { + LightTableBarItem *ltItem = d->barView->findItemByInfo(curr_rinfo); + if (ltItem) + { + LightTableBarItem* next = dynamic_cast<LightTableBarItem*>(ltItem->next()); + if (next) + { + next_rinfo = next->info(); + } + } + } + + d->barView->removeItem(info); + + // Make sure that next_linfo and next_rinfo are still available: + if (!d->barView->findItemByInfo(next_linfo)) + { + next_linfo = 0; + } + if (!d->barView->findItemByInfo(next_rinfo)) + { + next_rinfo = 0; + } + + // removal of the left panel item? + if (curr_linfo) + { + if ( curr_linfo->id() == infoId ) + { + leftPanelActive = true; + // Delete the item A_L of the left panel: + // 1) A_L B_R C D -> B_L C_R D + // 2) A_L B C_R D -> B C_L D_R + // 3) A_L B C D_R -> B_R C D_L + // 4) A_L B_R -> A_L + // some more corner cases: + // 5) A B_L C_R D -> A C_L D_R + // 6) A B_L C_R -> A_R C_L + // 7) A_LR B C D -> B_L C_R D (does not yet work) + // I.e. in 3) we wrap around circularly. + + // When removing the left panel image, + // put the right panel image into the left panel. + // Check if this one is not the same (i.e. also removed). + if (curr_rinfo) + { + if (curr_rinfo->id() != infoId) + { + new_linfo = curr_rinfo; + // Set the right panel to the next image: + new_rinfo = next_rinfo; + // set the right panel active + leftPanelActive = false; + } + } + } + } + + // removal of the right panel item? + if (curr_rinfo) + { + if (curr_rinfo->id() == infoId) + { + // Leave the left panel as the current one + new_linfo = curr_linfo; + // Set the right panel to the next image + new_rinfo = next_rinfo; + } + } + + // Now we deal with the corner cases, where no left or right item exists. + // If the right panel would be set, but not the left-one, then swap + if (!new_linfo && new_rinfo) + { + new_linfo = new_rinfo; + new_rinfo = 0; + leftPanelActive = true; + } + + if (!new_linfo) + { + if (d->barView->countItems() > 0) + { + LightTableBarItem* first = dynamic_cast<LightTableBarItem*>(d->barView->firstItem()); + new_linfo = first->info(); + } + } + + + // Make sure that new_linfo and new_rinfo exist. + // This addresses a crash occuring if the last image is removed + // in the navigate by pairs mode. + if (!d->barView->findItemByInfo(new_linfo)) + { + new_linfo = 0; + } + if (!d->barView->findItemByInfo(new_rinfo)) + { + new_rinfo = 0; + } + + // no right item defined? + if (!new_rinfo) + { + // If there are at least two items, we can find reasonable right image. + if (d->barView->countItems() > 1) + { + // See if there is an item next to the left one: + LightTableBarItem *ltItem = d->barView->findItemByInfo(new_linfo); + LightTableBarItem* next = 0; + // re-check if ltItem is really set + if (ltItem) + { + next = dynamic_cast<LightTableBarItem*>(ltItem->next()); + } + if (next) + { + new_rinfo = next->info(); + } + else + { + // If there is no item to the right of new_linfo + // then we can choose the first item for new_rinfo + // (as we made sure that there are at least two items) + LightTableBarItem* first = dynamic_cast<LightTableBarItem*>(d->barView->firstItem()); + new_rinfo = first->info(); + } + } + } + + // Check if left and right are set to the same + if (new_linfo && new_rinfo) + { + if (new_linfo->id() == new_rinfo->id()) + { + // Only keep the left one + new_rinfo = 0; + } + } + + // If the right panel would be set, but not the left-one, then swap + // (note that this has to be done here again!) + if (!new_linfo && new_rinfo) + { + new_linfo = new_rinfo; + new_rinfo = 0; + leftPanelActive = true; + } + + // set the image for the left panel + if (new_linfo) + { + d->barView->setOnLeftPanel(new_linfo); + slotSetItemOnLeftPanel(new_linfo); + + // make this the selected item if the left was active before + if ( leftPanelActive) + { + LightTableBarItem *ltItem = d->barView->findItemByInfo(new_linfo); + d->barView->setSelectedItem(ltItem); + } + } + else + { + d->previewView->setLeftImageInfo(); + d->leftSidebar->slotNoCurrentItem(); + } + + // set the image for the right panel + if (new_rinfo) + { + d->barView->setOnRightPanel(new_rinfo); + slotSetItemOnRightPanel(new_rinfo); + // make this the selected item if the left was active before + if (!leftPanelActive) + { + LightTableBarItem *ltItem = d->barView->findItemByInfo(new_rinfo); + d->barView->setSelectedItem(ltItem); + } + } + else + { + d->previewView->setRightImageInfo(); + d->rightSidebar->slotNoCurrentItem(); + } + + refreshStatusBar(); +} + +void LightTableWindow::slotEditItem() +{ + if (d->barView->currentItemImageInfo()) + slotEditItem(d->barView->currentItemImageInfo()); +} + +void LightTableWindow::slotEditItem(ImageInfo* info) +{ + ImageWindow *im = ImageWindow::imagewindow(); + ImageInfoList list = d->barView->itemsImageInfoList(); + + im->loadImageInfos(list, info, i18n("Light Table"), true); + + if (im->isHidden()) + im->show(); + else + im->raise(); + + im->setFocus(); +} + +void LightTableWindow::slotZoomTo100Percents() +{ + d->previewView->toggleFitToWindowOr100(); +} + +void LightTableWindow::slotFitToWindow() +{ + d->previewView->fitToWindow(); +} + +void LightTableWindow::slotToggleSlideShow() +{ + TDEConfig* config = kapp->config(); + config->setGroup("ImageViewer Settings"); + bool startWithCurrent = config->readBoolEntry("SlideShowStartCurrent", false); + + SlideShowSettings settings; + settings.exifRotate = AlbumSettings::instance()->getExifRotate(); + settings.delay = config->readNumEntry("SlideShowDelay", 5) * 1000; + settings.printName = config->readBoolEntry("SlideShowPrintName", true); + settings.printDate = config->readBoolEntry("SlideShowPrintDate", false); + settings.printApertureFocal = config->readBoolEntry("SlideShowPrintApertureFocal", false); + settings.printExpoSensitivity = config->readBoolEntry("SlideShowPrintExpoSensitivity", false); + settings.printMakeModel = config->readBoolEntry("SlideShowPrintMakeModel", false); + settings.printComment = config->readBoolEntry("SlideShowPrintComment", false); + settings.loop = config->readBoolEntry("SlideShowLoop", false); + slideShow(startWithCurrent, settings); +} + +void LightTableWindow::slideShow(bool startWithCurrent, SlideShowSettings& settings) +{ + if (!d->barView->countItems()) return; + + DMetadata meta; + int i = 0; + d->cancelSlideShow = false; + + d->statusProgressBar->progressBarMode(StatusProgressBar::CancelProgressBarMode, + i18n("Preparing slideshow. Please wait...")); + + ImageInfoList list = d->barView->itemsImageInfoList(); + + for (ImageInfo *info = list.first() ; !d->cancelSlideShow && info ; info = list.next()) + { + SlidePictureInfo pictInfo; + pictInfo.comment = info->caption(); + + // Perform optimizations: only read pictures metadata if necessary. + if (settings.printApertureFocal || settings.printExpoSensitivity || settings.printMakeModel) + { + meta.load(info->kurl().path()); + pictInfo.photoInfo = meta.getPhotographInformations(); + } + + // In case of dateTime extraction from metadata failed + pictInfo.photoInfo.dateTime = info->dateTime(); + settings.pictInfoMap.insert(info->kurl(), pictInfo); + settings.fileList.append(info->kurl()); + + d->statusProgressBar->setProgressValue((int)((i++/(float)list.count())*100.0)); + kapp->processEvents(); + } + + d->statusProgressBar->progressBarMode(StatusProgressBar::TextMode, TQString()); + refreshStatusBar(); + + if (!d->cancelSlideShow) + { + settings.exifRotate = AlbumSettings::instance()->getExifRotate(); + + SlideShow *slide = new SlideShow(settings); + if (startWithCurrent) + slide->setCurrent(d->barView->currentItemImageInfo()->kurl()); + + slide->show(); + } +} + +void LightTableWindow::slotProgressBarCancelButtonPressed() +{ + d->cancelSlideShow = true; +} + +void LightTableWindow::slotToggleFullScreen() +{ + if (d->fullScreen) // out of fullscreen + { + + setWindowState( windowState() & ~WindowFullScreen ); + menuBar()->show(); + statusBar()->show(); + leftDock()->show(); + rightDock()->show(); + topDock()->show(); + bottomDock()->show(); + + TQObject* obj = child("ToolBar","TDEToolBar"); + + if (obj) + { + TDEToolBar* toolBar = static_cast<TDEToolBar*>(obj); + + if (d->fullScreenAction->isPlugged(toolBar) && d->removeFullScreenButton) + d->fullScreenAction->unplug(toolBar); + + if (toolBar->isHidden()) + showToolBars(); + } + + // -- remove the gui action accels ---- + + unplugActionAccel(d->zoomFitToWindowAction); + + if (d->fullScreen) + { + d->leftSidebar->restore(); + d->rightSidebar->restore(); + } + else + { + d->leftSidebar->backup(); + d->rightSidebar->backup(); + } + + d->fullScreen = false; + } + else // go to fullscreen + { + // hide the menubar and the statusbar + menuBar()->hide(); + statusBar()->hide(); + topDock()->hide(); + leftDock()->hide(); + rightDock()->hide(); + bottomDock()->hide(); + + TQObject* obj = child("ToolBar","TDEToolBar"); + + if (obj) + { + TDEToolBar* toolBar = static_cast<TDEToolBar*>(obj); + + if (d->fullScreenHideToolBar) + { + hideToolBars(); + } + else + { + showToolBars(); + + if ( !d->fullScreenAction->isPlugged(toolBar) ) + { + d->fullScreenAction->plug(toolBar); + d->removeFullScreenButton=true; + } + else + { + // If FullScreen button is enable in toolbar settings + // We don't remove it when we out of fullscreen mode. + d->removeFullScreenButton=false; + } + } + } + + // -- Insert all the gui actions into the accel -- + + plugActionAccel(d->zoomFitToWindowAction); + + if (d->fullScreen) + { + d->leftSidebar->restore(); + d->rightSidebar->restore(); + } + else + { + d->leftSidebar->backup(); + d->rightSidebar->backup(); + } + + showFullScreen(); + d->fullScreen = true; + } +} + +void LightTableWindow::slotEscapePressed() +{ + if (d->fullScreen) + d->fullScreenAction->activate(); +} + +void LightTableWindow::showToolBars() +{ + TQPtrListIterator<TDEToolBar> it = toolBarIterator(); + TDEToolBar* bar; + + for( ; it.current()!=0L ; ++it) + { + bar=it.current(); + + if (bar->area()) + bar->area()->show(); + else + bar->show(); + } +} + +void LightTableWindow::hideToolBars() +{ + TQPtrListIterator<TDEToolBar> it = toolBarIterator(); + TDEToolBar* bar; + + for( ; it.current()!=0L ; ++it) + { + bar=it.current(); + + if (bar->area()) + bar->area()->hide(); + else + bar->hide(); + } +} + +void LightTableWindow::plugActionAccel(TDEAction* action) +{ + if (!action) + return; + + d->accelerators->insert(action->text(), + action->text(), + action->whatsThis(), + action->shortcut(), + action, + TQ_SLOT(activate())); +} + +void LightTableWindow::unplugActionAccel(TDEAction* action) +{ + d->accelerators->remove(action->text()); +} + +void LightTableWindow::slotDonateMoney() +{ + TDEApplication::kApplication()->invokeBrowser("http://www.digikam.org/?q=donation"); +} + +void LightTableWindow::slotContribute() +{ + TDEApplication::kApplication()->invokeBrowser("http://www.digikam.org/?q=contrib"); +} + +void LightTableWindow::slotEditKeys() +{ + KKeyDialog dialog(true, this); + dialog.insert( actionCollection(), i18n( "General" ) ); + dialog.configure(); +} + +void LightTableWindow::slotConfToolbars() +{ + saveMainWindowSettings(TDEGlobal::config(), "LightTable Settings"); + KEditToolbar dlg(factory(), this); + + connect(&dlg, TQ_SIGNAL(newToolbarConfig()), + this, TQ_SLOT(slotNewToolbarConfig())); + + dlg.exec(); +} + +void LightTableWindow::slotNewToolbarConfig() +{ + applyMainWindowSettings(TDEGlobal::config(), "LightTable Settings"); +} + +void LightTableWindow::slotSetup() +{ + Setup setup(this, 0); + + if (setup.exec() != TQDialog::Accepted) + return; + + kapp->config()->sync(); + + applySettings(); +} + +void LightTableWindow::slotLeftZoomFactorChanged(double zoom) +{ + double h = (double)ThumbnailSize::Huge; + double s = (double)ThumbnailSize::Small; + double zmin = d->previewView->leftZoomMin(); + double zmax = d->previewView->leftZoomMax(); + double b = (zmin-(zmax*s/h))/(1-s/h); + double a = (zmax-b)/h; + int size = (int)((zoom - b) /a); + + d->leftZoomBar->setZoomSliderValue(size); + d->leftZoomBar->setZoomTrackerText(i18n("zoom: %1%").arg((int)(zoom*100.0))); + + d->leftZoomBar->setEnableZoomPlus(true); + d->leftZoomBar->setEnableZoomMinus(true); + + if (d->previewView->leftMaxZoom()) + d->leftZoomBar->setEnableZoomPlus(false); + + if (d->previewView->leftMinZoom()) + d->leftZoomBar->setEnableZoomMinus(false); +} + +void LightTableWindow::slotRightZoomFactorChanged(double zoom) +{ + double h = (double)ThumbnailSize::Huge; + double s = (double)ThumbnailSize::Small; + double zmin = d->previewView->rightZoomMin(); + double zmax = d->previewView->rightZoomMax(); + double b = (zmin-(zmax*s/h))/(1-s/h); + double a = (zmax-b)/h; + int size = (int)((zoom - b) /a); + + d->rightZoomBar->setZoomSliderValue(size); + d->rightZoomBar->setZoomTrackerText(i18n("zoom: %1%").arg((int)(zoom*100.0))); + + d->rightZoomBar->setEnableZoomPlus(true); + d->rightZoomBar->setEnableZoomMinus(true); + + if (d->previewView->rightMaxZoom()) + d->rightZoomBar->setEnableZoomPlus(false); + + if (d->previewView->rightMinZoom()) + d->rightZoomBar->setEnableZoomMinus(false); +} + +void LightTableWindow::slotToggleSyncPreview() +{ + d->previewView->setSyncPreview(d->syncPreviewAction->isChecked()); +} + +void LightTableWindow::slotToggleOnSyncPreview(bool t) +{ + d->syncPreviewAction->setEnabled(t); + + if (!t) + { + d->syncPreviewAction->setChecked(false); + } + else + { + if (d->autoSyncPreview) + d->syncPreviewAction->setChecked(true); + } +} + +void LightTableWindow::slotBackward() +{ + ThumbBarItem* curr = d->barView->currentItem(); + ThumbBarItem* last = d->barView->lastItem(); + if (curr) + { + if (curr->prev()) + d->barView->setSelected(curr->prev()); + else + d->barView->setSelected(last); + } +} + +void LightTableWindow::slotForward() +{ + ThumbBarItem* curr = d->barView->currentItem(); + ThumbBarItem* first = d->barView->firstItem(); + if (curr) + { + if (curr->next()) + d->barView->setSelected(curr->next()); + else + d->barView->setSelected(first); + } +} + +void LightTableWindow::slotFirst() +{ + d->barView->setSelected( d->barView->firstItem() ); +} + +void LightTableWindow::slotLast() +{ + d->barView->setSelected( d->barView->lastItem() ); +} + +void LightTableWindow::slotToggleNavigateByPair() +{ + d->barView->setNavigateByPair(d->navigateByPairAction->isChecked()); + d->previewView->setNavigateByPair(d->navigateByPairAction->isChecked()); + slotItemSelected(d->barView->currentItemImageInfo()); +} + +void LightTableWindow::slotRawCameraList() +{ + RawCameraDlg dlg(this); + dlg.exec(); +} + +void LightTableWindow::slotThemeChanged() +{ + TQStringList themes(ThemeEngine::instance()->themeNames()); + int index = themes.findIndex(AlbumSettings::instance()->getCurrentTheme()); + if (index == -1) + index = themes.findIndex(i18n("Default")); + + d->themeMenuAction->setCurrentItem(index); +} + +void LightTableWindow::slotChangeTheme(const TQString& theme) +{ + AlbumSettings::instance()->setCurrentTheme(theme); + ThemeEngine::instance()->slotChangeTheme(theme); +} + +void LightTableWindow::slotShowMenuBar() +{ + if (menuBar()->isVisible()) + menuBar()->hide(); + else + menuBar()->show(); +} + +} // namespace Digikam diff --git a/src/utilities/lighttable/lighttablewindow.h b/src/utilities/lighttable/lighttablewindow.h new file mode 100644 index 00000000..f080e2d1 --- /dev/null +++ b/src/utilities/lighttable/lighttablewindow.h @@ -0,0 +1,162 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-03-05 + * Description : digiKam light table GUI + * + * Copyright (C) 2007-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef LIGHTTABLEWINDOW_H +#define LIGHTTABLEWINDOW_H + +// TQt includes. + +#include <tqstring.h> + +// KDE includes. + +#include <kurl.h> +#include <tdemainwindow.h> + +// Local includes. + +#include "imageinfo.h" + +class TDEAction; + +namespace Digikam +{ + +class SlideShowSettings; +class ThumbBarItem; +class LightTableWindowPriv; + +class LightTableWindow : public TDEMainWindow +{ + TQ_OBJECT + + +public: + + ~LightTableWindow(); + + static LightTableWindow *lightTableWindow(); + static bool lightTableWindowCreated(); + + void loadImageInfos(const ImageInfoList &list, ImageInfo *imageInfoCurrent, bool addTo); + void setLeftRightItems(const ImageInfoList &list, bool addTo); + void applySettings(); + void refreshView(); + +signals: + + void signalFileDeleted(const KURL&); + +public slots: + + void slotItemsUpdated(const KURL::List&); + +private: + + void closeEvent(TQCloseEvent* e); + void setupActions(); + void setupConnections(); + void setupUserArea(); + void setupStatusBar(); + void setupAccelerators(); + void slideShow(bool startWithCurrent, SlideShowSettings& settings); + void showToolBars(); + void hideToolBars(); + void plugActionAccel(TDEAction* action); + void unplugActionAccel(TDEAction* action); + void readSettings(); + void writeSettings(); + void refreshStatusBar(); + + LightTableWindow(); + +private slots: + + void slotBackward(); + void slotForward(); + void slotFirst(); + void slotLast(); + + void slotSetItemLeft(); + void slotSetItemRight(); + void slotSetItemOnLeftPanel(ImageInfo*); + void slotSetItemOnRightPanel(ImageInfo*); + void slotLeftDroppedItems(const ImageInfoList&); + void slotRightDroppedItems(const ImageInfoList&); + + void slotLeftPanelLeftButtonClicked(); + void slotRightPanelLeftButtonClicked(); + + void slotLeftPreviewLoaded(bool); + void slotRightPreviewLoaded(bool); + + void slotLeftZoomFactorChanged(double); + void slotRightZoomFactorChanged(double); + + void slotToggleOnSyncPreview(bool); + void slotToggleSyncPreview(); + void slotToggleNavigateByPair(); + + void slotEditItem(); + void slotEditItem(ImageInfo*); + + void slotDeleteItem(); + void slotDeleteItem(ImageInfo*); + + void slotRemoveItem(); + void slotRemoveItem(ImageInfo*); + + void slotItemSelected(ImageInfo*); + void slotClearItemsList(); + + void slotThumbbarDroppedItems(const ImageInfoList&); + + void slotZoomTo100Percents(); + void slotFitToWindow(); + + void slotProgressBarCancelButtonPressed(); + void slotToggleSlideShow(); + void slotToggleFullScreen(); + void slotEscapePressed(); + void slotDonateMoney(); + void slotContribute(); + void slotRawCameraList(); + void slotEditKeys(); + void slotConfToolbars(); + void slotShowMenuBar(); + void slotNewToolbarConfig(); + void slotSetup(); + + void slotThemeChanged(); + void slotChangeTheme(const TQString& theme); + +private: + + LightTableWindowPriv *d; + + static LightTableWindow *m_instance; +}; + +} // namespace Digikam + +#endif /* LIGHTTABLEWINDOW_H */ diff --git a/src/utilities/lighttable/lighttablewindowprivate.h b/src/utilities/lighttable/lighttablewindowprivate.h new file mode 100644 index 00000000..a96e0b08 --- /dev/null +++ b/src/utilities/lighttable/lighttablewindowprivate.h @@ -0,0 +1,158 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-03-05 + * Description : digiKam light table GUI + * + * Copyright (C) 2007-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqsplitter.h> + +// KDE includes. + +#include <tdeaction.h> +#include <tdeaccel.h> + +// Local includes. + +#include "imagepropertiessidebardb.h" +#include "statusprogressbar.h" +#include "statuszoombar.h" +#include "lighttableview.h" +#include "lighttablebar.h" + +namespace Digikam +{ + +class LightTableWindowPriv +{ + +public: + + LightTableWindowPriv() + { + autoLoadOnRightPanel = true; + autoSyncPreview = true; + fullScreenHideToolBar = false; + fullScreen = false; + removeFullScreenButton = false; + cancelSlideShow = false; + star0 = 0; + star1 = 0; + star2 = 0; + star3 = 0; + star4 = 0; + star5 = 0; + accelerators = 0; + leftSidebar = 0; + rightSidebar = 0; + previewView = 0; + barView = 0; + hSplitter = 0; + vSplitter = 0; + syncPreviewAction = 0; + clearListAction = 0; + setItemLeftAction = 0; + setItemRightAction = 0; + editItemAction = 0; + removeItemAction = 0; + fileDeleteAction = 0; + slideShowAction = 0; + fullScreenAction = 0; + donateMoneyAction = 0; + zoomFitToWindowAction = 0; + zoomTo100percents = 0; + zoomPlusAction = 0; + zoomMinusAction = 0; + statusProgressBar = 0; + leftZoomBar = 0; + rightZoomBar = 0; + forwardAction = 0; + backwardAction = 0; + firstAction = 0; + lastAction = 0; + navigateByPairAction = 0; + rawCameraListAction = 0; + themeMenuAction = 0; + contributeAction = 0; + showMenuBarAction = 0; + } + + bool autoLoadOnRightPanel; + bool autoSyncPreview; + bool fullScreenHideToolBar; + bool fullScreen; + bool removeFullScreenButton; + bool cancelSlideShow; + + TQSplitter *hSplitter; + TQSplitter *vSplitter; + + // Rating actions. + TDEAction *star0; + TDEAction *star1; + TDEAction *star2; + TDEAction *star3; + TDEAction *star4; + TDEAction *star5; + + TDEAction *forwardAction; + TDEAction *backwardAction; + TDEAction *firstAction; + TDEAction *lastAction; + + TDEAction *setItemLeftAction; + TDEAction *setItemRightAction; + TDEAction *clearListAction; + TDEAction *editItemAction; + TDEAction *removeItemAction; + TDEAction *fileDeleteAction; + TDEAction *slideShowAction; + TDEAction *donateMoneyAction; + TDEAction *contributeAction; + TDEAction *zoomPlusAction; + TDEAction *zoomMinusAction; + TDEAction *zoomTo100percents; + TDEAction *zoomFitToWindowAction; + TDEAction *rawCameraListAction; + + TDESelectAction *themeMenuAction; + + TDEToggleAction *fullScreenAction; + TDEToggleAction *syncPreviewAction; + TDEToggleAction *navigateByPairAction; + TDEToggleAction *showMenuBarAction; + + TDEAccel *accelerators; + + LightTableBar *barView; + + LightTableView *previewView; + + StatusZoomBar *leftZoomBar; + StatusZoomBar *rightZoomBar; + + StatusProgressBar *statusProgressBar; + + ImagePropertiesSideBarDB *leftSidebar; + ImagePropertiesSideBarDB *rightSidebar; +}; + +} // namespace Digikam diff --git a/src/utilities/lighttable/lighttablewindowui.rc b/src/utilities/lighttable/lighttablewindowui.rc new file mode 100644 index 00000000..ae7ce721 --- /dev/null +++ b/src/utilities/lighttable/lighttablewindowui.rc @@ -0,0 +1,83 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<gui version="24" name="lighttablewindow" > + +<MenuBar> + + <Menu name="File" ><text>&File</text> + <Action name="lighttable_first" /> + <Action name="lighttable_backward" /> + <Action name="lighttable_forward" /> + <Action name="lighttable_last" /> + <Separator/> + <Action name="lighttable_setitemleft" /> + <Action name="lighttable_setitemright" /> + <Action name="lighttable_edititem" /> + <Separator/> + <Action name="lighttable_removeitem" /> + <Action name="lighttable_clearlist" /> + <Separator/> + <Action name="lighttable_filedelete" /> + <Separator/> + <Action name="lighttable_close" /> + </Menu> + + <Menu name="View" ><text>&View</text> + <Action name="lighttable_fullscreen" /> + <Action name="lighttable_slideshow" /> + <Separator/> + <Action name="lighttable_syncpreview" /> + <Action name="lighttable_navigatebypair" /> + <Separator/> + <Action name="lighttable_zoomplus" /> + <Action name="lighttable_zoomminus" /> + <Action name="lighttable_zoomto100percents" /> + <Action name="lighttable_zoomfit2window" /> + </Menu> + + <Menu name="help" ><text>&Help</text> + <Action name="lighttable_rawcameralist"/> + <Action name="lighttable_donatemoney" /> + <Action name="lighttable_contribute" /> + </Menu> + + <Merge/> + + <Menu name="settings" noMerge="1"><Text>&Settings</Text> + <Merge name="StandardToolBarMenuHandler" /> + <Action name="options_show_menubar"/> + <Action name="options_show_statusbar"/> + <Action name="options_show_toolbar"/> + <Separator/> + <Action name="theme_menu" /> + <Action name="options_configure_keybinding"/> + <Action name="options_configure_toolbars"/> + <Action name="options_configure" /> + </Menu> + +</MenuBar> + +<ToolBar name="ToolBar" ><text>Main Toolbar</text> + <Action name="lighttable_first" /> + <Action name="lighttable_backward" /> + <Action name="lighttable_forward" /> + <Action name="lighttable_last" /> + <Separator/> + <Action name="lighttable_zoomplus" /> + <Action name="lighttable_zoomminus" /> + <Action name="lighttable_zoomfit2window" /> + <Separator/> + <Action name="lighttable_setitemleft" /> + <Action name="lighttable_setitemright" /> + <Action name="lighttable_syncpreview" /> + <Action name="lighttable_navigatebypair" /> + <Separator/> + <Action name="lighttable_fullscreen" /> + <Action name="lighttable_slideshow" /> + <Merge /> + <WeakSeparator/> + <Action name="logo_action" /> +</ToolBar> + +<ActionProperties/> + +</gui> diff --git a/src/utilities/scripts/Makefile.am b/src/utilities/scripts/Makefile.am new file mode 100644 index 00000000..198f6c0d --- /dev/null +++ b/src/utilities/scripts/Makefile.am @@ -0,0 +1,4 @@ +####### This script name should probably be more 'namespaced'. Think about distros putting everything in /usr/bin... +bin_SCRIPTS = digitaglinktree +man_MANS = digitaglinktree.1 + diff --git a/src/utilities/scripts/digitaglinktree b/src/utilities/scripts/digitaglinktree new file mode 100644 index 00000000..8d0d12c9 --- /dev/null +++ b/src/utilities/scripts/digitaglinktree @@ -0,0 +1,568 @@ +#!/usr/bin/perl + +$version="1.6.3"; +# Author: Rainer Krienke, krienke@uni-koblenz.de +# +# Description: +# +# This script will create a linktree for all photos in a digikam +# database that have tags set on them. Tags are used in digikam to +# create virtual folders containing images that all have one or more tags. +# Since other programs do not know about these virtual folders this script +# maps these virtual folders into the filesystem by creating a directory +# for each tag name and then linking from the tag dir to the tagged image in the +# digikam photo directory. +# +# Call this script like: +# digitaglinktree -r /home/user/photos -l /home/user/photos/tags \ +# -d /home/user/photos/digikam.db + + +# Changes +# +# 1.1->1.2: +# Support for hierarchical tags was added. The script can either create +# a hierarchical directory structure for these tags (default) or treat them +# as tags beeing on the same level resulting in a flat dir structure (-f) +# +# 1.2->1.3 +# Added support for multiple tags assigned to one photo. Up to now only +# one tag (the last one found) was used. +# +# 1.3->1.4 +# Bug fix, links with same name in one tag directory were no resolved +# which led to "file exists" errors when trying to create the symbolic link. +# +# 1.4-> 1.6, 2006/08/03 +# Added an option (-A) to archive photos and tag structure in an extra directory +# 1.6-> 1.6.1 2006/08/15 +# Added an option (-C) for archive mode (-A). Added more information to +# the manual page. +# 1.6.1-> 1.6.2 2008/11/24 (Peter Muehlenpfordt, muehlenp@gmx.de) +# Bug fix, subtags with the same name in different branches were merged +# into one directory. +# 1.6.2 -> 1.6.3 2008/11/25 Rainer Krienke, krienke@uni-koblenz.de +# Fixed a bug that shows in some later perl interpreter versions when +# accessing array references with $# operator. If $r is a ref to an array +# then $#{@r} was ok in the past but now $#{r] is correct, perhaps it was +# always correct and the fact that $#{@r} worked was just good luck .... +# + + +# --------------------------------------------------------------------------- +# LICENSE: +# +# 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, 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. +# +# --------------------------------------------------------------------------- + + +use Getopt::Std; +use File::Spec; +use File::Path; + +# Please adapt the sqlite version to your digikam version. +# Do not forget to also install the correct sqlite version: +# Digikam 0.7x needs sqlite2 +# Digikam 0.8x needs sqlite3 + +#$SQLITE="/usr/bin/sqlite"; +$SQLITE="/usr/bin/sqlite3"; +$archiveDirPhotos="Photos"; +$archiveDirLinks="Tags"; + +# +$scriptAuthor="krienke@uni-koblenz.de"; + +# +# check if sqlite can open the database +# +sub checkVersion{ + my($database)=shift; + my(@data, $res); + + if( ! -r $SQLITE ){ + warn "Cannot start sqlite executable \"$SQLITE\". \n", + "Please check if the executable searched is really installed.\n", + "You should also check if the installed version is correct. The name\n", + "of the executable to search for can be set in the head of this script.\n\n"; + exit(1); + } + + @data=`$SQLITE $database .tables 2>&1`; # Try to get the tables information + + if( $? != 0 || grep(/Error:\s*file is encrypted/i, @data) != 0 ){ + warn "Cannot open database \"$database\". Have you installed a current\n", + "version of sqlite? Depending on you digikam version you have to \n", + "install and use sqlite2 (for digikam 0.7x) or sqlite3 (for digikam >= 0.8 )\n", + "Please install the correct version and set the SQLITE variable in the\n", + "script head accordingly \n\n"; + exit(1); + } + + $res=grep(/TagsTree/, @data); + if( $res==0 ){ + return("7") # digikam version 0.7 + }else{ + return("8"); # digikam version 0.8 + } +} + + +# +# Get all images that have tags from digikam database +# refPathNames will contain all photos including path having tags +# The key in refPathNames is the path, value is photos name +# +sub getTaggedImages{ + my($refImg, $database, $rootDir, $refPaths, $refPathNames)=@_; + my($i, $image, $path, $tag, $status, $id, $pid, $tmp); + my($sqliteCmd, @data, $vers, @tags, %tagPath, $tagCount, $refTag); + + $vers=checkVersion($database); # Find out version of digikam + + # Get tag information from database + $sqliteCmd="\"select id,pid,name from tags;\""; + + @data=`$SQLITE $database $sqliteCmd`; + $status=$?; + if( $status == 0 ){ + foreach $i ( @data ){ + chomp($i); + ($id, $pid, $tag)=split(/\|/, $i); + # map tag id data into array + $tags[$id]->{"tag"}="$tag"; + $tags[$id]->{"pid"}="$pid"; + } + + # Now build up the path for tags having parents + # Eg: a tag people might have a subtag friends: people + # |->friends + # We want to find out the complete path for the subtag. For friends this + # would be the path "people/friends". Images with tag friends would then be + # linked in the directory <tagsroot>/people/friends/ + for($i=0; $i<=$#tags; $i++){ + $pid=$tags[$i]->{"pid"}; # Get parent tag id of current tag + $tag=$tags[$i]->{"tag"}; # Start constructing tag path + if( $pid == 0 ){ + $tagPath{$i}=$tag; + next; + } + + while( $pid != 0){ + $tag=$tags[$pid]->{"tag"} . "/$tag"; # add parents tag name to path + $pid=$tags[$pid]->{"pid"}; # look if parent has another parent + } + # Store path constructed + $tagPath{$i}=$tag; + #warn "+ $tag \n"; + } + }else{ + print "Error running SQL-Command \"$sqliteCmd\" on database \"$database\"\n"; + } + + + if( $vers==7 ){ + # SQL to get all tagged images from digikam DB with names of tags + $sqliteCmd="\"select ImageTags.name,Albums.Url,ImageTags.tagid from " . + " ImageTags, Albums,Tags " . + " where Albums.id = ImageTags.dirid; \""; + + }elsif( $vers == 8 ){ + # SQL to get all tagged images from digikam DB with names of tags + $sqliteCmd="\"select Images.name,Albums.url,ImageTags.tagid from" . + " Images,Albums,ImageTags where " . + " Images.dirid=Albums.id AND Images.id=ImageTags.imageid; \""; + }else{ + warn "Unsupported digikam database version. Contact $scriptAuthor \n\n"; + exit(1); + } + + @data=`$SQLITE $database $sqliteCmd`; + $status=$?; + if( $status == 0 ){ + foreach $i ( @data ){ + chomp($i); + ($image, $path, $tag)=split(/\|/, $i); + $refPaths->{"$rootDir$path"}=1; + $refPathNames->{"$rootDir$path/$image"}=1; + + $tmp=$path; + #warn "- $rootDir, $path, $image \n"; + # Enter all subpaths in $path as defined too + do{ + $tmp=~s/\/[^\/]+$//; + $refPaths->{"$rootDir$tmp"}=1; + } while (length($tmp)); + + + $refImg->{"$path/$image"}->{"path"}=$path; + $refImg->{"$path/$image"}->{"image"}=$image; + $refTag=$refImg->{"$path/$image"}->{"tag"}; + # + # One image can have several tags assigned. So we manage + # a list of tags for each image + $tagCount=$#{$refTag}+1; + # The tags name + $refImg->{"$path/$image"}->{"tag"}->[$tagCount]=$tag; + # tags path for sub(sub+)tags + $refImg->{"$path/$image"}->{"tagpath"}->[$tagCount]=$tagPath{$tag}; + } + }else{ + print "Error running SQL-Command \"$sqliteCmd\" on database \"$database\"\n"; + } + + return($status); +} + + +# +# Create a directory with tag-subdirectories containing links to photos having +# tags set. Links can be absolute or relative as well as hard or soft. +# +sub createLinkTree{ + my($refImages)=shift; + my($photoRootDir, $linktreeRootDir, $opt_flat, $opt_absolute, $linkMode)=@_; + my( $i, $j, $cmd, $path, $image, $tag, $qpath, $qtag, $qimage, $linkName, + $tmp, $ret, $sourceDir, $destDir); + my($qtagPath, $relPath); + + if( -d "$linktreeRootDir" ){ + # Remove old backuplinktree + system("rm -rf ${linktreeRootDir}.bak"); + if( $? ){ + die "Cannot run \"rm -rf ${linktreeRootDir}.bak\"\n"; + } + + # rename current tree to .bak + $ret=rename("$linktreeRootDir", "${linktreeRootDir}.bak" ); + if( !$ret ){ + die "Cannot rename \"$linktreeRootDir\" to \"${linktreeRootDir}.bak\"\n"; + } + } + + # Create new to level directory to hold linktree + $ret=mkdir("$linktreeRootDir"); + if( !$ret ){ + die "Cannot mkdir \"$linktreeRootDir\"\n"; + } + # Iterate over all tagged images and create links + foreach $i (keys(%$refImages)){ + # Check that the images in the linktrees themselves are + # ignored in the process of link creation. If we do not do this + # we might get into trouble when the linktree root is inside the + # digikam root dir and so the image links inside the liktree directory + # are indexed by digikam. + next if( -l "${photoRootDir}$i" ); + + $path=$refImages->{$i}->{"path"}; + $image=$refImages->{$i}->{"image"}; + #$qimage=quotemeta($image); + #$qpath=quotemeta($path); + + # Handle all of the tags for the current photo + for( $j=0; $j<=$#{$refImages->{$i}->{"tag"}}; $j++){ + $tag=$refImages->{$i}->{"tagpath"}->[$j]; + #$qtagPath=quotemeta($tagPath); + + # For tags that have subtags there is a path defined in qtagPath + # describing the parentrelationship as directory path like "friends/family/brothers" + # If it is defined we want to use this path instead of the flat tag name like "brothers" + # Doing so results in a directory hirachy that maps the tags parent relationships + # into the filesystem. + if( $opt_flat ){ + # For flat option just use the last subdirectory + $tag=~s/^.+\///; + } + # Create subdirectories needed + if( ! -d "${linktreeRootDir}/$tag" ){ + $ret=mkpath("${linktreeRootDir}/$tag", 0, 0755); + + if( !$ret ){ + die "Cannot mkdir \"${linktreeRootDir}/$tag\"\n"; + } + } + # Avoid conflicts for images with the same name in one tag directory + $linkName=$image; + $tmp=$image; + while( -r "${linktreeRootDir}/$tag/$tmp" ){ + $linkName="_" . $linkName; + $tmp="_" . $tmp; + } + + if(length($opt_absolute)){ + # Create link + $sourceDir="${photoRootDir}$path/$image"; + $destDir="${linktreeRootDir}/$tag/$linkName"; + }else{ + # Get relative path from absolute one + # $rel=File::Spec->abs2rel( $path, $base ) ; + $relPath=""; + $relPath=File::Spec->abs2rel("${photoRootDir}$path", "${linktreeRootDir}/$tag" ) ; + if( !length($relPath)){ + die "Cannot create relative path from \"${linktreeRootDir}/$tag\" to \"${photoRootDir}$path\" . Abort.\n"; + } + # Create link + #print "-> $relPath\n"; + $sourceDir="$relPath/$image"; + $destDir="${linktreeRootDir}/$tag/$linkName"; + } + + if( $linkMode eq "symbolic" ){ + $ret=symlink($sourceDir, $destDir); + if( !$ret ){ + $ret="$!"; + warn "Warning: Failed symbolic linking \"$sourceDir\" to \"$destDir\": ", + "\"$ret\"\n"; + } + }elsif( $linkMode eq "hard" ){ + $ret=link($sourceDir, $destDir); + if( !$ret ){ + $ret="$!"; + warn "Warning: Failed hard linking \"$sourceDir\" to \"$destDir\": ", + "\"$ret\"\n"; + } + }else{ + die "$0: Illegal linkMode: \"$linkMode\". Abort.\n"; + } + + }# for + } +} + + + +# +# Clone a directory into another directory. +# source and destination have to exist +# Files in $srcDir will symbolically or hard linked +# to the $cloneDir according to $cloneMode which can +# be symbolic for symbolic links or hard for hardlinks +# refPathNames contains path for all photos having tags set +# +sub cloneDir{ + local($srcDir, $cloneDir, $linkMode, $cloneMode, $refPaths, $refPathNames)=@_; + local(@entries, $ki, $path, $name, $ret); + + # Read directory entries + opendir( DIR, $srcDir ) || warn "Cannot read directory $srcDir \n"; + local(@entries)=readdir(DIR); + closedir(DIR); + + foreach $k (@entries){ + next if( $k eq '.' ); + next if( $k eq '..'); + next if( $k =~ /digikam.*\.db/o ); + + #warn "-> $srcDir, $k, $cloneDir\n"; + # Recurse into directories + if( -d "$srcDir/$k" ){ + # if cloneMode is "minimal" then only clone directories and files + # with photos that have tags set. + if( $cloneMode ne "minimal" || + defined($refPaths->{"$srcDir/$k"}) ){ + #warn "* $srcDir/$k defined \n"; + mkdir("$cloneDir/$k", 0755); + if( $? ){ + die "Cannot run \"mkdir $cloneDir/$k\"\n"; + } + cloneDir("$srcDir/$k", "$cloneDir/$k", $linkMode, + $cloneMode, $refPaths, $refPathNames ); + } + }else{ + # if cloneMode is "minimal" then only clone directories and files + # with photos that have tags set. + if( $cloneMode ne "minimal" || + defined($refPathNames->{"$srcDir/$k"}) ){ + # link files + if( $linkMode eq "symbolic" ){ + $ret=symlink( "$srcDir/$k", "$cloneDir/$k"); + if( !$ret ){ + $ret="$!"; + warn "Warning: In cloning failed symbolic linking \"$srcDir/$k\" to \"$cloneDir/$k\": ", + "\"$ret\"\n"; + } + }elsif( $linkMode eq "hard" ){ + $ret=link( "$srcDir/$k", "$cloneDir/$k"); + if( !$ret ){ + $ret="$!"; + warn "Warning: In cloning failed hard linking \"$srcDir/$k\" to \"$cloneDir/$k\": ", + "\"$ret\"\n"; + } + }else{ + die "$0: Illegal cloneLinkMode: $linkMode set. Aborting.\n"; + } + } + } + } +} + + +# +# Manage cloning of a complete directory. If the clone directory exists +# a .bak will be created +# Files will be linked (hard/symbolic) according to cloneLinkMode which +# should be "symbolic" or "hard" +# cloneMode can "minimal" or anything else. minimal means only to clone +# those files and directories that have tags set. Anything else means +# to clone the whole photo tree with all files +# +sub runClone{ + my($rootDir, $cloneDir, $photoDir, $cloneMode, $cloneLinkMode, + $refPaths, $refPathNames)=@_; + my($ret); + my($dev_r,$dev_A, $ino,$mode,$nlink,$uid,$gid,$rdev,$size, + $atime,$mtime,$ctime,$blksize,$blocks); + + if( -d "$cloneDir" ){ + # Remove old backuplinktree + system("rm -rf ${cloneDir}.bak"); + if( $? ){ + die "Cannot run \"rm -rf ${cloneDir}.bak\"\n"; + } + + # rename current tree to .bak + $ret=rename("$cloneDir", "${cloneDir}.bak" ); + if( !$ret ){ + die "Cannot rename \"$cloneDir\" to \"${cloneDir}.bak\"\n"; + } + } + $ret=mkdir("$cloneDir", 0755); + if( !$ret ){ + die "Cannot run \"mkdir $cloneDir\"\n"; + } + $ret=mkdir("$cloneDir/$photoDir", 0755); + if( !$ret ){ + die "Cannot run \"mkdir $cloneDir/$photoDir\"\n"; + } + + # Check if root and archive dir are on same filesystem + ($dev_r,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, + $atime,$mtime,$ctime,$blksize,$blocks) = stat($rootDir); + ($dev_A,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, + $atime,$mtime,$ctime,$blksize,$blocks) = stat($cloneDir); + # + if( $dev_r != $dev_A ){ + warn "Error: \"$rootDir\" and \"$cloneDir\" are not on the same filesystem. Please select ", + "an archive directory on the same filesystem like digikams root directory! Abort.\n"; + exit 1; + } + + # Create clone of all entries in $rootDir into $clonedir + cloneDir($rootDir, "$cloneDir/$photoDir", $cloneLinkMode, + $cloneMode, $refPaths, $refPathNames); +} + + +# +# print out usage +# +sub usage{ + print "$0 -r <photoroot> \n", + " -l <taglinkdir> | -A <archivedir> \n", + " -d <digikamdatabasefile> \n", + " [-H|-f|-a|-v|-C] \n", + " -r <rootdir> path to digikams root directory containing all photos\n", + " -l <tagdir> path to directory where the tag directory structure with \n", + " links to the original images should be created \n", + " -d <dbfile> full path to the digikam database file \n", + " -A <ardir> Create selfcontained archive of photos and tags in directory\n", + " ardir. rootdir and arDir have to be on the *same* filesystem!\n", + " -C If -A <archdir> was given this option will put hardlinks of all\n", + " photos in the \"$archiveDirPhotos\" directory not only of those with tags.\n", + " -a Create absolute symbolic links instead of relative ones \n", + " -H Use hard links instead of symbolic links in linktree. \n", + " -f If there are hierarchical tags (tags that have subtags) \n", + " create a flat tag directory hierarchy. So directories for\n", + " subtags are at the same directory level like their parent tags\n", + " -h Print this help \n", + " -v Print scripts version number \n"; + + print "Exit.\n\n"; + exit 1; +} + + +# ------------------------------------------------------------------ +# main +# ------------------------------------------------------------------ +$ret=getopts('HhCar:l:d:A:fv'); + +if( defined($opt_h) ){ + usage(0); +} + +if( defined($opt_v) ){ + print "Version: $version\n"; + exit 0; +} + +usage if( !length($opt_r) || (!length($opt_l) && ! length($opt_A)) + || !length($opt_d) ); + +$opt_f=0 if( ! length($opt_f) ); + +if( ! -d $opt_r ){ + print "Invalid digikam photoroot directory \"$opt_r\". Exit.\n"; + exit 1; +} +if( ! -f $opt_d ){ + print "Digikam database \"$opt_d\" not found. Exit.\n"; + exit 1; +} + +# Remove trailing / +$opt_r=~s/\/$//; +$opt_l=~s/\/$//; +# +$rootDir=$opt_r; +$linkDir=$opt_l; + +if( length($opt_C) ){ + $cloneMode="full"; # Clone all files/dirs +}else{ + $cloneMode="minimal"; # Only clone dirs and files haviong tags not all files +} + +if( length($opt_H) ){ + $linkMode="hard"; # type of link from linktree to phototree +}else{ + $linkMode="symbolic"; # type of link from linktree to phototree +} + +# Extract data needed from database +$ret=getTaggedImages(\%images, $opt_d, $opt_r, \%paths, \%pathNames); + +# Handle Archiving photos and tag structure +# digikams photo dir will be clone as a whole by using +# either soft or hard links ($cloneMode) +if( length($opt_A) ){ + # + runClone($opt_r, $opt_A, $archiveDirPhotos, $cloneMode, "hard", \%paths, \%pathNames); + $rootDir="$opt_A/$archiveDirPhotos"; + $linkDir="$opt_A/$archiveDirLinks"; +} + +if( !$ret && ( length($opt_l) || length($opt_A)) ){ + # When doing hard links we always use absolute linking + if( $linkMode eq "hard" ){ + warn "Will use absolute hard links for linktree in archive mode...\n" if( !length($opt_a) ); + $opt_a="1"; + } + + # Create the link trees for all tagged images + createLinkTree(\%images, $rootDir, $linkDir, $opt_f, $opt_a, $linkMode); +} diff --git a/src/utilities/scripts/digitaglinktree.1 b/src/utilities/scripts/digitaglinktree.1 new file mode 100644 index 00000000..fdfe9e09 --- /dev/null +++ b/src/utilities/scripts/digitaglinktree.1 @@ -0,0 +1,182 @@ +.\" -*-Nroff-*- +.\" +.TH digitaglinktree 1 "16 Aug 2006 " " " "Linux User's Manual" +.SH NAME +digitaglinktree \- Export tag structure of photos in digikam to the filesystem. +.SH SYNOPSIS +.B digitaglinktree +.B -r\fI rootdir\fR + +.B -l\fI taglinkdir\fR +| +.B -A\fI archivedir\fR + +.B -d\fI database\fR + +.B [-H|-f|-a|-v|-C] + +.SH DESCRIPTION +.B "digitaglinktree " +will create a linktree for all photos in a digikam database that have tags set +on them. Tags (like eg. "family", "events", ...) are used in digikam to create +virtual folders containing images that all have one or more tags assigned. +Please note: Photos that have no tags at all assigned are silently ignored by +this script. The program will not modify or even touch your original photos +managed by digikam. + + +The script can be used in two ways: If you call it using +Option -l \fItaglinkdir\fR the script will create the user specified +directory \fItaglinkdir\fR and inside this directory it will create sub +directories for digikam tags set on the photos. Inside these subdirectories it +will finally place symbolic or hard links (see -H) to photos having the tags +in question. As a result you will see the tags of your photos as folders and in +these folders you will find links to your original photos. + + +In this way you can access the collection of all images that share a certain tag +by changing directory to the folder with the tags name created by this script. +This allows you e.g. to run JAlbum a photo album software that needs to find the +pictures to be put into a web album in the filesystem because JAlbum cannot +access digikams virtual folders directly. + + +The second way of calling this script is the so called archive-mode by setting +option -A \fIarchiveDir\fR. + +Archive mode is thought for people who want to archive tagged photos +independently of digikams root directories and the photos therein. This way you +can put your photos and their tag structure in eg. a tar archive and send it to +a friend, who can look at the photos via their tag structure. In this mode the +script creates the directory given as parameter to -A and in this directory two +more subdirectories. One named Photos and a second named Tags. The Photos +directory contains hard links to your original photos, and the Tags directory +contains a subdirectory for each Tag used by any of your photos. Inside this +subdirectory there are links (either symbolic or hard links) to the files in the +Photos directory. This way the archive directory needs nearly no additional +space on your harddisk and you have an archive that allows you or a friend to +easily look at the photos tag structure. + +Another benefit from using this script is that you have kind of a backup of your +tag settings for all of your photos. The backup is simply the directory +structure containing links to the original images that wear the tags. +This could become important if +for whatever reason the digikam.db file gets corrupted or even lost. + +.PP +.SH "COMMAND\-LINE OPTIONS" +.TP +\fB \-r \fIrootdir\fR +\fIrootdir\fR denotes the digikam base directory containing all your photos. + +.TP +\fB \-l\fI taglinkdir\fR +Parameter \fI taglinkdir\fR denotes a directory in which the tag structure of +all your photos stored in +rootdir will be exported to by creating subdirectories for each tag and placing +symbolic links in these subdirectories that point to the original photo wearing +the tags. If calling the script with option -l\fI taglinkDir\fR you also have +to specify options -r \fIrootdir\fR as well as -d \fIdatabase\fR. + +.TP +\fB \-A \fIarchivedirectory\fR +\fIarchivedirectory\fR denotes a directory into which the script will export the photos and their tag +structure. -A has to be used together with option -r \fIrootdir\fR as well as +-d\fI database\fR else the script will terminate. Inside the archive directory +the script will create a Photos and a Tags directory. It will put hard links in +the Photos directory that point to your original photos. By using hard links +you are independent of changes in your digikam root directory but on the other +hand you are limited to one filesystem. So the directory given by +-r \fIrootdir\fR and the directory specified for -A \fIarchivedir\fR have to be one +the same filesystem. The Tags subdirectory will contain links to the files in +the Photos directory. This way you have one archive directory that is completely +self contained. You can tar it, send it to a friend or just put it somewhere +for archivel or backup purposes. Usually only those photos will be archived that +have a digikam tag set on them. By using option -C however you can perform a +complete archive. See -C for more infos. + +.TP +\fB \-d \fIdatabase\fR +\fIdatabase\fR is the complete path including the filename to digikams photo database which +usually can be found in digikams root directory. The files name is usually +digikam.db . + +.TP +\fB \-C\fR +When the script is called with option -A \fIarchivedir\fR only those photos +will be archived (by placing links) in the Photos subdirectory of +\fIarchivedir\fR that have at least one digikam tag set. By setting option -C all +photos will be archived to \fIarchivedir\fR no matter if they have a tag set +or not. Note: This only changes the contents of the Photos subdirectory not of +the Tags subdirectory in the \fIarchivedir\fR directory. + +.TP +\fB \-a \fR +By default the script will try to create relative symbolic links from the +directory \fItaglinkdir\fR set by option -l to the photo files under +\fIrootdir\fR given by option -r. Using this option will result in absolute symbolic +links beeing created instead of relative ones. + +.TP +\fB \-H \fR +By default the script will create soft (symbolic) links from the Tag-Tree to the +photos. By setting option -H the script will use hard links instead. Please note +that hard links can only be created inside one filesystem. So your photos and the Tag tree +have to be one the same filesystem. If not you will see a warning about this problem and the script +will not run. + +.TP +\fB \-f \fR +In digikam photos can have hierachical tags (tags that have subtags). In this case +digitaglinktree would by default add a directory for the tag and a subdirectory for +each of the subtags of this tag. By setting \fB \-f \fR a subtag is treated like a +regular tag just as its parent tag so digitaglinktree will create all subdirectories +for tags and subtags at the same level independent of the tag - subtag hierarchy. + +.TP +\fB \-v \fR +Prints the scripts version number and exits. + + +.SH CONFIGURATION + +The script has to know which version of database is beeing used by digikam. +The script needs this information to find the correct sqlite binary to +start queries to the database. +.sp +You have to configure the script by setting the path to the sqlite binary that +is used by the script to query the digikam database digikam.db. Since older +digikam version use sqlite in version 2, but later digikam 0.80 versions +needs sqlite version 3 you have to take care to install the correct version of +sqlite for the installed digikam version and to set the path to the correct +sqlite executable in the scripts head: +.sp +Choose + +$SQLITE="/usr/bin/sqlite3"; + +for digikam version 0.8x and 0.9x and + +$SQLITE="/usr/bin/sqlite"; + +for digikam version 0.7x. + +.SH EXAMPLE + +A call to digitaglinktree is shown below: + +digiTagLinktree -r /home/user/photos -l /home/user/photos/tags \ + -d /home/user/photos/digikam.db + +In this example digikams photo root denoted by -r is /home/user/photos where all of the photos +can be found that are managed by digikam. The option -l /home/user/photos/tags +tells the script that all the subdirectories and symbolic links will be placed in +the directory /home/user/photos/tags. Because the link directory is +below digikams root directory in this example, you will see a new album in digikam +after running the script that contains the exported tag structure with all the photos inside. +Since only links are used here this tag structure does hardly need any additional space on your +harddisk. + +.SH AUTHORS +.B digitaglinktree +was written by Rainer Krienke <krienke at uni-koblenz.de> diff --git a/src/utilities/setup/Makefile.am b/src/utilities/setup/Makefile.am new file mode 100644 index 00000000..bd6fd66e --- /dev/null +++ b/src/utilities/setup/Makefile.am @@ -0,0 +1,29 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/digikam \ + -I$(top_srcdir)/src/utilities/cameragui \ + -I$(top_srcdir)/src/utilities/batch \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dimg/loaders \ + -I$(top_srcdir)/src/libs/widgets/common \ + $(LIBKIPI_CFLAGS) $(LIBKDCRAW_CFLAGS) $(all_includes) + +noinst_LTLIBRARIES = libsetup.la libshowfotosetup.la + +libsetup_la_SOURCES = cameraselection.cpp setupcamera.cpp \ + setupmime.cpp setupplugins.cpp setupidentity.cpp \ + setupgeneral.cpp setup.cpp \ + setupcollections.cpp setupmetadata.cpp \ + setupeditor.cpp setupmisc.cpp setupicc.cpp \ + setupiofiles.cpp setupdcraw.cpp setupslideshow.cpp \ + setuptooltip.cpp setuplighttable.cpp + +libsetup_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_GPHOTO) + +libshowfotosetup_la_SOURCES = setupiofiles.cpp setupdcraw.cpp \ + setupicc.cpp setupslideshow.cpp + +libshowfotosetup_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + diff --git a/src/utilities/setup/cameraselection.cpp b/src/utilities/setup/cameraselection.cpp new file mode 100644 index 00000000..2cf2635c --- /dev/null +++ b/src/utilities/setup/cameraselection.cpp @@ -0,0 +1,494 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-02-10 + * Description : Camera type selection dialog + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqcombobox.h> +#include <tqvgroupbox.h> +#include <tqlabel.h> +#include <tqpushbutton.h> +#include <tqradiobutton.h> +#include <tqlistview.h> +#include <tqvbuttongroup.h> +#include <tqlayout.h> +#include <tqwhatsthis.h> + +// KDE includes. + +#include <kiconloader.h> +#include <tdeglobalsettings.h> +#include <kactivelabel.h> +#include <kurlrequester.h> +#include <tdelocale.h> +#include <klineedit.h> +#include <kcursor.h> +#include <tdeapplication.h> + +// Local includes. + +#include "searchtextbar.h" +#include "gpcamera.h" +#include "cameraselection.h" +#include "cameraselection.moc" + +namespace Digikam +{ + +class CameraSelectionPriv +{ +public: + + CameraSelectionPriv() + { + listView = 0; + titleEdit = 0; + portButtonGroup = 0; + usbButton = 0; + serialButton = 0; + portPathLabel = 0; + portPathComboBox = 0; + umsMountURL = 0; + searchBar = 0; + } + + TQVButtonGroup *portButtonGroup; + + TQRadioButton *usbButton; + TQRadioButton *serialButton; + + TQLabel *portPathLabel; + + TQComboBox *portPathComboBox; + + TQString UMSCameraNameActual; + TQString UMSCameraNameShown; + TQString PTPCameraNameShown; + + TQStringList serialPortList; + + TQListView *listView; + + KLineEdit *titleEdit; + + KURLRequester *umsMountURL; + + SearchTextBar *searchBar; +}; + +CameraSelection::CameraSelection( TQWidget* parent ) + : KDialogBase(Plain, i18n("Camera Configuration"), + Help|Ok|Cancel, Ok, parent, 0, true, true) +{ + d = new CameraSelectionPriv; + + kapp->setOverrideCursor(KCursor::waitCursor()); + setHelp("cameraselection.anchor", "digikam"); + d->UMSCameraNameActual = TQString("Directory Browse"); // Don't be i18n! + d->UMSCameraNameShown = i18n("Mounted Camera"); + d->PTPCameraNameShown = TQString("USB PTP Class Camera"); + + TQGridLayout* mainBoxLayout = new TQGridLayout(plainPage(), 6, 1, 0, KDialog::spacingHint()); + mainBoxLayout->setColStretch(0, 10); + mainBoxLayout->setRowStretch(6, 10); + + // -------------------------------------------------------------- + + d->listView = new TQListView(plainPage()); + d->listView->addColumn(i18n("Camera List")); + d->listView->setAllColumnsShowFocus(true); + d->listView->setResizeMode(TQListView::LastColumn); + d->listView->setMinimumWidth(350); + TQWhatsThis::add(d->listView, i18n("<p>Select the camera name that you want to use. All " + "default settings on the right panel " + "will be set automatically.</p><p>This list has been generated " + "using the gphoto2 library installed on your computer.</p>")); + + d->searchBar = new SearchTextBar(plainPage(), "CameraSelectionSearchBar"); + + // -------------------------------------------------------------- + + TQVGroupBox* titleBox = new TQVGroupBox( i18n("Camera Title"), plainPage() ); + d->titleEdit = new KLineEdit( titleBox ); + TQWhatsThis::add(d->titleEdit, i18n("<p>Set here the name used in digiKam interface to " + "identify this camera.</p>")); + + // -------------------------------------------------------------- + + d->portButtonGroup = new TQVButtonGroup( i18n("Camera Port Type"), plainPage() ); + d->portButtonGroup->setRadioButtonExclusive( true ); + + d->usbButton = new TQRadioButton( d->portButtonGroup ); + d->usbButton->setText( i18n( "USB" ) ); + TQWhatsThis::add(d->usbButton, i18n("<p>Select this option if your camera is connected to your " + "computer using an USB cable.</p>")); + + d->serialButton = new TQRadioButton( d->portButtonGroup ); + d->serialButton->setText( i18n( "Serial" ) ); + TQWhatsThis::add(d->serialButton, i18n("<p>Select this option if your camera is connected to your " + "computer using a serial cable.</p>")); + + // -------------------------------------------------------------- + + TQVGroupBox* portPathBox = new TQVGroupBox( i18n( "Camera Port Path" ), plainPage() ); + d->portPathLabel = new TQLabel( portPathBox); + d->portPathLabel->setText( i18n( "Note: only for serial port camera" ) ); + + d->portPathComboBox = new TQComboBox( false, portPathBox ); + d->portPathComboBox->setDuplicatesEnabled( false ); + TQWhatsThis::add(d->portPathComboBox, i18n("<p>Select the serial port to use on your computer. " + "This option is only required if you use a serial camera.</p>")); + + // -------------------------------------------------------------- + + TQVGroupBox* umsMountBox = new TQVGroupBox(i18n("Camera Mount Path"), plainPage()); + + TQLabel* umsMountLabel = new TQLabel( umsMountBox ); + umsMountLabel->setText(i18n("Note: only for USB/IEEE mass storage camera")); + + d->umsMountURL = new KURLRequester( TQString("/mnt/camera"), umsMountBox); + d->umsMountURL->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly); + TQWhatsThis::add(d->umsMountURL, i18n("<p>Set here the mount path to use on your computer. This " + "option is only required if you use a <b>USB Mass Storage</b> " + "camera.</p>")); + + // -------------------------------------------------------------- + + TQGroupBox* box2 = new TQGroupBox( 0, TQt::Vertical, plainPage() ); + box2->setFrameStyle( TQFrame::NoFrame ); + TQGridLayout* box2Layout = new TQGridLayout( box2->layout(), 1, 5 ); + + TQLabel* logo = new TQLabel( box2 ); + + TDEIconLoader* iconLoader = TDEApplication::kApplication()->iconLoader(); + logo->setPixmap(iconLoader->loadIcon("digikam", TDEIcon::NoGroup, 64, + TDEIcon::DefaultState, 0, true)); + + KActiveLabel* link = new KActiveLabel(box2); + link->setText(i18n("<p>To set a <b>USB Mass Storage</b> camera<br>" + "(which looks like a removable drive when mounted on your desktop), please<br>" + "use <a href=\"umscamera\">%1</a> from camera list.</p>") + .arg(d->UMSCameraNameShown)); + + KActiveLabel* link2 = new KActiveLabel(box2); + link2->setText(i18n("<p>To set a <b>Generic PTP USB Device</b><br>" + "(which uses the Picture Transfer Protocol), please<br>" + "use <a href=\"ptpcamera\">%1</a> from the camera list.</p>") + .arg(d->PTPCameraNameShown)); + + KActiveLabel* explanation = new KActiveLabel(box2); + explanation->setText(i18n("<p>A complete list of camera settings to use is<br>" + "available at <a href='http://www.teaser.fr/~hfiguiere/linux/digicam.html'>" + "this url</a>.</p>")); + + box2Layout->addMultiCellWidget(logo, 0, 0, 0, 0); + box2Layout->addMultiCellWidget(link, 0, 1, 1, 1); + box2Layout->addMultiCellWidget(link2, 2, 3, 1, 1); + box2Layout->addMultiCellWidget(explanation, 4, 5, 1, 1); + + // -------------------------------------------------------------- + + mainBoxLayout->addMultiCellWidget(d->listView, 0, 5, 0, 0); + mainBoxLayout->addMultiCellWidget(d->searchBar, 6, 6, 0, 0); + mainBoxLayout->addMultiCellWidget(titleBox, 0, 0, 1, 1); + mainBoxLayout->addMultiCellWidget(d->portButtonGroup, 1, 1, 1, 1); + mainBoxLayout->addMultiCellWidget(portPathBox, 2, 2, 1, 1); + mainBoxLayout->addMultiCellWidget(umsMountBox, 3, 3, 1, 1); + mainBoxLayout->addMultiCellWidget(box2, 4, 5, 1, 1); + + // Connections -------------------------------------------------- + + disconnect(link, TQ_SIGNAL(linkClicked(const TQString &)), + link, TQ_SLOT(openLink(const TQString &))); + + connect(link, TQ_SIGNAL(linkClicked(const TQString &)), + this, TQ_SLOT(slotUMSCameraLinkUsed())); + + disconnect(link2, TQ_SIGNAL(linkClicked(const TQString &)), + link2, TQ_SLOT(openLink(const TQString &))); + + connect(link2, TQ_SIGNAL(linkClicked(const TQString &)), + this, TQ_SLOT(slotPTPCameraLinkUsed())); + + connect(d->listView, TQ_SIGNAL(selectionChanged(TQListViewItem *)), + this, TQ_SLOT(slotSelectionChanged(TQListViewItem *))); + + connect(d->portButtonGroup, TQ_SIGNAL(clicked(int)), + this, TQ_SLOT(slotPortChanged())); + + connect(this, TQ_SIGNAL(okClicked()), + this, TQ_SLOT(slotOkClicked())); + + connect(d->searchBar, TQ_SIGNAL(signalTextChanged(const TQString&)), + this, TQ_SLOT(slotSearchTextChanged(const TQString&))); + + // Initialize -------------------------------------------------- + + getCameraList(); + getSerialPortList(); + kapp->restoreOverrideCursor(); +} + +CameraSelection::~CameraSelection() +{ + delete d; +} + +void CameraSelection::slotUMSCameraLinkUsed() +{ + TQListViewItem *item = d->listView->findItem(d->UMSCameraNameShown, 0); + if (item) + { + d->listView->setCurrentItem(item); + d->listView->ensureItemVisible(item); + } +} + +void CameraSelection::slotPTPCameraLinkUsed() +{ + TQListViewItem *item = d->listView->findItem(d->PTPCameraNameShown, 0); + if (item) + { + d->listView->setCurrentItem(item); + d->listView->ensureItemVisible(item); + } +} + +void CameraSelection::setCamera(const TQString& title, const TQString& model, + const TQString& port, const TQString& path) +{ + TQString camModel(model); + + if (camModel == d->UMSCameraNameActual) + camModel = d->UMSCameraNameShown; + + TQListViewItem* item = d->listView->findItem(camModel, 0); + if (!item) return; + + d->listView->setSelected(item, true); + d->listView->ensureItemVisible(item); + + d->titleEdit->setText(title); + + if (port.contains("usb")) + { + d->usbButton->setChecked(true); + slotPortChanged(); + } + else if (port.contains("serial")) + { + d->serialButton->setChecked(true); + + for (int i=0; i<d->portPathComboBox->count(); i++) + { + if (port == d->portPathComboBox->text(i)) + { + d->portPathComboBox->setCurrentItem(i); + break; + } + } + slotPortChanged(); + } + + d->umsMountURL->setURL(path); +} + +void CameraSelection::getCameraList() +{ + int count = 0; + TQStringList clist; + TQString cname; + + GPCamera::getSupportedCameras(count, clist); + + for (int i = 0 ; i < count ; i++) + { + cname = clist[i]; + if (cname == d->UMSCameraNameActual) + new TQListViewItem(d->listView, d->UMSCameraNameShown); + else + new TQListViewItem(d->listView, cname); + } +} + +void CameraSelection::getSerialPortList() +{ + TQStringList plist; + + GPCamera::getSupportedPorts(plist); + + d->serialPortList.clear(); + + for (unsigned int i=0; i<plist.count(); i++) + { + if ((plist[i]).startsWith("serial:")) + d->serialPortList.append(plist[i]); + } +} + +void CameraSelection::slotSelectionChanged(TQListViewItem *item) +{ + if (!item) return; + + TQString model(item->text(0)); + + if (model == d->UMSCameraNameShown) + { + model = d->UMSCameraNameActual; + + d->titleEdit->setText(model); + + d->serialButton->setEnabled(true); + d->serialButton->setChecked(false); + d->serialButton->setEnabled(false); + d->usbButton->setEnabled(true); + d->usbButton->setChecked(false); + d->usbButton->setEnabled(false); + d->portPathComboBox->setEnabled(true); + d->portPathComboBox->insertItem(TQString("NONE"), 0); + d->portPathComboBox->setEnabled(false); + + d->umsMountURL->setEnabled(true); + d->umsMountURL->clear(); + d->umsMountURL->setURL(TQString("/mnt/camera")); + return; + } + else + { + d->umsMountURL->setEnabled(true); + d->umsMountURL->clear(); + d->umsMountURL->setURL(TQString("/")); + d->umsMountURL->setEnabled(false); + } + + d->titleEdit->setText(model); + + TQStringList plist; + GPCamera::getCameraSupportedPorts(model, plist); + + if (plist.contains("serial")) + { + d->serialButton->setEnabled(true); + d->serialButton->setChecked(true); + } + else + { + d->serialButton->setEnabled(true); + d->serialButton->setChecked(false); + d->serialButton->setEnabled(false); + } + + if (plist.contains("usb")) + { + d->usbButton->setEnabled(true); + d->usbButton->setChecked(true); + } + else + { + d->usbButton->setEnabled(true); + d->usbButton->setChecked(false); + d->usbButton->setEnabled(false); + } + + slotPortChanged(); +} + +void CameraSelection::slotPortChanged() +{ + if (d->usbButton->isChecked()) + { + d->portPathComboBox->setEnabled(true); + d->portPathComboBox->clear(); + d->portPathComboBox->insertItem( TQString("usb:"), 0 ); + d->portPathComboBox->setEnabled(false); + return; + } + + if (d->serialButton->isChecked()) + { + d->portPathComboBox->setEnabled(true); + d->portPathComboBox->clear(); + d->portPathComboBox->insertStringList(d->serialPortList); + } +} + +TQString CameraSelection::currentTitle() +{ + return d->titleEdit->text(); +} + +TQString CameraSelection::currentModel() +{ + TQListViewItem* item = d->listView->currentItem(); + if (!item) + return TQString(); + + TQString model(item->text(0)); + if (model == d->UMSCameraNameShown) + model = d->UMSCameraNameActual; + + return model; +} + +TQString CameraSelection::currentPortPath() +{ + return d->portPathComboBox->currentText(); +} + +TQString CameraSelection::currentCameraPath() +{ + return d->umsMountURL->url(); +} + +void CameraSelection::slotOkClicked() +{ + emit signalOkClicked(currentTitle(), currentModel(), + currentPortPath(), currentCameraPath()); +} + +void CameraSelection::slotSearchTextChanged(const TQString& filter) +{ + bool query = false; + TQString search = filter.lower(); + + TQListViewItemIterator it(d->listView); + + for ( ; it.current(); ++it ) + { + TQListViewItem *item = it.current(); + + if (item->text(0).lower().contains(search)) + { + query = true; + item->setVisible(true); + } + else + { + item->setVisible(false); + } + } + + d->searchBar->slotSearchResult(query); +} + +} // namespace Digikam diff --git a/src/utilities/setup/cameraselection.h b/src/utilities/setup/cameraselection.h new file mode 100644 index 00000000..0458f13b --- /dev/null +++ b/src/utilities/setup/cameraselection.h @@ -0,0 +1,88 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-02-10 + * Description : Camera type selection dialog + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef CAMERASELECTION_H +#define CAMERASELECTION_H + +// TQt includes. + +#include <tqstring.h> +#include <tqstringlist.h> + +// KDE includes. + +#include <kdialogbase.h> + +class TQListViewItem; + +namespace Digikam +{ + +class CameraSelectionPriv; + +class CameraSelection : public KDialogBase +{ + TQ_OBJECT + + +public: + + CameraSelection(TQWidget *parent=0); + ~CameraSelection(); + + void setCamera(const TQString& title, const TQString& model, + const TQString& port, const TQString& path); + + TQString currentTitle(); + TQString currentModel(); + TQString currentPortPath(); + TQString currentCameraPath(); + +signals: + + void signalOkClicked(const TQString& title, const TQString& model, + const TQString& port, const TQString& path); + +private: + + void getCameraList(); + void getSerialPortList(); + +private slots: + + void slotPTPCameraLinkUsed(); + void slotUMSCameraLinkUsed(); + void slotSelectionChanged(TQListViewItem *item); + void slotPortChanged(); + void slotOkClicked(); + void slotSearchTextChanged(const TQString&); + +private: + + CameraSelectionPriv *d; +}; + +} // namespace Digikam + +#endif // CAMERASELECTION_H diff --git a/src/utilities/setup/setup.cpp b/src/utilities/setup/setup.cpp new file mode 100644 index 00000000..bd47796f --- /dev/null +++ b/src/utilities/setup/setup.cpp @@ -0,0 +1,266 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-02-03 + * Description : digiKam setup dialog. + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi at pooh.tam.uiuc.edu> + * Copyright (C) 2003-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqtabwidget.h> +#include <tqapplication.h> +#include <tqframe.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kiconloader.h> +#include <tdemessagebox.h> +#include <tdeconfig.h> +#include <tdeapplication.h> + +// Local includes. + +#include "batchthumbsgenerator.h" +#include "setupgeneral.h" +#include "setuptooltip.h" +#include "setupmetadata.h" +#include "setupidentity.h" +#include "setupcollections.h" +#include "setupmime.h" +#include "setuplighttable.h" +#include "setupeditor.h" +#include "setupdcraw.h" +#include "setupiofiles.h" +#include "setupslideshow.h" +#include "setupicc.h" +#include "setupplugins.h" +#include "setupcamera.h" +#include "setupmisc.h" +#include "setup.h" +#include "setup.moc" + +namespace Digikam +{ + +class SetupPrivate +{ +public: + + SetupPrivate() + { + page_general = 0; + page_tooltip = 0; + page_metadata = 0; + page_identity = 0; + page_collections = 0; + page_mime = 0; + page_lighttable = 0; + page_editor = 0; + page_dcraw = 0; + page_iofiles = 0; + page_slideshow = 0; + page_icc = 0; + page_plugins = 0; + page_camera = 0; + page_misc = 0; + + generalPage = 0; + tooltipPage = 0; + metadataPage = 0; + identityPage = 0; + collectionsPage = 0; + mimePage = 0; + lighttablePage = 0; + editorPage = 0; + dcrawPage = 0; + iofilesPage = 0; + slideshowPage = 0; + iccPage = 0; + cameraPage = 0; + miscPage = 0; + pluginsPage = 0; + } + + TQFrame *page_general; + TQFrame *page_tooltip; + TQFrame *page_metadata; + TQFrame *page_identity; + TQFrame *page_collections; + TQFrame *page_mime; + TQFrame *page_lighttable; + TQFrame *page_editor; + TQFrame *page_dcraw; + TQFrame *page_iofiles; + TQFrame *page_slideshow; + TQFrame *page_icc; + TQFrame *page_plugins; + TQFrame *page_camera; + TQFrame *page_misc; + + SetupGeneral *generalPage; + SetupToolTip *tooltipPage; + SetupMetadata *metadataPage; + SetupIdentity *identityPage; + SetupCollections *collectionsPage; + SetupMime *mimePage; + SetupLightTable *lighttablePage; + SetupEditor *editorPage; + SetupDcraw *dcrawPage; + SetupIOFiles *iofilesPage; + SetupSlideShow *slideshowPage; + SetupICC *iccPage; + SetupCamera *cameraPage; + SetupMisc *miscPage; + SetupPlugins *pluginsPage; +}; + +Setup::Setup(TQWidget* parent, const char* name, Setup::Page page) + : KDialogBase(IconList, i18n("Configure"), Help|Ok|Cancel, Ok, parent, + name, true, true ) +{ + d = new SetupPrivate; + setHelp("setupdialog.anchor", "digikam"); + + d->page_general = addPage(i18n("Albums"), i18n("Album Settings"), + BarIcon("folder_image", TDEIcon::SizeMedium)); + d->generalPage = new SetupGeneral(d->page_general, this); + + d->page_collections = addPage(i18n("Collections"), i18n("Album Collections"), + BarIcon("document-open", TDEIcon::SizeMedium)); + d->collectionsPage = new SetupCollections(d->page_collections); + + d->page_identity = addPage(i18n("Identity"), i18n("Default IPTC identity information"), + BarIcon("identity", TDEIcon::SizeMedium)); + d->identityPage = new SetupIdentity(d->page_identity); + + d->page_metadata = addPage(i18n("Metadata"), i18n("Embedded Image Information Management"), + BarIcon("exifinfo", TDEIcon::SizeMedium)); + d->metadataPage = new SetupMetadata(d->page_metadata); + + d->page_tooltip = addPage(i18n("Tool Tip"), i18n("Album Items Tool Tip Settings"), + BarIcon("filetypes", TDEIcon::SizeMedium)); + d->tooltipPage = new SetupToolTip(d->page_tooltip); + + d->page_mime = addPage(i18n("Mime Types"), i18n("File (MIME) Types Settings"), + BarIcon("preferences-system", TDEIcon::SizeMedium)); + d->mimePage = new SetupMime(d->page_mime); + + d->page_lighttable = addPage(i18n("Light Table"), i18n("Light Table Settings"), + BarIcon("lighttable", TDEIcon::SizeMedium)); + d->lighttablePage = new SetupLightTable(d->page_lighttable); + + d->page_editor = addPage(i18n("Image Editor"), i18n("Image Editor General Settings"), + BarIcon("image-x-generic", TDEIcon::SizeMedium)); + d->editorPage = new SetupEditor(d->page_editor); + + d->page_iofiles = addPage(i18n("Save Images"), i18n("Image Editor: Settings for Saving Images Files"), + BarIcon("document-save", TDEIcon::SizeMedium)); + d->iofilesPage = new SetupIOFiles(d->page_iofiles); + + d->page_dcraw = addPage(i18n("RAW decoding"), i18n("RAW Files Decoding Settings"), + BarIcon("kdcraw", TDEIcon::SizeMedium)); + d->dcrawPage = new SetupDcraw(d->page_dcraw); + + d->page_icc = addPage(i18n("Color Management"), i18n("Image Editor Color Management Settings"), + BarIcon("colorize", TDEIcon::SizeMedium)); + d->iccPage = new SetupICC(d->page_icc, this); + + d->page_plugins = addPage(i18n("Kipi Plugins"), i18n("Main Interface Plug-in Settings"), + BarIcon("kipi", TDEIcon::SizeMedium)); + d->pluginsPage = new SetupPlugins(d->page_plugins); + + d->page_slideshow = addPage(i18n("Slide Show"), i18n("Slide Show Settings"), + BarIcon("slideshow", TDEIcon::SizeMedium)); + d->slideshowPage = new SetupSlideShow(d->page_slideshow); + + d->page_camera = addPage(i18n("Cameras"), i18n("Camera Settings"), + BarIcon("digitalcam", TDEIcon::SizeMedium)); + d->cameraPage = new SetupCamera(d->page_camera); + + d->page_misc = addPage(i18n("Miscellaneous"), i18n("Miscellaneous Settings"), + BarIcon("misc", TDEIcon::SizeMedium)); + d->miscPage = new SetupMisc(d->page_misc); + + connect(this, TQ_SIGNAL(okClicked()), + this, TQ_SLOT(slotOkClicked()) ); + + if (page != LastPageUsed) + showPage((int) page); + else + { + TDEConfig* config = kapp->config(); + config->setGroup("General Settings"); + showPage(config->readNumEntry("Setup Page", General)); + } + + show(); +} + +Setup::~Setup() +{ + TDEConfig* config = kapp->config(); + config->setGroup("General Settings"); + config->writeEntry("Setup Page", activePageIndex()); + config->sync(); + delete d; +} + +void Setup::slotOkClicked() +{ + d->generalPage->applySettings(); + d->tooltipPage->applySettings(); + d->metadataPage->applySettings(); + d->identityPage->applySettings(); + d->collectionsPage->applySettings(); + d->mimePage->applySettings(); + d->cameraPage->applySettings(); + d->lighttablePage->applySettings(); + d->editorPage->applySettings(); + d->dcrawPage->applySettings(); + d->iofilesPage->applySettings(); + d->slideshowPage->applySettings(); + d->iccPage->applySettings(); + d->miscPage->applySettings(); + + if (d->metadataPage->exifAutoRotateAsChanged()) + { + TQString msg = i18n("The Exif auto-rotate thumbnails option has been changed.\n" + "Do you want to rebuild all albums' items' thumbnails now?\n\n" + "Note: thumbnail processing can take a while! You can start " + "this job later from the \"Tools\" menu."); + int result = KMessageBox::warningYesNo(this, msg); + if (result != KMessageBox::Yes) + return; + + BatchThumbsGenerator *thumbsGenerator = new BatchThumbsGenerator(this); + thumbsGenerator->exec(); + } + + close(); +} + +SetupPlugins* Setup::kipiPluginsPage() +{ + return d->pluginsPage; +} + +} // namespace Digikam + diff --git a/src/utilities/setup/setup.h b/src/utilities/setup/setup.h new file mode 100644 index 00000000..ce5e57b1 --- /dev/null +++ b/src/utilities/setup/setup.h @@ -0,0 +1,81 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-02-03 + * Description : digiKam setup dialog. + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi at pooh.tam.uiuc.edu> + * Copyright (C) 2003-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUP_H +#define SETUP_H + +// KDE includes. + +#include <kdialogbase.h> + +namespace Digikam +{ + +class SetupPlugins; +class SetupPrivate; + +class Setup : public KDialogBase +{ + TQ_OBJECT + + +public: + + enum Page + { + LastPageUsed = -1, + General = 0, + ToolTip, + Metadata, + Identify, + Collections, + Mime, + LightTable, + Editor, + Dcraw, + IOFiles, + Slideshow, + IccProfiles, + KipiPlugins, + Camera, + Miscellaneous + }; + + Setup(TQWidget* parent=0, const char* name=0, Page page=LastPageUsed); + ~Setup(); + + SetupPlugins *kipiPluginsPage(); + +private slots: + + void slotOkClicked(); + +private: + + SetupPrivate* d; +}; + +} // namespace Digikam + +#endif // SETUP_H diff --git a/src/utilities/setup/setupcamera.cpp b/src/utilities/setup/setupcamera.cpp new file mode 100644 index 00000000..1b8d0bb8 --- /dev/null +++ b/src/utilities/setup/setupcamera.cpp @@ -0,0 +1,315 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-02-10 + * Description : camera setup tab. + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqgroupbox.h> +#include <tqpushbutton.h> +#include <tqlayout.h> +#include <tqwhatsthis.h> +#include <tqtooltip.h> +#include <tqdatetime.h> +#include <tqlistview.h> + +// KDE includes. + +#include <tdelocale.h> +#include <tdemessagebox.h> +#include <kurllabel.h> +#include <kiconloader.h> +#include <tdeglobalsettings.h> +#include <kstandarddirs.h> +#include <kcursor.h> +#include <tdeapplication.h> + +// Local includes. + +#include "cameraselection.h" +#include "cameralist.h" +#include "cameratype.h" +#include "gpcamera.h" +#include "setupcamera.h" +#include "setupcamera.moc" + +namespace Digikam +{ +class SetupCameraPriv +{ +public: + + SetupCameraPriv() + { + listView = 0; + addButton = 0; + removeButton = 0; + editButton = 0; + autoDetectButton = 0; + } + + TQPushButton *addButton; + TQPushButton *removeButton; + TQPushButton *editButton; + TQPushButton *autoDetectButton; + + TQListView *listView; +}; + +SetupCamera::SetupCamera( TQWidget* parent ) + : TQWidget( parent ) +{ + d = new SetupCameraPriv; + + TQVBoxLayout *mainLayout = new TQVBoxLayout(parent); + TQGridLayout* groupBoxLayout = new TQGridLayout( this, 2, 5, 0, KDialog::spacingHint() ); + + d->listView = new TQListView( this ); + d->listView->addColumn( i18n("Title") ); + d->listView->addColumn( i18n("Model") ); + d->listView->addColumn( i18n("Port") ); + d->listView->addColumn( i18n("Path") ); + d->listView->addColumn( "Last Access Date", 0 ); // No i18n here. Hidden column with the last access date. + d->listView->setAllColumnsShowFocus(true); + TQWhatsThis::add( d->listView, i18n("<p>Here you can see the digital camera list used by digiKam " + "via the Gphoto interface.")); + + // ------------------------------------------------------------- + + d->addButton = new TQPushButton( this ); + d->removeButton = new TQPushButton( this ); + d->editButton = new TQPushButton( this ); + d->autoDetectButton = new TQPushButton( this ); + + d->addButton->setText( i18n( "&Add..." ) ); + d->addButton->setIconSet(SmallIcon("add")); + d->removeButton->setText( i18n( "&Remove" ) ); + d->removeButton->setIconSet(SmallIcon("remove")); + d->editButton->setText( i18n( "&Edit..." ) ); + d->editButton->setIconSet(SmallIcon("configure")); + d->autoDetectButton->setText( i18n( "Auto-&Detect" ) ); + d->autoDetectButton->setIconSet(SmallIcon("edit-find")); + d->removeButton->setEnabled(false); + d->editButton->setEnabled(false); + + TQSpacerItem* spacer = new TQSpacerItem( 20, 20, TQSizePolicy::Minimum, TQSizePolicy::Expanding ); + + KURLLabel *gphotoLogoLabel = new KURLLabel(this); + gphotoLogoLabel->setText(TQString()); + gphotoLogoLabel->setURL("http://www.gphoto.org"); + TDEGlobal::dirs()->addResourceType("logo-gphoto", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("logo-gphoto", "logo-gphoto.png"); + gphotoLogoLabel->setPixmap( TQPixmap( directory + "logo-gphoto.png" ) ); + TQToolTip::add(gphotoLogoLabel, i18n("Visit Gphoto project website")); + + groupBoxLayout->setAlignment( TQt::AlignTop ); + groupBoxLayout->addMultiCellWidget( d->listView, 0, 5, 0, 0 ); + groupBoxLayout->addWidget( d->addButton, 0, 1 ); + groupBoxLayout->addWidget( d->removeButton, 1, 1 ); + groupBoxLayout->addWidget( d->editButton, 2, 1 ); + groupBoxLayout->addWidget( d->autoDetectButton, 3, 1 ); + groupBoxLayout->addItem( spacer, 4, 1 ); + groupBoxLayout->addWidget( gphotoLogoLabel, 5, 1 ); + + adjustSize(); + mainLayout->addWidget(this); + + // ------------------------------------------------------------- + + connect(gphotoLogoLabel, TQ_SIGNAL(leftClickedURL(const TQString&)), + this, TQ_SLOT(processGphotoURL(const TQString&))); + + connect(d->listView, TQ_SIGNAL(selectionChanged()), + this, TQ_SLOT(slotSelectionChanged())); + + connect(d->addButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotAddCamera())); + + connect(d->removeButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotRemoveCamera())); + + connect(d->editButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotEditCamera())); + + connect(d->autoDetectButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotAutoDetectCamera())); + + // Add cameras -------------------------------------- + + CameraList* clist = CameraList::instance(); + + if (clist) + { + TQPtrList<CameraType>* cl = clist->cameraList(); + + for (CameraType *ctype = cl->first(); ctype; + ctype = cl->next()) + { + new TQListViewItem(d->listView, ctype->title(), ctype->model(), + ctype->port(), ctype->path(), + ctype->lastAccess().toString(TQt::ISODate)); + } + } +} + +SetupCamera::~SetupCamera() +{ + delete d; +} + +void SetupCamera::processGphotoURL(const TQString& url) +{ + TDEApplication::kApplication()->invokeBrowser(url); +} + +void SetupCamera::slotSelectionChanged() +{ + TQListViewItem *item = d->listView->selectedItem(); + + if (!item) + { + d->removeButton->setEnabled(false); + d->editButton->setEnabled(false); + return; + } + + d->removeButton->setEnabled(true); + d->editButton->setEnabled(true); +} + +void SetupCamera::slotAddCamera() +{ + CameraSelection *select = new CameraSelection; + + connect(select, TQ_SIGNAL(signalOkClicked(const TQString&, const TQString&, + const TQString&, const TQString&)), + this, TQ_SLOT(slotAddedCamera(const TQString&, const TQString&, + const TQString&, const TQString&))); + + select->show(); +} + +void SetupCamera::slotRemoveCamera() +{ + TQListViewItem *item = d->listView->currentItem(); + if (!item) return; + + delete item; +} + +void SetupCamera::slotEditCamera() +{ + TQListViewItem *item = d->listView->currentItem(); + if (!item) return; + + CameraSelection *select = new CameraSelection; + select->setCamera(item->text(0), item->text(1), item->text(2), item->text(3)); + + connect(select, TQ_SIGNAL(signalOkClicked(const TQString&, const TQString&, + const TQString&, const TQString&)), + this, TQ_SLOT(slotEditedCamera(const TQString&, const TQString&, + const TQString&, const TQString&))); + + select->show(); +} + +void SetupCamera::slotAutoDetectCamera() +{ + TQString model, port; + + kapp->setOverrideCursor( KCursor::waitCursor() ); + int ret = GPCamera::autoDetect(model, port); + kapp->restoreOverrideCursor(); + + if (ret != 0) + { + KMessageBox::error(this,i18n("Failed to auto-detect camera.\n" + "Please check if your camera is turned on " + "and retry or try setting it manually.")); + return; + } + + // NOTE: See note in digikam/digikam/cameralist.cpp + if (port.startsWith("usb:")) + port = "usb:"; + + if (d->listView->findItem(model, 1)) + { + KMessageBox::information(this, i18n("Camera '%1' (%2) is already in list.").arg(model).arg(port)); + } + else + { + KMessageBox::information(this, i18n("Found camera '%1' (%2) and added it to the list.") + .arg(model).arg(port)); + new TQListViewItem(d->listView, model, model, port, "/", + TQDateTime::currentDateTime().toString(TQt::ISODate)); + } +} + +void SetupCamera::slotAddedCamera(const TQString& title, const TQString& model, + const TQString& port, const TQString& path) +{ + new TQListViewItem(d->listView, title, model, port, path, + TQDateTime::currentDateTime().toString(TQt::ISODate)); +} + +void SetupCamera::slotEditedCamera(const TQString& title, const TQString& model, + const TQString& port, const TQString& path) +{ + TQListViewItem *item = d->listView->currentItem(); + if (!item) return; + + item->setText(0, title); + item->setText(1, model); + item->setText(2, port); + item->setText(3, path); +} + +void SetupCamera::applySettings() +{ + CameraList* clist = CameraList::instance(); + + if (clist) + { + clist->clear(); + + TQListViewItemIterator it(d->listView); + + for ( ; it.current(); ++it ) + { + TQListViewItem *item = it.current(); + TQDateTime lastAccess = TQDateTime::currentDateTime(); + + if (!item->text(4).isEmpty()) + lastAccess = TQDateTime::fromString(item->text(4), TQt::ISODate); + + CameraType *ctype = new CameraType(item->text(0), item->text(1), item->text(2), + item->text(3), lastAccess); + clist->insert(ctype); + } + + clist->save(); + } +} + +} // namespace Digikam + diff --git a/src/utilities/setup/setupcamera.h b/src/utilities/setup/setupcamera.h new file mode 100644 index 00000000..f0df8a71 --- /dev/null +++ b/src/utilities/setup/setupcamera.h @@ -0,0 +1,73 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-02-10 + * Description : camera setup tab. + * + * Copyright (C) 2003-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2003-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUPCAMERA_H +#define SETUPCAMERA_H + +// TQt includes. + +#include <tqwidget.h> + +namespace Digikam +{ + +class SetupCameraPriv; + +class SetupCamera : public TQWidget +{ + TQ_OBJECT + + +public: + + SetupCamera( TQWidget* parent = 0 ); + ~SetupCamera(); + + void applySettings(); + +private slots: + + void processGphotoURL(const TQString& url); + + void slotSelectionChanged(); + + void slotAddCamera(); + void slotRemoveCamera(); + void slotEditCamera(); + void slotAutoDetectCamera(); + + void slotAddedCamera(const TQString& title, const TQString& model, + const TQString& port, const TQString& path); + void slotEditedCamera(const TQString& title, const TQString& model, + const TQString& port, const TQString& path); + +private: + + SetupCameraPriv* d; + +}; + +} // namespace Digikam + +#endif // SETUPCAMERA_H diff --git a/src/utilities/setup/setupcollections.cpp b/src/utilities/setup/setupcollections.cpp new file mode 100644 index 00000000..10a25c31 --- /dev/null +++ b/src/utilities/setup/setupcollections.cpp @@ -0,0 +1,218 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-01-02 + * Description : collection setup tab. + * + * Copyright (C) 2004-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlayout.h> +#include <tqvbuttongroup.h> +#include <tqvgroupbox.h> +#include <tqhgroupbox.h> +#include <tqgroupbox.h> +#include <tqradiobutton.h> +#include <tqcheckbox.h> +#include <tqlabel.h> +#include <tqlineedit.h> +#include <tqpushbutton.h> +#include <tqdir.h> +#include <tqwhatsthis.h> + +// KDE includes. + +#include <tdelistbox.h> +#include <tdelocale.h> +#include <kdialog.h> +#include <tdefiledialog.h> +#include <kurl.h> +#include <tdemessagebox.h> +#include <kiconloader.h> +#include <tdeversion.h> + +#if KDE_IS_VERSION(3,2,0) +#include <kinputdialog.h> +#else +#include <klineeditdlg.h> +#endif + +// Local includes. + +#include "thumbnailsize.h" +#include "albumsettings.h" +#include "setupcollections.h" +#include "setupcollections.moc" + +namespace Digikam +{ + +class SetupCollectionsPriv +{ +public: + + SetupCollectionsPriv() + { + albumCollectionBox = 0; + addCollectionButton = 0; + delCollectionButton = 0; + } + + TQListBox *albumCollectionBox; + + TQPushButton *addCollectionButton; + TQPushButton *delCollectionButton; +}; + +SetupCollections::SetupCollections(TQWidget* parent ) + : TQWidget(parent) +{ + d = new SetupCollectionsPriv; + + TQVBoxLayout *mainLayout = new TQVBoxLayout(parent); + TQGridLayout *collectionGroupLayout = new TQGridLayout( this, 2, 5, 0, KDialog::spacingHint() ); + + // -------------------------------------------------------- + + d->albumCollectionBox = new TDEListBox(this); + TQWhatsThis::add( d->albumCollectionBox, i18n("<p>You can add or remove Album " + "collection types here to improve how " + "your Albums are sorted in digiKam.")); + + d->albumCollectionBox->setVScrollBarMode(TQScrollView::AlwaysOn); + + d->addCollectionButton = new TQPushButton( i18n("&Add..."), this); + d->delCollectionButton = new TQPushButton( i18n("&Delete"), this); + + d->addCollectionButton->setIconSet(SmallIcon("add")); + d->delCollectionButton->setIconSet(SmallIcon("remove")); + d->delCollectionButton->setEnabled(false); + + TQSpacerItem* spacer = new TQSpacerItem( 20, 20, TQSizePolicy::Minimum, TQSizePolicy::Expanding ); + + collectionGroupLayout->setAlignment( TQt::AlignTop ); + collectionGroupLayout->addMultiCellWidget( d->albumCollectionBox, 0, 4, 0, 0 ); + collectionGroupLayout->addWidget( d->addCollectionButton, 0, 1); + collectionGroupLayout->addWidget( d->delCollectionButton, 1, 1); + collectionGroupLayout->addItem( spacer, 4, 1 ); + + // -------------------------------------------------------- + + connect(d->albumCollectionBox, TQ_SIGNAL(selectionChanged()), + this, TQ_SLOT(slotCollectionSelectionChanged())); + + connect(d->addCollectionButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotAddCollection())); + + connect(d->delCollectionButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotDelCollection())); + + // -------------------------------------------------------- + + readSettings(); + adjustSize(); + mainLayout->addWidget(this); +} + +SetupCollections::~SetupCollections() +{ + delete d; +} + +void SetupCollections::applySettings() +{ + AlbumSettings* settings = AlbumSettings::instance(); + + if (!settings) return; + + TQStringList collectionList; + + for (TQListBoxItem *item = d->albumCollectionBox->firstItem(); + item; item = item->next()) + { + collectionList.append(item->text()); + } + + settings->setAlbumCollectionNames(collectionList); + + settings->saveSettings(); +} + +void SetupCollections::readSettings() +{ + AlbumSettings* settings = AlbumSettings::instance(); + + if (!settings) return; + + d->albumCollectionBox->insertStringList(settings->getAlbumCollectionNames()); +} + +void SetupCollections::slotCollectionSelectionChanged() +{ + if (d->albumCollectionBox->currentItem() != -1) + d->delCollectionButton->setEnabled(true); + else + d->delCollectionButton->setEnabled(false); +} + +void SetupCollections::slotAddCollection() +{ + bool ok; + +#if KDE_IS_VERSION(3,2,0) + TQString newCollection = + KInputDialog::getText(i18n("New Collection Name"), + i18n("Enter new collection name:"), + TQString(), &ok, this); +#else + TQString newCollection = + KLineEditDlg::getText(i18n("New Collection Name"), + i18n("Enter new collection name:"), + TQString(), &ok, this); +#endif + + if (!ok) return; + + bool found = false; + for (TQListBoxItem *item = d->albumCollectionBox->firstItem(); + item; item = item->next()) + { + if (newCollection == item->text()) + { + found = true; + break; + } + } + + if (!found) + d->albumCollectionBox->insertItem(newCollection); +} + +void SetupCollections::slotDelCollection() +{ + int index = d->albumCollectionBox->currentItem(); + if (index == -1) + return; + + TQListBoxItem* item = d->albumCollectionBox->item(index); + if (!item) return; + delete item; +} + +} // namespace Digikam diff --git a/src/utilities/setup/setupcollections.h b/src/utilities/setup/setupcollections.h new file mode 100644 index 00000000..c65b93e1 --- /dev/null +++ b/src/utilities/setup/setupcollections.h @@ -0,0 +1,66 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-01-02 + * Description : collection setup tab. + * + * Copyright (C) 2004-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUPCOLLECTIONS_H +#define SETUPCOLLECTIONS_H + +// TQt includes. + +#include <tqwidget.h> + +namespace Digikam +{ + +class SetupCollectionsPriv; + +class SetupCollections : public TQWidget +{ + TQ_OBJECT + + +public: + + SetupCollections(TQWidget* parent = 0); + ~SetupCollections(); + + void applySettings(); + +private: + + void readSettings(); + +private slots: + + void slotCollectionSelectionChanged(); + void slotAddCollection(); + void slotDelCollection(); + +private: + + SetupCollectionsPriv* d; + +}; + +} // namespace Digikam + +#endif // SETUPCOLLECTIONS_H diff --git a/src/utilities/setup/setupdcraw.cpp b/src/utilities/setup/setupdcraw.cpp new file mode 100644 index 00000000..57f9cb2d --- /dev/null +++ b/src/utilities/setup/setupdcraw.cpp @@ -0,0 +1,150 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-02-06 + * Description : setup RAW decoding settings. + * + * Copyright (C) 2007-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlayout.h> +#include <tqlabel.h> +#include <tqcolor.h> +#include <tqhbox.h> +#include <tqvgroupbox.h> +#include <tqlabel.h> +#include <tqwhatsthis.h> +#include <tqcheckbox.h> +#include <tqcombobox.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kiconloader.h> +#include <kdialog.h> +#include <tdeconfig.h> +#include <tdeapplication.h> + +// LibKDcraw includes. + +#include <libkdcraw/version.h> +#include <libkdcraw/dcrawsettingswidget.h> + +// Local includes. + +#include "drawdecoding.h" +#include "setupdcraw.h" +#include "setupdcraw.moc" + +using namespace KDcrawIface; + +namespace Digikam +{ + +class SetupDcrawPriv +{ +public: + + + SetupDcrawPriv() + { + dcrawSettings = 0; + } + + KDcrawIface::DcrawSettingsWidget *dcrawSettings; +}; + +SetupDcraw::SetupDcraw(TQWidget* parent ) + : TQWidget(parent) +{ + d = new SetupDcrawPriv; + TQVBoxLayout *layout = new TQVBoxLayout(parent, 0, KDialog::spacingHint()); + d->dcrawSettings = new DcrawSettingsWidget(parent, DcrawSettingsWidget::SIXTEENBITS); + d->dcrawSettings->setItemIconSet(0, SmallIconSet("kdcraw")); + d->dcrawSettings->setItemIconSet(1, SmallIconSet("whitebalance")); + d->dcrawSettings->setItemIconSet(2, SmallIconSet("lensdistortion")); + layout->addWidget(d->dcrawSettings); + layout->addStretch(); + + connect(d->dcrawSettings, TQ_SIGNAL(signalSixteenBitsImageToggled(bool)), + this, TQ_SLOT(slotSixteenBitsImageToggled(bool))); + + readSettings(); +} + +SetupDcraw::~SetupDcraw() +{ + delete d; +} + +void SetupDcraw::slotSixteenBitsImageToggled(bool) +{ + // Dcraw do not provide a way to set brigness of image in 16 bits color depth. + // We always set on this option. We drive brightness adjustment in digiKam Raw image loader. + d->dcrawSettings->setEnabledBrightnessSettings(true); +} + +void SetupDcraw::applySettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("ImageViewer Settings"); + config->writeEntry("SixteenBitsImage", d->dcrawSettings->sixteenBits()); + config->writeEntry("WhiteBalance", d->dcrawSettings->whiteBalance()); + config->writeEntry("CustomWhiteBalance", d->dcrawSettings->customWhiteBalance()); + config->writeEntry("CustomWhiteBalanceGreen", d->dcrawSettings->customWhiteBalanceGreen()); + config->writeEntry("RGBInterpolate4Colors", d->dcrawSettings->useFourColor()); + config->writeEntry("DontStretchPixels", d->dcrawSettings->useDontStretchPixels()); + config->writeEntry("EnableNoiseReduction", d->dcrawSettings->useNoiseReduction()); + config->writeEntry("NRThreshold", d->dcrawSettings->NRThreshold()); + config->writeEntry("EnableCACorrection", d->dcrawSettings->useCACorrection()); + config->writeEntry("caRedMultiplier", d->dcrawSettings->caRedMultiplier()); + config->writeEntry("caBlueMultiplier", d->dcrawSettings->caBlueMultiplier()); + config->writeEntry("UnclipColors", d->dcrawSettings->unclipColor()); + config->writeEntry("RAWBrightness", d->dcrawSettings->brightness()); + config->writeEntry("RAWQuality", d->dcrawSettings->quality()); + config->writeEntry("MedianFilterPasses", d->dcrawSettings->medianFilterPasses()); + config->sync(); +} + +void SetupDcraw::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("ImageViewer Settings"); + d->dcrawSettings->setSixteenBits(config->readBoolEntry("SixteenBitsImage", false)); + d->dcrawSettings->setNoiseReduction(config->readBoolEntry("EnableNoiseReduction", false)); + d->dcrawSettings->setNRThreshold(config->readNumEntry("NRThreshold", 100)); + d->dcrawSettings->setUseCACorrection(config->readBoolEntry("EnableCACorrection", false)); + d->dcrawSettings->setcaRedMultiplier(config->readDoubleNumEntry("caRedMultiplier", 1.0)); + d->dcrawSettings->setcaBlueMultiplier(config->readDoubleNumEntry("caBlueMultiplier", 1.0)); + d->dcrawSettings->setDontStretchPixels(config->readBoolEntry("DontStretchPixels", false)); + d->dcrawSettings->setUnclipColor(config->readNumEntry("UnclipColors", 0)); + d->dcrawSettings->setWhiteBalance((DRawDecoding::WhiteBalance) + config->readNumEntry("WhiteBalance", + DRawDecoding::CAMERA)); + d->dcrawSettings->setCustomWhiteBalance(config->readNumEntry("CustomWhiteBalance", 6500)); + d->dcrawSettings->setCustomWhiteBalanceGreen(config->readDoubleNumEntry("CustomWhiteBalanceGreen", 1.0)); + d->dcrawSettings->setFourColor(config->readBoolEntry("RGBInterpolate4Colors", false)); + d->dcrawSettings->setQuality((DRawDecoding::DecodingQuality) + config->readNumEntry("RAWQuality", + DRawDecoding::BILINEAR)); + d->dcrawSettings->setBrightness(config->readDoubleNumEntry("RAWBrightness", 1.0)); + d->dcrawSettings->setMedianFilterPasses(config->readNumEntry("MedianFilterPasses", 0)); +} + +} // namespace Digikam diff --git a/src/utilities/setup/setupdcraw.h b/src/utilities/setup/setupdcraw.h new file mode 100644 index 00000000..835199f4 --- /dev/null +++ b/src/utilities/setup/setupdcraw.h @@ -0,0 +1,67 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-02-06 + * Description : setup RAW decoding settings. + * + * Copyright (C) 2007-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUPDCRAW_H +#define SETUPDCRAW_H + +// TQt includes. + +#include <tqwidget.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class SetupDcrawPriv; + +class DIGIKAM_EXPORT SetupDcraw : public TQWidget +{ + TQ_OBJECT + + +public: + + SetupDcraw(TQWidget* parent = 0); + ~SetupDcraw(); + + void applySettings(); + +private: + + void readSettings(); + +private slots: + + void slotSixteenBitsImageToggled(bool); + +private: + + SetupDcrawPriv* d; +}; + +} // namespace Digikam + +#endif // SETUPDCRAW_H diff --git a/src/utilities/setup/setupeditor.cpp b/src/utilities/setup/setupeditor.cpp new file mode 100644 index 00000000..337b5655 --- /dev/null +++ b/src/utilities/setup/setupeditor.cpp @@ -0,0 +1,176 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-03 + * Description : setup Image Editor tab. + * + * Copyright (C) 2004-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlayout.h> +#include <tqcolor.h> +#include <tqhbox.h> +#include <tqvgroupbox.h> +#include <tqlabel.h> +#include <tqwhatsthis.h> +#include <tqcheckbox.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kdialog.h> +#include <kcolorbutton.h> +#include <knuminput.h> +#include <tdeconfig.h> +#include <tdeapplication.h> + +// Local includes. + +#include "setupeditor.h" +#include "setupeditor.moc" + +namespace Digikam +{ +class SetupEditorPriv +{ +public: + + SetupEditorPriv() + { + hideToolBar = 0; + themebackgroundColor = 0; + backgroundColor = 0; + colorBox = 0; + overExposureColor = 0; + underExposureColor = 0; + useRawImportTool = 0; + } + + TQHBox *colorBox; + + TQCheckBox *hideToolBar; + TQCheckBox *themebackgroundColor; + TQCheckBox *useRawImportTool; + + KColorButton *backgroundColor; + KColorButton *underExposureColor; + KColorButton *overExposureColor; +}; + +SetupEditor::SetupEditor(TQWidget* parent ) + : TQWidget(parent) +{ + d = new SetupEditorPriv; + TQVBoxLayout *layout = new TQVBoxLayout( parent, 0, KDialog::spacingHint() ); + + // -------------------------------------------------------- + + TQVGroupBox *interfaceOptionsGroup = new TQVGroupBox(i18n("Interface Options"), parent); + + d->themebackgroundColor = new TQCheckBox(i18n("&Use theme background color"), interfaceOptionsGroup); + + TQWhatsThis::add(d->themebackgroundColor, i18n("<p>Enable this option to use background theme " + "color in image editor area")); + + d->colorBox = new TQHBox(interfaceOptionsGroup); + + TQLabel *backgroundColorlabel = new TQLabel(i18n("&Background color:"), d->colorBox); + + d->backgroundColor = new KColorButton(d->colorBox); + backgroundColorlabel->setBuddy(d->backgroundColor); + TQWhatsThis::add(d->backgroundColor, i18n("<p>Customize background color to use " + "in image editor area.")); + + d->hideToolBar = new TQCheckBox(i18n("H&ide toolbar in fullscreen mode"), interfaceOptionsGroup); + + d->useRawImportTool = new TQCheckBox(i18n("Use Raw Import Tool to handle Raw image"), interfaceOptionsGroup); + TQWhatsThis::add(d->useRawImportTool, i18n("<p>Set on this option to use Raw Import " + "tool before to load a Raw image, " + "to customize indeep decoding settings.")); + + // -------------------------------------------------------- + + TQVGroupBox *exposureOptionsGroup = new TQVGroupBox(i18n("Exposure Indicators"), parent); + + TQHBox *underExpoBox = new TQHBox(exposureOptionsGroup); + TQLabel *underExpoColorlabel = new TQLabel( i18n("&Under-exposure color:"), underExpoBox); + d->underExposureColor = new KColorButton(underExpoBox); + underExpoColorlabel->setBuddy(d->underExposureColor); + TQWhatsThis::add(d->underExposureColor, i18n("<p>Customize the color used in image editor to identify " + "under-exposed pixels.")); + + TQHBox *overExpoBox = new TQHBox(exposureOptionsGroup); + TQLabel *overExpoColorlabel = new TQLabel( i18n("&Over-exposure color:"), overExpoBox); + d->overExposureColor = new KColorButton(overExpoBox); + overExpoColorlabel->setBuddy(d->overExposureColor); + TQWhatsThis::add(d->overExposureColor, i18n("<p>Customize the color used in image editor to identify " + "over-exposed pixels.")); + + // -------------------------------------------------------- + + layout->addWidget(interfaceOptionsGroup); + layout->addWidget(exposureOptionsGroup); + layout->addStretch(); + + // -------------------------------------------------------- + + connect(d->themebackgroundColor, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotThemeBackgroundColor(bool))); + + readSettings(); +} + +SetupEditor::~SetupEditor() +{ + delete d; +} + +void SetupEditor::slotThemeBackgroundColor(bool e) +{ + d->colorBox->setEnabled(!e); +} + +void SetupEditor::readSettings() +{ + TDEConfig* config = kapp->config(); + TQColor Black(TQt::black); + TQColor White(TQt::white); + config->setGroup("ImageViewer Settings"); + d->themebackgroundColor->setChecked(config->readBoolEntry("UseThemeBackgroundColor", true)); + d->backgroundColor->setColor(config->readColorEntry("BackgroundColor", &Black)); + d->hideToolBar->setChecked(config->readBoolEntry("FullScreen Hide ToolBar", false)); + d->underExposureColor->setColor(config->readColorEntry("UnderExposureColor", &White)); + d->overExposureColor->setColor(config->readColorEntry("OverExposureColor", &Black)); + d->useRawImportTool->setChecked(config->readBoolEntry("UseRawImportTool", false)); +} + +void SetupEditor::applySettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("ImageViewer Settings"); + config->writeEntry("UseThemeBackgroundColor", d->themebackgroundColor->isChecked()); + config->writeEntry("BackgroundColor", d->backgroundColor->color()); + config->writeEntry("FullScreen Hide ToolBar", d->hideToolBar->isChecked()); + config->writeEntry("UnderExposureColor", d->underExposureColor->color()); + config->writeEntry("OverExposureColor", d->overExposureColor->color()); + config->writeEntry("UseRawImportTool", d->useRawImportTool->isChecked()); + config->sync(); +} + +} // namespace Digikam diff --git a/src/utilities/setup/setupeditor.h b/src/utilities/setup/setupeditor.h new file mode 100644 index 00000000..bc7793ad --- /dev/null +++ b/src/utilities/setup/setupeditor.h @@ -0,0 +1,63 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-03 + * Description : setup Image Editor tab. + * + * Copyright (C) 2004-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUPEDITOR_H +#define SETUPEDITOR_H + +// TQt includes. + +#include <tqwidget.h> + +namespace Digikam +{ + +class SetupEditorPriv; + +class SetupEditor : public TQWidget +{ + TQ_OBJECT + + +public: + + SetupEditor(TQWidget* parent = 0); + ~SetupEditor(); + + void applySettings(); + +private slots: + + void slotThemeBackgroundColor(bool); + +private: + + void readSettings(); + +private: + + SetupEditorPriv* d; +}; + +} // namespace Digikam + +#endif // SETUPEDITOR_H diff --git a/src/utilities/setup/setupgeneral.cpp b/src/utilities/setup/setupgeneral.cpp new file mode 100644 index 00000000..2a8db22c --- /dev/null +++ b/src/utilities/setup/setupgeneral.cpp @@ -0,0 +1,313 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-02-01 + * Description : general configuration setup tab + * + * Copyright (C) 2003-2004 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlayout.h> +#include <tqcombobox.h> +#include <tqvbuttongroup.h> +#include <tqvgroupbox.h> +#include <tqhgroupbox.h> +#include <tqgroupbox.h> +#include <tqradiobutton.h> +#include <tqcheckbox.h> +#include <tqlabel.h> +#include <tqdir.h> +#include <tqlistbox.h> +#include <tqwhatsthis.h> +#include <tqtooltip.h> +#include <tqfileinfo.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kdialogbase.h> +#include <tdefiledialog.h> +#include <kurl.h> +#include <tdemessagebox.h> +#include <kurlrequester.h> + +// Local includes. + +#include "albumsettings.h" +#include "setupgeneral.h" +#include "setupgeneral.moc" + +namespace Digikam +{ + +class SetupGeneralPriv +{ +public: + + SetupGeneralPriv() + { + albumPathEdit = 0; + iconTreeThumbSize = 0; + iconTreeThumbLabel = 0; + iconShowNameBox = 0; + iconShowSizeBox = 0; + iconShowDateBox = 0; + iconShowModDateBox = 0; + iconShowResolutionBox = 0; + iconShowCommentsBox = 0; + iconShowTagsBox = 0; + iconShowRatingBox = 0; + rightClickActionComboBox = 0; + previewLoadFullImageSize = 0; + showFolderTreeViewItemsCount = 0; + } + + TQLabel *iconTreeThumbLabel; + + TQCheckBox *iconShowNameBox; + TQCheckBox *iconShowSizeBox; + TQCheckBox *iconShowDateBox; + TQCheckBox *iconShowModDateBox; + TQCheckBox *iconShowResolutionBox; + TQCheckBox *iconShowCommentsBox; + TQCheckBox *iconShowTagsBox; + TQCheckBox *iconShowRatingBox; + TQCheckBox *previewLoadFullImageSize; + TQCheckBox *showFolderTreeViewItemsCount; + + TQComboBox *iconTreeThumbSize; + TQComboBox *rightClickActionComboBox; + + KURLRequester *albumPathEdit; + + KDialogBase *mainDialog; +}; + +SetupGeneral::SetupGeneral(TQWidget* parent, KDialogBase* dialog ) + : TQWidget(parent) +{ + d = new SetupGeneralPriv; + d->mainDialog = dialog; + TQVBoxLayout *layout = new TQVBoxLayout( parent, 0, KDialog::spacingHint() ); + + // -------------------------------------------------------- + + TQHGroupBox *albumPathBox = new TQHGroupBox(parent); + albumPathBox->setTitle(i18n("Album &Library Path")); + + d->albumPathEdit = new KURLRequester(albumPathBox); + d->albumPathEdit->setMode(KFile::Directory | KFile::LocalOnly | KFile::ExistingOnly); + TQToolTip::add( d->albumPathEdit, i18n("<p>Here you can set the main path to the digiKam album " + "library in your computer." + "<p>Write access is required for this path and do not use a " + "remote path here, like an NFS mounted file system.")); + + connect(d->albumPathEdit, TQ_SIGNAL(urlSelected(const TQString &)), + this, TQ_SLOT(slotChangeAlbumPath(const TQString &))); + + connect(d->albumPathEdit, TQ_SIGNAL(textChanged(const TQString&)), + this, TQ_SLOT(slotPathEdited(const TQString&)) ); + + layout->addWidget(albumPathBox); + + // -------------------------------------------------------- + + TQVGroupBox *iconTextGroup = new TQVGroupBox(i18n("Thumbnail Information"), parent); + + d->iconShowNameBox = new TQCheckBox(i18n("Show file &name"), iconTextGroup); + TQWhatsThis::add( d->iconShowNameBox, i18n("<p>Set this option to show the file name below the image thumbnail.")); + + d->iconShowSizeBox = new TQCheckBox(i18n("Show file si&ze"), iconTextGroup); + TQWhatsThis::add( d->iconShowSizeBox, i18n("<p>Set this option to show the file size below the image thumbnail.")); + + d->iconShowDateBox = new TQCheckBox(i18n("Show camera creation &date"), iconTextGroup); + TQWhatsThis::add( d->iconShowDateBox, i18n("<p>Set this option to show the camera creation date " + "below the image thumbnail.")); + + d->iconShowModDateBox = new TQCheckBox(i18n("Show file &modification date"), iconTextGroup); + TQWhatsThis::add( d->iconShowModDateBox, i18n("<p>Set this option to show the file modification date " + "below the image thumbnail.")); + + d->iconShowCommentsBox = new TQCheckBox(i18n("Show digiKam &captions"), iconTextGroup); + TQWhatsThis::add( d->iconShowCommentsBox, i18n("<p>Set this option to show the digiKam captions " + "below the image thumbnail.")); + + d->iconShowTagsBox = new TQCheckBox(i18n("Show digiKam &tags"), iconTextGroup); + TQWhatsThis::add( d->iconShowTagsBox, i18n("<p>Set this option to show the digiKam tags " + "below the image thumbnail.")); + + d->iconShowRatingBox = new TQCheckBox(i18n("Show digiKam &rating"), iconTextGroup); + TQWhatsThis::add( d->iconShowRatingBox, i18n("<p>Set this option to show the digiKam rating " + "below the image thumbnail.")); + + d->iconShowResolutionBox = new TQCheckBox(i18n("Show ima&ge dimensions (warning: slow)"), iconTextGroup); + TQWhatsThis::add( d->iconShowResolutionBox, i18n("<p>Set this option to show the image size in pixels " + "below the image thumbnail.")); + + layout->addWidget(iconTextGroup); + + // -------------------------------------------------------- + + TQVGroupBox *interfaceOptionsGroup = new TQVGroupBox(i18n("Interface Options"), parent); + interfaceOptionsGroup->setColumnLayout(0, TQt::Vertical ); + interfaceOptionsGroup->layout()->setMargin(KDialog::marginHint()); + TQGridLayout* ifaceSettingsLayout = new TQGridLayout(interfaceOptionsGroup->layout(), 3, 4, KDialog::spacingHint()); + + d->iconTreeThumbLabel = new TQLabel(i18n("Sidebar thumbnail size:"), interfaceOptionsGroup); + d->iconTreeThumbSize = new TQComboBox(false, interfaceOptionsGroup); + d->iconTreeThumbSize->insertItem("16"); + d->iconTreeThumbSize->insertItem("22"); + d->iconTreeThumbSize->insertItem("32"); + d->iconTreeThumbSize->insertItem("48"); + TQToolTip::add( d->iconTreeThumbSize, i18n("<p>Set this option to configure the size " + "in pixels of the thumbnails in digiKam's sidebars. " + "This option will take effect when you restart " + "digiKam.")); + ifaceSettingsLayout->addMultiCellWidget(d->iconTreeThumbLabel, 0, 0, 0, 0); + ifaceSettingsLayout->addMultiCellWidget(d->iconTreeThumbSize, 0, 0, 1, 1); + + d->showFolderTreeViewItemsCount = new TQCheckBox(i18n("Show count of items in all tree-view"), interfaceOptionsGroup); + ifaceSettingsLayout->addMultiCellWidget(d->showFolderTreeViewItemsCount, 1, 1, 0, 4); + + + TQLabel *rightClickLabel = new TQLabel(i18n("Thumbnail click action:"), interfaceOptionsGroup); + d->rightClickActionComboBox = new TQComboBox(false, interfaceOptionsGroup); + d->rightClickActionComboBox->insertItem(i18n("Show embedded preview"), AlbumSettings::ShowPreview); + d->rightClickActionComboBox->insertItem(i18n("Start image editor"), AlbumSettings::StartEditor); + TQToolTip::add( d->rightClickActionComboBox, i18n("<p>Here, choose what should happen when you " + "click on a thumbnail.")); + ifaceSettingsLayout->addMultiCellWidget(rightClickLabel, 2 ,2, 0, 0); + ifaceSettingsLayout->addMultiCellWidget(d->rightClickActionComboBox, 2, 2, 1, 4); + + d->previewLoadFullImageSize = new TQCheckBox(i18n("Embedded preview loads full image size"), interfaceOptionsGroup); + TQWhatsThis::add( d->previewLoadFullImageSize, i18n("<p>Set this option to load the full image size " + "with an embedded preview, instead a reduced one. Because this option will take more time " + "to load images, use it only if you have a fast computer.")); + ifaceSettingsLayout->addMultiCellWidget(d->previewLoadFullImageSize, 3, 3, 0, 4); + + layout->addWidget(interfaceOptionsGroup); + + // -------------------------------------------------------- + + layout->addStretch(); + + readSettings(); + adjustSize(); +} + +SetupGeneral::~SetupGeneral() +{ + delete d; +} + +void SetupGeneral::applySettings() +{ + AlbumSettings* settings = AlbumSettings::instance(); + if (!settings) return; + + settings->setAlbumLibraryPath(d->albumPathEdit->url()); + + settings->setDefaultTreeIconSize(d->iconTreeThumbSize->currentText().toInt()); + settings->setIconShowName(d->iconShowNameBox->isChecked()); + settings->setIconShowTags(d->iconShowTagsBox->isChecked()); + settings->setIconShowSize(d->iconShowSizeBox->isChecked()); + settings->setIconShowDate(d->iconShowDateBox->isChecked()); + settings->setIconShowModDate(d->iconShowModDateBox->isChecked()); + settings->setIconShowResolution(d->iconShowResolutionBox->isChecked()); + settings->setIconShowComments(d->iconShowCommentsBox->isChecked()); + settings->setIconShowRating(d->iconShowRatingBox->isChecked()); + + settings->setItemRightClickAction((AlbumSettings::ItemRightClickAction) + d->rightClickActionComboBox->currentItem()); + + settings->setPreviewLoadFullImageSize(d->previewLoadFullImageSize->isChecked()); + settings->setShowFolderTreeViewItemsCount(d->showFolderTreeViewItemsCount->isChecked()); + settings->saveSettings(); +} + +void SetupGeneral::readSettings() +{ + AlbumSettings* settings = AlbumSettings::instance(); + + if (!settings) return; + + d->albumPathEdit->setURL(settings->getAlbumLibraryPath()); + + if (settings->getDefaultTreeIconSize() == 16) + d->iconTreeThumbSize->setCurrentItem(0); + else if (settings->getDefaultTreeIconSize() == 22) + d->iconTreeThumbSize->setCurrentItem(1); + else if (settings->getDefaultTreeIconSize() == 32) + d->iconTreeThumbSize->setCurrentItem(2); + else + d->iconTreeThumbSize->setCurrentItem(3); + + d->iconShowNameBox->setChecked(settings->getIconShowName()); + d->iconShowTagsBox->setChecked(settings->getIconShowTags()); + d->iconShowSizeBox->setChecked(settings->getIconShowSize()); + d->iconShowDateBox->setChecked(settings->getIconShowDate()); + d->iconShowModDateBox->setChecked(settings->getIconShowModDate()); + d->iconShowResolutionBox->setChecked(settings->getIconShowResolution()); + d->iconShowCommentsBox->setChecked(settings->getIconShowComments()); + d->iconShowRatingBox->setChecked(settings->getIconShowRating()); + + d->rightClickActionComboBox->setCurrentItem((int)settings->getItemRightClickAction()); + + d->previewLoadFullImageSize->setChecked(settings->getPreviewLoadFullImageSize()); + d->showFolderTreeViewItemsCount->setChecked(settings->getShowFolderTreeViewItemsCount()); +} + +void SetupGeneral::slotChangeAlbumPath(const TQString &result) +{ + if (KURL(result).equals(KURL(TQDir::homeDirPath()), true)) + { + KMessageBox::sorry(0, i18n("Sorry you can't use your home directory as album library.")); + return; + } + + TQFileInfo targetPath(result); + + if (!result.isEmpty() && !targetPath.isWritable()) + { + KMessageBox::information(0, i18n("No write access for this path.\n" + "Warning: the caption and tag features will not work.")); + } +} + +void SetupGeneral::slotPathEdited(const TQString& newPath) +{ + if (newPath.isEmpty()) + { + d->mainDialog->enableButtonOK(false); + return; + } + + if (!newPath.startsWith("/")) + { + d->albumPathEdit->setURL(TQDir::homeDirPath() + '/' + newPath); + } + + TQFileInfo targetPath(newPath); + TQDir dir(newPath); + d->mainDialog->enableButtonOK(dir.exists() && dir.path() != TQDir::homeDirPath()); +} + +} // namespace Digikam + diff --git a/src/utilities/setup/setupgeneral.h b/src/utilities/setup/setupgeneral.h new file mode 100644 index 00000000..768c4154 --- /dev/null +++ b/src/utilities/setup/setupgeneral.h @@ -0,0 +1,67 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-02-01 + * Description : general configuration setup tab + * + * Copyright (C) 2003-2004 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUPGENERAL_H +#define SETUPGENERAL_H + +// TQt includes. + +#include <tqwidget.h> + +class KDialogBase; + +namespace Digikam +{ + +class SetupGeneralPriv; + +class SetupGeneral : public TQWidget +{ + TQ_OBJECT + + +public: + + SetupGeneral(TQWidget* parent = 0, KDialogBase* dialog = 0); + ~SetupGeneral(); + + void applySettings(); + +private: + + void readSettings(); + +private slots: + + void slotChangeAlbumPath(const TQString &); + void slotPathEdited(const TQString&); + +private: + + SetupGeneralPriv* d; +}; + +} // namespace Digikam + +#endif // SETUPGENERAL_H diff --git a/src/utilities/setup/setupicc.cpp b/src/utilities/setup/setupicc.cpp new file mode 100644 index 00000000..b08750c1 --- /dev/null +++ b/src/utilities/setup/setupicc.cpp @@ -0,0 +1,727 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-11-24 + * Description : Color management setup tab. + * + * Copyright (C) 2005-2007 by F.J. Cruz <fj.cruz@supercable.es> + * Copyright (C) 2005-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#include <config.h> + +// TQt includes. + +#include <tqlayout.h> +#include <tqvbuttongroup.h> +#include <tqvgroupbox.h> +#include <tqhgroupbox.h> +#include <tqgroupbox.h> +#include <tqcheckbox.h> +#include <tqradiobutton.h> +#include <tqlabel.h> +#include <tqwhatsthis.h> +#include <tqiconset.h> +#include <tqpixmap.h> +#include <tqpushbutton.h> +#include <tqstringlist.h> +#include <tqmap.h> +#include <tqdir.h> +#include <tqtooltip.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kdialog.h> +#include <kdialogbase.h> +#include <kurlrequester.h> +#include <klineedit.h> +#include <tdeconfig.h> +#include <kcombobox.h> +#include <tdeapplication.h> +#include <tdemessagebox.h> +#include <kurllabel.h> +#include <kiconloader.h> +#include <tdeglobalsettings.h> +#include <kstandarddirs.h> + +// lcms includes. + +#include LCMS_HEADER +#if LCMS_VERSION < 114 +#define cmsTakeCopyright(profile) "Unknown" +#endif // LCMS_VERSION < 114 + +// Local includes. + +#include "ddebug.h" +#include "squeezedcombobox.h" +#include "iccprofileinfodlg.h" +#include "albumsettings.h" +#include "setupicc.h" +#include "setupicc.moc" + +namespace Digikam +{ + +class SetupICCPriv +{ +public: + + SetupICCPriv() + { + enableColorManagement = 0; + bpcAlgorithm = 0; + managedView = 0; + defaultApplyICC = 0; + defaultAskICC = 0; + defaultPathKU = 0; + inProfilesKC = 0; + workProfilesKC = 0; + proofProfilesKC = 0; + monitorProfilesKC = 0; + renderingIntentKC = 0; + infoWorkProfiles = 0; + infoMonitorProfiles = 0; + infoInProfiles = 0; + infoProofProfiles = 0; + behaviourGB = 0; + defaultPathGB = 0; + profilesGB = 0; + advancedSettingsGB = 0; + monitorIcon = 0; + monitorProfiles = 0; + } + + TQLabel *monitorIcon; + TQLabel *monitorProfiles; + + TQCheckBox *enableColorManagement; + TQCheckBox *bpcAlgorithm; + TQCheckBox *managedView; + + TQRadioButton *defaultApplyICC; + TQRadioButton *defaultAskICC; + + TQPushButton *infoWorkProfiles; + TQPushButton *infoMonitorProfiles; + TQPushButton *infoInProfiles; + TQPushButton *infoProofProfiles; + + TQVGroupBox *behaviourGB; + TQHGroupBox *defaultPathGB; + TQGroupBox *profilesGB; + TQVGroupBox *advancedSettingsGB; + + // Maps to store profile descriptions and profile file path + TQMap<TQString, TQString> inICCPath; + TQMap<TQString, TQString> workICCPath; + TQMap<TQString, TQString> proofICCPath; + TQMap<TQString, TQString> monitorICCPath; + + KURLRequester *defaultPathKU; + + KComboBox *renderingIntentKC; + + KDialogBase *mainDialog; + + SqueezedComboBox *inProfilesKC; + SqueezedComboBox *workProfilesKC; + SqueezedComboBox *proofProfilesKC; + SqueezedComboBox *monitorProfilesKC; +}; + +SetupICC::SetupICC(TQWidget* parent, KDialogBase* dialog ) + : TQWidget(parent) +{ + d = new SetupICCPriv(); + d->mainDialog = dialog; + TQVBoxLayout *layout = new TQVBoxLayout( parent, 0, KDialog::spacingHint()); + + // -------------------------------------------------------- + + TQGroupBox *colorPolicy = new TQGroupBox(0, TQt::Horizontal, i18n("Color Management Policy"), parent); + TQGridLayout* grid = new TQGridLayout( colorPolicy->layout(), 1, 2, KDialog::spacingHint()); + + d->enableColorManagement = new TQCheckBox(colorPolicy); + d->enableColorManagement->setText(i18n("Enable Color Management")); + TQWhatsThis::add( d->enableColorManagement, i18n("<ul><li>Checked: Color Management is enabled</li>" + "<li>Unchecked: Color Management is disabled</li></ul>")); + + KURLLabel *lcmsLogoLabel = new KURLLabel(colorPolicy); + lcmsLogoLabel->setText(TQString()); + lcmsLogoLabel->setURL("http://www.littlecms.com"); + TDEGlobal::dirs()->addResourceType("logo-lcms", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("logo-lcms", "logo-lcms.png"); + lcmsLogoLabel->setPixmap( TQPixmap( directory + "logo-lcms.png" ) ); + TQToolTip::add(lcmsLogoLabel, i18n("Visit Little CMS project website")); + + d->behaviourGB = new TQVGroupBox(i18n("Behavior"), colorPolicy); + TQButtonGroup *behaviourOptions = new TQButtonGroup(2, TQt::Vertical, d->behaviourGB); + behaviourOptions->setFrameStyle( TQFrame::NoFrame ); + behaviourOptions->setInsideMargin(0); + + d->defaultApplyICC = new TQRadioButton(behaviourOptions); + d->defaultApplyICC->setText(i18n("Apply when opening an image in the Image Editor")); + TQWhatsThis::add( d->defaultApplyICC, i18n("<p>If this option is enabled, digiKam applies the " + "Workspace default color profile to an image, without prompting you about missing " + "embedded profiles or embedded profiles different from the workspace profile.</p>")); + + d->defaultAskICC = new TQRadioButton(behaviourOptions); + d->defaultAskICC->setText(i18n("Ask when opening an image in the Image Editor")); + TQWhatsThis::add( d->defaultAskICC, i18n("<p>If this option is enabled, digiKam asks to user " + "before it applies the Workspace default color profile to an image which has no " + "embedded profile or, if the image has an embedded profile, when it's not the same " + "as the workspace profile.</p>")); + + grid->addMultiCellWidget(d->enableColorManagement, 0, 0, 0, 0); + grid->addMultiCellWidget(lcmsLogoLabel, 0, 0, 2, 2); + grid->addMultiCellWidget(d->behaviourGB, 1, 1, 0, 2); + grid->setColStretch(1, 10); + + layout->addWidget(colorPolicy); + + // -------------------------------------------------------- + + d->defaultPathGB = new TQHGroupBox(parent); + d->defaultPathGB->setTitle(i18n("Color Profiles Directory")); + + d->defaultPathKU = new KURLRequester(d->defaultPathGB); + d->defaultPathKU->lineEdit()->setReadOnly(true); + d->defaultPathKU->setMode(KFile::Directory | KFile::LocalOnly | KFile::ExistingOnly); + TQWhatsThis::add( d->defaultPathKU, i18n("<p>Default path to the color profiles folder. " + "You must store all your color profiles in this directory.</p>")); + + layout->addWidget(d->defaultPathGB); + + // -------------------------------------------------------- + + d->profilesGB = new TQGroupBox(0, TQt::Horizontal, i18n("ICC Profiles Settings"), parent); + TQGridLayout* grid2 = new TQGridLayout( d->profilesGB->layout(), 4, 3, KDialog::spacingHint()); + grid2->setColStretch(2, 10); + + d->managedView = new TQCheckBox(d->profilesGB); + d->managedView->setText(i18n("Use color managed view (warning: slow)")); + TQWhatsThis::add( d->managedView, i18n("<p>Turn on this option if " + "you want to use your <b>Monitor Color Profile</b> to show your pictures in " + "the Image Editor window with a color correction adapted to your monitor. " + "Warning: this option can take a while to render " + "pictures on the screen, especially with a slow computer.</p>")); + + d->monitorIcon = new TQLabel(d->profilesGB); + d->monitorIcon->setPixmap(SmallIcon("tv")); + d->monitorProfiles = new TQLabel(i18n("Monitor:"), d->profilesGB); + d->monitorProfilesKC = new SqueezedComboBox(d->profilesGB); + d->monitorProfiles->setBuddy(d->monitorProfilesKC); + TQWhatsThis::add( d->monitorProfilesKC, i18n("<p>Select the color profile for your monitor. " + "You need to enable the <b>Use color managed view</b> option to use this profile.</p>")); + d->infoMonitorProfiles = new TQPushButton(i18n("Info..."), d->profilesGB); + TQWhatsThis::add( d->infoMonitorProfiles, i18n("<p>You can use this button to get more detailed " + "information about the selected monitor profile.</p>")); + + grid2->addMultiCellWidget(d->managedView, 0, 0, 0, 3); + grid2->addMultiCellWidget(d->monitorIcon, 1, 1, 0, 0); + grid2->addMultiCellWidget(d->monitorProfiles, 1, 1, 1, 1); + grid2->addMultiCellWidget(d->monitorProfilesKC, 1, 1, 2, 2); + grid2->addMultiCellWidget(d->infoMonitorProfiles, 1, 1, 3, 3); + + TQLabel *workIcon = new TQLabel(d->profilesGB); + workIcon->setPixmap(SmallIcon("input-tablet")); + TQLabel *workProfiles = new TQLabel(i18n("Workspace:"), d->profilesGB); + d->workProfilesKC = new SqueezedComboBox(d->profilesGB); + workProfiles->setBuddy(d->workProfilesKC); + TQWhatsThis::add( d->workProfilesKC, i18n("<p>All the images will be converted to the color " + "space of this profile, so you must select a profile appropriate for editing.</p>" + "<p>These color profiles are device independent.</p>")); + d->infoWorkProfiles = new TQPushButton(i18n("Info..."), d->profilesGB); + TQWhatsThis::add( d->infoWorkProfiles, i18n("<p>You can use this button to get more detailed " + "information about the selected workspace profile.</p>")); + + grid2->addMultiCellWidget(workIcon, 2, 2, 0, 0); + grid2->addMultiCellWidget(workProfiles, 2, 2, 1, 1); + grid2->addMultiCellWidget(d->workProfilesKC, 2, 2, 2, 2); + grid2->addMultiCellWidget(d->infoWorkProfiles, 2, 2, 3, 3); + + TQLabel *inIcon = new TQLabel(d->profilesGB); + inIcon->setPixmap(SmallIcon("camera-photo")); + TQLabel *inProfiles = new TQLabel(i18n("Input:"), d->profilesGB); + d->inProfilesKC = new SqueezedComboBox(d->profilesGB); + inProfiles->setBuddy(d->inProfilesKC); + TQWhatsThis::add( d->inProfilesKC, i18n("<p>You must select the profile for your input device " + "(usually, your camera, scanner...)</p>")); + d->infoInProfiles = new TQPushButton(i18n("Info..."), d->profilesGB); + TQWhatsThis::add( d->infoInProfiles, i18n("<p>You can use this button to get more detailed " + "information about the selected input profile.</p>")); + + grid2->addMultiCellWidget(inIcon, 3, 3, 0, 0); + grid2->addMultiCellWidget(inProfiles, 3, 3, 1, 1); + grid2->addMultiCellWidget(d->inProfilesKC, 3, 3, 2, 2); + grid2->addMultiCellWidget(d->infoInProfiles, 3, 3, 3, 3); + + TQLabel *proofIcon = new TQLabel(d->profilesGB); + proofIcon->setPixmap(SmallIcon("printer")); + TQLabel *proofProfiles = new TQLabel(i18n("Soft proof:"), d->profilesGB); + d->proofProfilesKC = new SqueezedComboBox(d->profilesGB); + proofProfiles->setBuddy(d->proofProfilesKC); + TQWhatsThis::add( d->proofProfilesKC, i18n("<p>You must select the profile for your output device " + "(usually, your printer). This profile will be used to do a soft proof, so you will " + "be able to preview how an image will be rendered via an output device.</p>")); + d->infoProofProfiles = new TQPushButton(i18n("Info..."), d->profilesGB); + TQWhatsThis::add( d->infoProofProfiles, i18n("<p>You can use this button to get more detailed " + "information about the selected soft proof profile.</p>")); + + grid2->addMultiCellWidget(proofIcon, 4, 4, 0, 0); + grid2->addMultiCellWidget(proofProfiles, 4, 4, 1, 1); + grid2->addMultiCellWidget(d->proofProfilesKC, 4, 4, 2, 2); + grid2->addMultiCellWidget(d->infoProofProfiles, 4, 4, 3, 3); + + layout->addWidget(d->profilesGB); + + // -------------------------------------------------------- + + d->advancedSettingsGB = new TQVGroupBox(i18n("Advanced Settings"), parent); + + d->bpcAlgorithm = new TQCheckBox(d->advancedSettingsGB); + d->bpcAlgorithm->setText(i18n("Use black point compensation")); + TQWhatsThis::add( d->bpcAlgorithm, i18n("<p><b>Black Point Compensation</b> is a way to make " + "adjustments between the maximum " + "black levels of digital files and the black capabilities of various " + "digital devices.</p>")); + + TQHBox *hbox2 = new TQHBox(d->advancedSettingsGB); + TQLabel *lablel = new TQLabel(hbox2); + lablel->setText(i18n("Rendering Intents:")); + + d->renderingIntentKC = new KComboBox(false, hbox2); + d->renderingIntentKC->insertItem("Perceptual"); + d->renderingIntentKC->insertItem("Relative Colorimetric"); + d->renderingIntentKC->insertItem("Saturation"); + d->renderingIntentKC->insertItem("Absolute Colorimetric"); + TQWhatsThis::add( d->renderingIntentKC, i18n("<ul><li><p><b>Perceptual intent</b> causes the full gamut of the image to be " + "compressed or expanded to fill the gamut of the destination device, so that gray balance is " + "preserved but colorimetric accuracy may not be preserved.</p>" + "<p>In other words, if certain colors in an image fall outside of the range of colors that the output " + "device can render, the image intent will cause all the colors in the image to be adjusted so that " + "the every color in the image falls within the range that can be rendered and so that the relationship " + "between colors is preserved as much as possible.</p>" + "<p>This intent is most suitable for display of photographs and images, and is the default intent.</p></li>" + "<li><p><b>Absolute Colorimetric intent</b> causes any colors that fall outside the range that the output device " + "can render are adjusted to the closest color that can be rendered, while all other colors are " + "left unchanged.</p>" + "<p>This intent preserves the white point and is most suitable for spot colors (Pantone, TruMatch, " + "logo colors, ...).</p></li>" + "<li><p><b>Relative Colorimetric intent</b> is defined such that any colors that fall outside the range that the " + "output device can render are adjusted to the closest color that can be rendered, while all other colors " + "are left unchanged. Proof intent does not preserve the white point.</p></li>" + "<li><p><b>Saturation intent</b> preserves the saturation of colors in the image at the possible expense of " + "hue and lightness.</p>" + "<p>Implementation of this intent remains somewhat problematic, and the ICC is still working on methods to " + "achieve the desired effects.</p>" + "<p>This intent is most suitable for business graphics such as charts, where it is more important that the " + "colors be vivid and contrast well with each other rather than a specific color.</p></li></ul>")); + + layout->addWidget(d->advancedSettingsGB); + layout->addStretch(); + + // -------------------------------------------------------- + + connect(d->managedView, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotToggleManagedView(bool))); + + connect(lcmsLogoLabel, TQ_SIGNAL(leftClickedURL(const TQString&)), + this, TQ_SLOT(processLCMSURL(const TQString&))); + + connect(d->enableColorManagement, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotToggledWidgets(bool))); + + connect(d->infoProofProfiles, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotClickedProof()) ); + + connect(d->infoInProfiles, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotClickedIn()) ); + + connect(d->infoMonitorProfiles, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotClickedMonitor()) ); + + connect(d->infoWorkProfiles, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotClickedWork())); + + connect(d->defaultPathKU, TQ_SIGNAL(urlSelected(const TQString&)), + this, TQ_SLOT(slotFillCombos(const TQString&))); + + // -------------------------------------------------------- + + adjustSize(); + readSettings(); + slotToggledWidgets(d->enableColorManagement->isChecked()); + slotToggleManagedView(d->managedView->isChecked()); +} + +SetupICC::~SetupICC() +{ + delete d; +} + +void SetupICC::processLCMSURL(const TQString& url) +{ + TDEApplication::kApplication()->invokeBrowser(url); +} + +void SetupICC::applySettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("Color Management"); + + config->writeEntry("EnableCM", d->enableColorManagement->isChecked()); + + if (!d->enableColorManagement->isChecked()) + return; // No need to write settings in this case. + + if (d->defaultApplyICC->isChecked()) + config->writeEntry("BehaviourICC", true); + else + config->writeEntry("BehaviourICC", false); + + config->writePathEntry("DefaultPath", d->defaultPathKU->url()); + config->writeEntry("WorkSpaceProfile", d->workProfilesKC->currentItem()); + config->writeEntry("MonitorProfile", d->monitorProfilesKC->currentItem()); + config->writeEntry("InProfile", d->inProfilesKC->currentItem()); + config->writeEntry("ProofProfile", d->proofProfilesKC->currentItem()); + config->writeEntry("BPCAlgorithm", d->bpcAlgorithm->isChecked()); + config->writeEntry("RenderingIntent", d->renderingIntentKC->currentItem()); + config->writeEntry("ManagedView", d->managedView->isChecked()); + + config->writePathEntry("InProfileFile", + *(d->inICCPath.find(d->inProfilesKC->itemHighlighted()))); + config->writePathEntry("WorkProfileFile", + *(d->workICCPath.find(d->workProfilesKC->itemHighlighted()))); + config->writePathEntry("MonitorProfileFile", + *(d->monitorICCPath.find(d->monitorProfilesKC->itemHighlighted()))); + config->writePathEntry("ProofProfileFile", + *(d->proofICCPath.find(d->proofProfilesKC->itemHighlighted()))); +} + +void SetupICC::readSettings(bool restore) +{ + TDEConfig* config = kapp->config(); + config->setGroup("Color Management"); + + if (!restore) + d->enableColorManagement->setChecked(config->readBoolEntry("EnableCM", false)); + + d->defaultPathKU->setURL(config->readPathEntry("DefaultPath", TQString())); + d->bpcAlgorithm->setChecked(config->readBoolEntry("BPCAlgorithm", false)); + d->renderingIntentKC->setCurrentItem(config->readNumEntry("RenderingIntent", 0)); + d->managedView->setChecked(config->readBoolEntry("ManagedView", false)); + + if (config->readBoolEntry("BehaviourICC")) + d->defaultApplyICC->setChecked(true); + else + d->defaultAskICC->setChecked(true); + + fillCombos(d->defaultPathKU->url(), false); + + d->workProfilesKC->setCurrentItem(config->readNumEntry("WorkSpaceProfile", 0)); + d->monitorProfilesKC->setCurrentItem(config->readNumEntry("MonitorProfile", 0)); + d->inProfilesKC->setCurrentItem(config->readNumEntry("InProfile", 0)); + d->proofProfilesKC->setCurrentItem(config->readNumEntry("ProofProfile", 0)); +} + +void SetupICC::slotFillCombos(const TQString& path) +{ + fillCombos(path, true); +} + +void SetupICC::fillCombos(const TQString& path, bool report) +{ + if (!d->enableColorManagement->isChecked()) + return; + + d->inProfilesKC->clear(); + d->monitorProfilesKC->clear(); + d->workProfilesKC->clear(); + d->proofProfilesKC->clear(); + d->inICCPath.clear(); + d->workICCPath.clear(); + d->proofICCPath.clear(); + d->monitorICCPath.clear(); + TQDir dir(path); + + if (path.isEmpty() || !dir.exists() || !dir.isReadable()) + { + if (report) + KMessageBox::sorry(this, i18n("<p>You must set a correct default " + "path for your ICC color profiles files.</p>")); + + d->mainDialog->enableButtonOK(false); + return; + } + d->mainDialog->enableButtonOK(true); + + // Look the ICC profile path repository set by user. + TQDir userProfilesDir(path, "*.icc;*.icm", TQDir::Files); + const TQFileInfoList* usersFiles = userProfilesDir.entryInfoList(); + DDebug() << "Scanning ICC profiles from user repository: " << path << endl; + + if ( !parseProfilesfromDir(usersFiles) ) + { + if (report) + { + TQString message = i18n("<p>Sorry, there are no ICC profiles files in "); + message.append(path); + message.append(i18n("</p>")); + KMessageBox::sorry(this, message); + } + + DDebug() << "No ICC profile files found!!!" << endl; + d->mainDialog->enableButtonOK(false); + return; + } + + // Look the ICC color-space profile path include with digiKam dist. + TDEGlobal::dirs()->addResourceType("profiles", TDEGlobal::dirs()->kde_default("data") + "digikam/profiles"); + TQString digiKamProfilesPath = TDEGlobal::dirs()->findResourceDir("profiles", "srgb.icm"); + TQDir digiKamProfilesDir(digiKamProfilesPath, "*.icc;*.icm", TQDir::Files); + const TQFileInfoList* digiKamFiles = digiKamProfilesDir.entryInfoList(); + DDebug() << "Scanning ICC profiles included with digiKam: " << digiKamProfilesPath << endl; + parseProfilesfromDir(digiKamFiles); + + d->monitorProfilesKC->insertSqueezedList(d->monitorICCPath.keys(), 0); + if (d->monitorICCPath.keys().isEmpty()) + { + d->managedView->setEnabled(false); + d->managedView->setChecked(false); + } + else + { + d->managedView->setEnabled(true); + } + + d->inProfilesKC->insertSqueezedList(d->inICCPath.keys(), 0); + d->proofProfilesKC->insertSqueezedList(d->proofICCPath.keys(), 0); + + d->workProfilesKC->insertSqueezedList(d->workICCPath.keys(), 0); + if (d->workICCPath.keys().isEmpty()) + { + // If there is no workspace icc profiles available, + // the CM is broken and cannot be used. + d->mainDialog->enableButtonOK(false); + return; + } + + d->mainDialog->enableButtonOK(true); +} + +bool SetupICC::parseProfilesfromDir(const TQFileInfoList* files) +{ + cmsHPROFILE tmpProfile=0; + bool findIccFiles=false; + + if (files) + { + TQFileInfoListIterator it(*files); + TQFileInfo *fileInfo=0; + + while ((fileInfo = it.current()) != 0) + { + if (fileInfo->isFile() && fileInfo->isReadable()) + { + TQString fileName = fileInfo->filePath(); + tmpProfile = cmsOpenProfileFromFile(TQFile::encodeName(fileName), "r"); + + if (tmpProfile == NULL) + { + DDebug() << "Error: Parsed profile is NULL (invalid profile); " << fileName << endl; + cmsCloseProfile(tmpProfile); + ++it; + TQString message = i18n("<p>The following profile is invalid:</p><p><b>"); + message.append(fileName); + message.append("</b></p><p>To avoid this message remove it from color profiles repository</p>"); + message.append("<p>Do you want digiKam do it for you?</p>"); + if (KMessageBox::warningYesNo(this, message, i18n("Invalid Profile")) == 3) + { + if (TQFile::remove(fileName)) + { + KMessageBox::information(this, i18n("Invalid color profile has been removed")); + } + else + { + KMessageBox::information(this, i18n("<p>digiKam has failed to remove the invalid color profile</p><p>You have to do it manually</p>")); + } + } + + continue; + } + + TQString profileDescription = TQString((cmsTakeProductDesc(tmpProfile))); + + switch ((int)cmsGetDeviceClass(tmpProfile)) + { + case icSigInputClass: + { + if (TQString(cmsTakeProductDesc(tmpProfile)).isEmpty()) + d->inICCPath.insert(fileName, fileName); + else + d->inICCPath.insert(TQString(cmsTakeProductDesc(tmpProfile)), fileName); + + DDebug() << "ICC file: " << fileName << " ==> Input device class (" + << cmsGetDeviceClass(tmpProfile) << ")" << endl; + findIccFiles = true; + break; + } + case icSigDisplayClass: + { + if (TQString(cmsTakeProductDesc(tmpProfile)).isEmpty()) + { + d->monitorICCPath.insert(fileName, fileName); + d->workICCPath.insert(fileName, fileName); + } + else + { + d->monitorICCPath.insert(TQString(cmsTakeProductDesc(tmpProfile)), fileName); + d->workICCPath.insert(TQString(cmsTakeProductDesc(tmpProfile)), fileName); + } + + DDebug() << "ICC file: " << fileName << " ==> Monitor device class (" + << cmsGetDeviceClass(tmpProfile) << ")" << endl; + findIccFiles = true; + break; + } + case icSigOutputClass: + { + if (TQString(cmsTakeProductDesc(tmpProfile)).isEmpty()) + d->proofICCPath.insert(fileName, fileName); + else + d->proofICCPath.insert(TQString(cmsTakeProductDesc(tmpProfile)), fileName); + + DDebug() << "ICC file: " << fileName << " ==> Output device class (" + << cmsGetDeviceClass(tmpProfile) << ")" << endl; + findIccFiles = true; + break; + } + case icSigColorSpaceClass: + { + if (TQString(cmsTakeProductDesc(tmpProfile)).isEmpty()) + { + d->inICCPath.insert(fileName, fileName); + d->workICCPath.insert(fileName, fileName); + } + else + { + d->inICCPath.insert(TQString(cmsTakeProductDesc(tmpProfile)), fileName); + d->workICCPath.insert(TQString(cmsTakeProductDesc(tmpProfile)), fileName); + } + + DDebug() << "ICC file: " << fileName << " ==> WorkingSpace device class (" + << cmsGetDeviceClass(tmpProfile) << ")" << endl; + findIccFiles = true; + break; + } + default: + { + DDebug() << "ICC file: " << fileName << " ==> UNKNOW device class (" + << cmsGetDeviceClass(tmpProfile) << ")" << endl; + break; + } + } + + cmsCloseProfile(tmpProfile); + } + ++it; + } + } + + return findIccFiles; +} + +void SetupICC::slotToggledWidgets(bool t) +{ + d->behaviourGB->setEnabled(t); + d->defaultPathGB->setEnabled(t); + d->profilesGB->setEnabled(t); + d->advancedSettingsGB->setEnabled(t); + + if (t) + { + readSettings(true); + slotToggleManagedView(d->managedView->isChecked()); + } + else + d->mainDialog->enableButtonOK(true); +} + +void SetupICC::slotClickedWork() +{ + profileInfo(*(d->workICCPath.find(d->workProfilesKC->itemHighlighted()))); +} + +void SetupICC::slotClickedIn() +{ + profileInfo(*(d->inICCPath.find(d->inProfilesKC->itemHighlighted()))); +} + +void SetupICC::slotClickedMonitor() +{ + profileInfo(*(d->monitorICCPath.find(d->monitorProfilesKC->itemHighlighted()))); +} + +void SetupICC::slotClickedProof() +{ + profileInfo(*(d->proofICCPath.find(d->proofProfilesKC->itemHighlighted()))); +} + +void SetupICC::profileInfo(const TQString& profile) +{ + if (profile.isEmpty()) + { + KMessageBox::error(this, i18n("Sorry, there is not any selected profile"), i18n("Profile Error")); + return; + } + + ICCProfileInfoDlg infoDlg(this, profile); + infoDlg.exec(); +} + +void SetupICC::slotToggleManagedView(bool b) +{ + d->monitorIcon->setEnabled(b); + d->monitorProfiles->setEnabled(b); + d->monitorProfilesKC->setEnabled(b); + d->infoMonitorProfiles->setEnabled(b); +} + +bool SetupICC::iccRepositoryIsValid() +{ + TDEConfig* config = kapp->config(); + config->setGroup("Color Management"); + + // If color management is disable, no need to check anymore. + if (!config->readBoolEntry("EnableCM", false)) + return true; + + // To be valid, the ICC profiles repository must exist and be readable. + + TQDir tmpPath(config->readPathEntry("DefaultPath", TQString())); + DDebug() << "ICC profiles repository is: " << tmpPath.dirName() << endl; + + if ( tmpPath.exists() && tmpPath.isReadable() ) + return true; + + return false; +} + +} // namespace Digikam diff --git a/src/utilities/setup/setupicc.h b/src/utilities/setup/setupicc.h new file mode 100644 index 00000000..8eaaece1 --- /dev/null +++ b/src/utilities/setup/setupicc.h @@ -0,0 +1,86 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-11-24 + * Description : Color management setup tab. + * + * Copyright (C) 2005-2007 by F.J. Cruz <fj.cruz@supercable.es> + * Copyright (C) 2005-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUPICC_H +#define SETUPICC_H + +// TQt includes. + +#include <tqwidget.h> +#include <tqmap.h> +#include <tqdir.h> + +// Local includes. + +#include "digikam_export.h" + +class KDialogBase; + +namespace Digikam +{ + +class SetupICCPriv; + +class DIGIKAM_EXPORT SetupICC : public TQWidget +{ + TQ_OBJECT + + +public: + + SetupICC(TQWidget* parent = 0, KDialogBase* dialog = 0); + ~SetupICC(); + + void applySettings(); + + static bool iccRepositoryIsValid(); + +private: + + void readSettings(bool restore=false); + void fillCombos(const TQString& path, bool report); + void enableWidgets(); + void disableWidgets(); + void profileInfo(const TQString&); + bool parseProfilesfromDir(const TQFileInfoList* files); + +private slots: + + void processLCMSURL(const TQString&); + void slotToggledWidgets(bool); + void slotToggleManagedView(bool); + void slotFillCombos(const TQString&); + void slotClickedIn(); + void slotClickedWork(); + void slotClickedMonitor(); + void slotClickedProof(); + +private: + + SetupICCPriv* d; +}; + +} // namespace Digikam + +#endif // SETUPICC_H diff --git a/src/utilities/setup/setupidentity.cpp b/src/utilities/setup/setupidentity.cpp new file mode 100644 index 00000000..ecbfdfbf --- /dev/null +++ b/src/utilities/setup/setupidentity.cpp @@ -0,0 +1,217 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-07-04 + * Description : default IPTC identity setup tab. + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlayout.h> +#include <tqhgroupbox.h> +#include <tqgroupbox.h> +#include <tqlabel.h> +#include <tqwhatsthis.h> +#include <tqvalidator.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kdialog.h> +#include <klineedit.h> +#include <kactivelabel.h> + +// Local includes. + +#include "albumsettings.h" +#include "setupidentity.h" +#include "setupidentity.moc" + +namespace Digikam +{ + +class SetupIdentityPriv +{ +public: + + SetupIdentityPriv() + { + authorEdit = 0; + authorTitleEdit = 0; + creditEdit = 0; + sourceEdit = 0; + copyrightEdit = 0; + } + + KLineEdit *authorEdit; + KLineEdit *authorTitleEdit; + KLineEdit *creditEdit; + KLineEdit *sourceEdit; + KLineEdit *copyrightEdit; +}; + +SetupIdentity::SetupIdentity(TQWidget* parent ) + : TQWidget(parent) +{ + d = new SetupIdentityPriv; + TQVBoxLayout *layout = new TQVBoxLayout( parent, 0, KDialog::spacingHint() ); + + // -------------------------------------------------------- + + // IPTC only accept printable Ascii char. + TQRegExp asciiRx("[\x20-\x7F]+$"); + TQValidator *asciiValidator = new TQRegExpValidator(asciiRx, this); + + TQGroupBox *photographerIdGroup = new TQGroupBox(0, TQt::Horizontal, i18n("Photographer and Copyright Information"), parent); + TQGridLayout* grid = new TQGridLayout( photographerIdGroup->layout(), 1, 1, KDialog::spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Author:"), photographerIdGroup); + d->authorEdit = new KLineEdit(photographerIdGroup); + d->authorEdit->setValidator(asciiValidator); + d->authorEdit->setMaxLength(32); + label1->setBuddy(d->authorEdit); + grid->addMultiCellWidget(label1, 0, 0, 0, 0); + grid->addMultiCellWidget(d->authorEdit, 0, 0, 1, 1); + TQWhatsThis::add( d->authorEdit, i18n("<p>This field should contain your name, or the name of the person who created the photograph. " + "If it is not appropriate to add the name of the photographer (for example, if the identify of " + "the photographer needs to be protected) the name of a company or organization can also be used. " + "Once saved, this field should not be changed by anyone. This field does not support the use of " + "commas or semi-colons as separator. \nThis field is limited to 32 ASCII characters.</p>")); + + TQLabel *label2 = new TQLabel(i18n("Author Title:"), photographerIdGroup); + d->authorTitleEdit = new KLineEdit(photographerIdGroup); + d->authorTitleEdit->setValidator(asciiValidator); + d->authorTitleEdit->setMaxLength(32); + label2->setBuddy(d->authorTitleEdit); + grid->addMultiCellWidget(label2, 1, 1, 0, 0); + grid->addMultiCellWidget(d->authorTitleEdit, 1, 1, 1, 1); + TQWhatsThis::add( d->authorTitleEdit, i18n("<p>This field should contain the job title of the photographer. Examples might include " + "titles such as: Staff Photographer, Freelance Photographer, or Independent Commercial " + "Photographer. Since this is a qualifier for the Author field, the Author field must also " + "be filled out. \nThis field is limited to 32 ASCII characters.</p>")); + + // -------------------------------------------------------- + + TQGroupBox *creditsGroup = new TQGroupBox(0, TQt::Horizontal, i18n("Credit and Copyright"), parent); + TQGridLayout* grid2 = new TQGridLayout( creditsGroup->layout(), 2, 1, KDialog::spacingHint()); + + TQLabel *label3 = new TQLabel(i18n("Credit:"), creditsGroup); + d->creditEdit = new KLineEdit(creditsGroup); + d->creditEdit->setValidator(asciiValidator); + d->creditEdit->setMaxLength(32); + label3->setBuddy(d->creditEdit); + grid2->addMultiCellWidget(label3, 0, 0, 0, 0); + grid2->addMultiCellWidget(d->creditEdit, 0, 0, 1, 1); + TQWhatsThis::add( d->creditEdit, i18n("<p>(synonymous to Provider): Use the Provider field to identify who is providing the photograph. " + "This does not necessarily have to be the author. If a photographer is working for a news agency " + "such as Reuters or the Associated Press, these organizations could be listed here as they are " + "\"providing\" the image for use by others. If the image is a stock photograph, then the group " + "(agency) involved in supplying the image should be listed here. " + "\nThis field is limited to 32 ASCII characters.</p>")); + + TQLabel *label4 = new TQLabel(i18n("Source:"), creditsGroup); + d->sourceEdit = new KLineEdit(creditsGroup); + d->sourceEdit->setValidator(asciiValidator); + d->sourceEdit->setMaxLength(32); + label4->setBuddy(d->sourceEdit); + grid2->addMultiCellWidget(label4, 1, 1, 0, 0); + grid2->addMultiCellWidget(d->sourceEdit, 1, 1, 1, 1); + TQWhatsThis::add( d->sourceEdit, i18n("<p>The Source field should be used to identify the original owner or copyright holder of the " + "photograph. The value of this field should never be changed after the information is entered " + "following the image's creation. While not yet enforced by the custom panels, you should consider " + "this to be a \"write-once\" field. The source could be an individual, an agency, or a " + "member of an agency. To aid in later searches, it is suggested to separate any slashes " + "\"/\" with a blank space. Use the form \"photographer / agency\" rather than " + "\"photographer/agency.\" Source may also be different from Creator and from the names " + "listed in the Copyright Notice.\nThis field is limited to 32 ASCII characters.</p>")); + + TQLabel *label5 = new TQLabel(i18n("Copyright:"), creditsGroup); + d->copyrightEdit = new KLineEdit(creditsGroup); + d->copyrightEdit->setValidator(asciiValidator); + d->copyrightEdit->setMaxLength(128); + label5->setBuddy(d->copyrightEdit); + grid2->addMultiCellWidget(label5, 2, 2, 0, 0); + grid2->addMultiCellWidget(d->copyrightEdit, 2, 2, 1, 1); + TQWhatsThis::add( d->copyrightEdit, i18n("<p>The Copyright Notice should contain any necessary copyright notice for claiming the intellectual " + "property, and should identify the current owner(s) of the copyright for the photograph. Usually, " + "this would be the photographer, but if the image was done by an employee or as work-for-hire, " + "then the agency or company should be listed. Use the form appropriate to your country. USA: " + "© {date of first publication} name of copyright owner, as in \"©2005 John Doe.\" " + "Note, the word \"copyright\" or the abbreviation \"copr\" may be used in place of the © symbol. " + "In some foreign countries only the copyright symbol is recognized and the abbreviation does not work. " + "Furthermore the copyright symbol must be a full circle with a \"c\" inside; using something like (c) " + "where the parentheses form a partial circle is not sufficient. For additional protection worldwide, " + "use of the phrase, \"all rights reserved\" following the notice above is encouraged. \nIn Europe " + "you would use: Copyright {Year} {Copyright owner}, all rights reserved. \nIn Japan, for maximum " + "protection, the following three items should appear in the copyright field of the IPTC Core: " + "(a) the word, Copyright; (b) year of the first publication; and (c) name of the author. " + "You may also wish to include the phrase \"all rights reserved.\"\n" + "This field is limited to 128 ASCII characters.</p>")); + + // -------------------------------------------------------- + + KActiveLabel *note = new KActiveLabel(i18n("<b>Note: These informations are used to set " + "<b><a href='http://en.wikipedia.org/wiki/IPTC'>IPTC</a></b> tags contents. " + "IPTC text tags only support the printable " + "<b><a href='http://en.wikipedia.org/wiki/Ascii'>ASCII</a></b> " + "characters set and limit strings size. " + "Use contextual help for details.</b>"), parent); + + // -------------------------------------------------------- + + layout->addWidget(photographerIdGroup); + layout->addWidget(creditsGroup); + layout->addWidget(note); + layout->addStretch(); + + readSettings(); +} + +SetupIdentity::~SetupIdentity() +{ + delete d; +} + +void SetupIdentity::applySettings() +{ + AlbumSettings* settings = AlbumSettings::instance(); + if (!settings) return; + + settings->setIptcAuthor(d->authorEdit->text()); + settings->setIptcAuthorTitle(d->authorTitleEdit->text()); + settings->setIptcCredit(d->creditEdit->text()); + settings->setIptcSource(d->sourceEdit->text()); + settings->setIptcCopyright(d->copyrightEdit->text()); + settings->saveSettings(); +} + +void SetupIdentity::readSettings() +{ + AlbumSettings* settings = AlbumSettings::instance(); + if (!settings) return; + + d->authorEdit->setText(settings->getIptcAuthor()); + d->authorTitleEdit->setText(settings->getIptcAuthorTitle()); + d->creditEdit->setText(settings->getIptcCredit()); + d->sourceEdit->setText(settings->getIptcSource()); + d->copyrightEdit->setText(settings->getIptcCopyright()); +} + +} // namespace Digikam + diff --git a/src/utilities/setup/setupidentity.h b/src/utilities/setup/setupidentity.h new file mode 100644 index 00000000..ae26dc77 --- /dev/null +++ b/src/utilities/setup/setupidentity.h @@ -0,0 +1,60 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-07-04 + * Description : default IPTC identity setup tab. + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUP_IDENTITY_H +#define SETUP_IDENTITY_H + +// TQt includes. + +#include <tqwidget.h> + +namespace Digikam +{ + +class SetupIdentityPriv; + +class SetupIdentity : public TQWidget +{ + TQ_OBJECT + + +public: + + SetupIdentity(TQWidget* parent = 0); + ~SetupIdentity(); + + void applySettings(); + +private: + + void readSettings(); + +private: + + SetupIdentityPriv* d; + +}; + +} // namespace Digikam + +#endif // SETUP_IDENTITY_H diff --git a/src/utilities/setup/setupiofiles.cpp b/src/utilities/setup/setupiofiles.cpp new file mode 100644 index 00000000..6cdf23de --- /dev/null +++ b/src/utilities/setup/setupiofiles.cpp @@ -0,0 +1,137 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-01-23 + * Description : setup image editor output files settings. + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlayout.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kdialog.h> +#include <tdeconfig.h> +#include <tdeapplication.h> +#include <kseparator.h> + +// Local includes. + +#include "jpegsettings.h" +#include "pngsettings.h" +#include "tiffsettings.h" +#include "jp2ksettings.h" +#include "setupiofiles.h" +#include "setupiofiles.moc" + +namespace Digikam +{ + +class SetupIOFilesPriv +{ +public: + + + SetupIOFilesPriv() + { + JPEGOptions = 0; + PNGOptions = 0; + TIFFOptions = 0; + JPEG2000Options = 0; + } + + JPEGSettings *JPEGOptions; + + PNGSettings *PNGOptions; + + TIFFSettings *TIFFOptions; + + JP2KSettings *JPEG2000Options; +}; + +SetupIOFiles::SetupIOFiles(TQWidget* parent ) + : TQWidget(parent) +{ + d = new SetupIOFilesPriv; + + TQVBoxLayout* vbox = new TQVBoxLayout(parent); + + //-- JPEG Settings ------------------------------------------------------ + + d->JPEGOptions = new JPEGSettings(parent); + KSeparator *line1 = new KSeparator(TQt::Horizontal, parent); + vbox->addWidget(d->JPEGOptions); + vbox->addWidget(line1); + + //-- PNG Settings ------------------------------------------------------- + + d->PNGOptions = new PNGSettings(parent); + KSeparator *line2 = new KSeparator(TQt::Horizontal, parent); + vbox->addWidget(d->PNGOptions); + vbox->addWidget(line2); + + //-- TIFF Settings ------------------------------------------------------ + + d->TIFFOptions = new TIFFSettings(parent); + KSeparator *line3 = new KSeparator(TQt::Horizontal, parent); + vbox->addWidget(d->TIFFOptions); + vbox->addWidget(line3); + + //-- JPEG 2000 Settings ------------------------------------------------- + + d->JPEG2000Options = new JP2KSettings(parent); + vbox->addWidget(d->JPEG2000Options); + + vbox->addStretch(10); + readSettings(); +} + +SetupIOFiles::~SetupIOFiles() +{ + delete d; +} + +void SetupIOFiles::applySettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("ImageViewer Settings"); + config->writeEntry("JPEGCompression", d->JPEGOptions->getCompressionValue()); + config->writeEntry("JPEGSubSampling", d->JPEGOptions->getSubSamplingValue()); + config->writeEntry("PNGCompression", d->PNGOptions->getCompressionValue()); + config->writeEntry("TIFFCompression", d->TIFFOptions->getCompression()); + config->writeEntry("JPEG2000Compression", d->JPEG2000Options->getCompressionValue()); + config->writeEntry("JPEG2000LossLess", d->JPEG2000Options->getLossLessCompression()); + config->sync(); +} + +void SetupIOFiles::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("ImageViewer Settings"); + d->JPEGOptions->setCompressionValue( config->readNumEntry("JPEGCompression", 75) ); + d->JPEGOptions->setSubSamplingValue( config->readNumEntry("JPEGSubSampling", 1) ); // Medium subsampling + d->PNGOptions->setCompressionValue( config->readNumEntry("PNGCompression", 9) ); + d->TIFFOptions->setCompression(config->readBoolEntry("TIFFCompression", false)); + d->JPEG2000Options->setCompressionValue( config->readNumEntry("JPEG2000Compression", 75) ); + d->JPEG2000Options->setLossLessCompression( config->readBoolEntry("JPEG2000LossLess", true) ); +} + +} // namespace Digikam diff --git a/src/utilities/setup/setupiofiles.h b/src/utilities/setup/setupiofiles.h new file mode 100644 index 00000000..060dbba1 --- /dev/null +++ b/src/utilities/setup/setupiofiles.h @@ -0,0 +1,63 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-01-23 + * Description : setup image editor output files settings. + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUPIOFILES_H +#define SETUPIOFILES_H + +// TQt includes. + +#include <tqwidget.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class SetupIOFilesPriv; + +class DIGIKAM_EXPORT SetupIOFiles : public TQWidget +{ + TQ_OBJECT + + +public: + + SetupIOFiles(TQWidget* parent = 0); + ~SetupIOFiles(); + + void applySettings(); + +private: + + void readSettings(); + +private: + + SetupIOFilesPriv* d; +}; + +} // namespace Digikam + +#endif // SETUPIOFILES_H diff --git a/src/utilities/setup/setuplighttable.cpp b/src/utilities/setup/setuplighttable.cpp new file mode 100644 index 00000000..0ed01eab --- /dev/null +++ b/src/utilities/setup/setuplighttable.cpp @@ -0,0 +1,133 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-05-11 + * Description : setup Light Table tab. + * + * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlayout.h> +#include <tqcolor.h> +#include <tqhbox.h> +#include <tqvgroupbox.h> +#include <tqlabel.h> +#include <tqwhatsthis.h> +#include <tqcheckbox.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kdialog.h> +#include <tdeconfig.h> +#include <tdeapplication.h> + +// Local includes. + +#include "setuplighttable.h" +#include "setuplighttable.moc" + +namespace Digikam +{ +class SetupLightTablePriv +{ +public: + + SetupLightTablePriv() + { + hideToolBar = 0; + autoSyncPreview = 0; + autoLoadOnRightPanel = 0; + loadFullImageSize = 0; + } + + TQCheckBox *hideToolBar; + TQCheckBox *autoSyncPreview; + TQCheckBox *autoLoadOnRightPanel; + TQCheckBox *loadFullImageSize; +}; + +SetupLightTable::SetupLightTable(TQWidget* parent ) + : TQWidget(parent) +{ + d = new SetupLightTablePriv; + TQVBoxLayout *layout = new TQVBoxLayout( parent, 0, KDialog::spacingHint() ); + + // -------------------------------------------------------- + + TQVGroupBox *interfaceOptionsGroup = new TQVGroupBox(i18n("Interface Options"), parent); + + + d->autoSyncPreview = new TQCheckBox(i18n("Synchronize panels automatically"), interfaceOptionsGroup); + TQWhatsThis::add( d->autoSyncPreview, i18n("<p>Set this option to automatically synchronize " + "zooming and panning between left and right panels if the images have " + "the same size.")); + + d->autoLoadOnRightPanel = new TQCheckBox(i18n("Selecting a thumbbar item loads image to the right panel"), + interfaceOptionsGroup); + TQWhatsThis::add( d->autoLoadOnRightPanel, i18n("<p>Set this option to automatically load an image " + "into the right panel when the corresponding item is selected on the thumbbar.")); + + d->loadFullImageSize = new TQCheckBox(i18n("Load full image size"), interfaceOptionsGroup); + TQWhatsThis::add( d->loadFullImageSize, i18n("<p>Set this option to load full image size " + "into the preview panel instead of a reduced size. Because this option will take more time " + "to load images, use it only if you have a fast computer.")); + + d->hideToolBar = new TQCheckBox(i18n("H&ide toolbar in fullscreen mode"), interfaceOptionsGroup); + + // -------------------------------------------------------- + + layout->addWidget(interfaceOptionsGroup); + layout->addStretch(); + + // -------------------------------------------------------- + + readSettings(); +} + +SetupLightTable::~SetupLightTable() +{ + delete d; +} + +void SetupLightTable::readSettings() +{ + TDEConfig* config = kapp->config(); + TQColor Black(TQt::black); + TQColor White(TQt::white); + config->setGroup("LightTable Settings"); + d->hideToolBar->setChecked(config->readBoolEntry("FullScreen Hide ToolBar", false)); + d->autoSyncPreview->setChecked(config->readBoolEntry("Auto Sync Preview", true)); + d->autoLoadOnRightPanel->setChecked(config->readBoolEntry("Auto Load Right Panel", true)); + d->loadFullImageSize->setChecked(config->readBoolEntry("Load Full Image size", false)); +} + +void SetupLightTable::applySettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("LightTable Settings"); + config->writeEntry("FullScreen Hide ToolBar", d->hideToolBar->isChecked()); + config->writeEntry("Auto Sync Preview", d->autoSyncPreview->isChecked()); + config->writeEntry("Auto Load Right Panel", d->autoLoadOnRightPanel->isChecked()); + config->writeEntry("Load Full Image size", d->loadFullImageSize->isChecked()); + config->sync(); +} + +} // namespace Digikam + diff --git a/src/utilities/setup/setuplighttable.h b/src/utilities/setup/setuplighttable.h new file mode 100644 index 00000000..20af9a64 --- /dev/null +++ b/src/utilities/setup/setuplighttable.h @@ -0,0 +1,59 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-05-11 + * Description : setup Light Table tab. + * + * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUPLIGHTTABLE_H +#define SETUPLIGHTTABLE_H + +// TQt includes. + +#include <tqwidget.h> + +namespace Digikam +{ + +class SetupLightTablePriv; + +class SetupLightTable : public TQWidget +{ + TQ_OBJECT + + +public: + + SetupLightTable(TQWidget* parent = 0); + ~SetupLightTable(); + + void applySettings(); + +private: + + void readSettings(); + +private: + + SetupLightTablePriv* d; +}; + +} // namespace Digikam + +#endif // SETUPLIGHTTABLE_H diff --git a/src/utilities/setup/setupmetadata.cpp b/src/utilities/setup/setupmetadata.cpp new file mode 100644 index 00000000..e4f74ede --- /dev/null +++ b/src/utilities/setup/setupmetadata.cpp @@ -0,0 +1,238 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-08-03 + * Description : setup Metadata tab. + * + * Copyright (C) 2003-2004 by Ralf Holzer <ralf at well.com> + * Copyright (C) 2003-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlayout.h> +#include <tqvbuttongroup.h> +#include <tqvgroupbox.h> +#include <tqhgroupbox.h> +#include <tqgroupbox.h> +#include <tqcheckbox.h> +#include <tqlabel.h> +#include <tqwhatsthis.h> +#include <tqtooltip.h> +#include <tqhbox.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kactivelabel.h> +#include <kdialog.h> +#include <kurllabel.h> +#include <kiconloader.h> +#include <tdeglobalsettings.h> +#include <kstandarddirs.h> +#include <tdeapplication.h> + +// Local includes. + +#include "albumsettings.h" +#include "setupmetadata.h" +#include "setupmetadata.moc" + +namespace Digikam +{ + +class SetupMetadataPriv +{ +public: + + SetupMetadataPriv() + { + ExifAutoRotateAsChanged = false; + saveCommentsBox = 0; + ExifRotateBox = 0; + ExifSetOrientationBox = 0; + saveRatingBox = 0; + saveTagsIptcBox = 0; + saveDateTimeBox = 0; + savePhotographerIdIptcBox = 0; + saveCreditsIptcBox = 0; + } + + bool ExifAutoRotateAsChanged; + bool ExifAutoRotateOrg; + + TQCheckBox *saveCommentsBox; + TQCheckBox *ExifRotateBox; + TQCheckBox *ExifSetOrientationBox; + TQCheckBox *saveRatingBox; + TQCheckBox *saveTagsIptcBox; + TQCheckBox *saveDateTimeBox; + TQCheckBox *savePhotographerIdIptcBox; + TQCheckBox *saveCreditsIptcBox; +}; + +SetupMetadata::SetupMetadata(TQWidget* parent ) + : TQWidget(parent) +{ + d = new SetupMetadataPriv; + TQVBoxLayout *mainLayout = new TQVBoxLayout(parent, 0, KDialog::spacingHint()); + + // -------------------------------------------------------- + + TQGroupBox *ExifGroup = new TQGroupBox(1, TQt::Horizontal, i18n("EXIF Actions"), parent); + + d->ExifRotateBox = new TQCheckBox(ExifGroup); + d->ExifRotateBox->setText(i18n("Show images/thumbs &rotated according to orientation tag")); + + d->ExifSetOrientationBox = new TQCheckBox(ExifGroup); + d->ExifSetOrientationBox->setText(i18n("Set orientation tag to normal after rotate/flip")); + + // -------------------------------------------------------- + + TQGroupBox *IptcGroup = new TQGroupBox(1, TQt::Horizontal, i18n("IPTC Actions"), parent); + + d->saveTagsIptcBox = new TQCheckBox(IptcGroup); + d->saveTagsIptcBox->setText(i18n("&Save image tags as \"Keywords\" tag")); + TQWhatsThis::add( d->saveTagsIptcBox, i18n("<p>Turn this option on to store the image tags " + "in the IPTC <i>Keywords</i> tag.")); + + d->savePhotographerIdIptcBox = new TQCheckBox(IptcGroup); + d->savePhotographerIdIptcBox->setText(i18n("&Save default photographer identity as tags")); + TQWhatsThis::add( d->savePhotographerIdIptcBox, i18n("<p>Turn this option on to store the default " + "photographer identity in the IPTC tags. You can set this " + "value in the Identity setup page.")); + + d->saveCreditsIptcBox = new TQCheckBox(IptcGroup); + d->saveCreditsIptcBox->setText(i18n("&Save default credit and copyright identity as tags")); + TQWhatsThis::add( d->saveCreditsIptcBox, i18n("<p>Turn this option on to store the default " + "credit and copyright identity in the IPTC tags. " + "You can set this value in the Identity setup page.")); + + // -------------------------------------------------------- + + TQGroupBox *commonGroup = new TQGroupBox(1, TQt::Horizontal, i18n("Common Metadata Actions"), parent); + + d->saveCommentsBox = new TQCheckBox(commonGroup); + d->saveCommentsBox->setText(i18n("&Save image captions as embedded text")); + TQWhatsThis::add( d->saveCommentsBox, i18n("<p>Turn this option on to store image captions " + "in the JFIF section, EXIF tag, and IPTC tag.")); + + d->saveDateTimeBox = new TQCheckBox(commonGroup); + d->saveDateTimeBox->setText(i18n("&Save image timestamps as tags")); + TQWhatsThis::add( d->saveDateTimeBox, i18n("<p>Turn this option on to store the image date and time " + "in the EXIF and IPTC tags.")); + + d->saveRatingBox = new TQCheckBox(commonGroup); + d->saveRatingBox->setText(i18n("&Save image rating as tags")); + TQWhatsThis::add( d->saveRatingBox, i18n("<p>Turn this option on to store the image rating " + "in the EXIF tag and IPTC <i>Urgency</i> tag.")); + + // -------------------------------------------------------- + + TQHBox *hbox = new TQHBox(parent); + + KURLLabel *exiv2LogoLabel = new KURLLabel(hbox); + exiv2LogoLabel->setText(TQString()); + exiv2LogoLabel->setURL("http://www.exiv2.org"); + TDEGlobal::dirs()->addResourceType("logo-exiv2", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("logo-exiv2", "logo-exiv2.png"); + exiv2LogoLabel->setPixmap( TQPixmap( directory + "logo-exiv2.png" ) ); + TQToolTip::add(exiv2LogoLabel, i18n("Visit Exiv2 project website")); + + KActiveLabel* explanation = new KActiveLabel(hbox); + explanation->setText(i18n("<p><b><a href='http://en.wikipedia.org/wiki/Exif'>EXIF</a></b> is " + "a standard used by most digital cameras today to store technical " + "informations about photograph.</p>" + "<p><b><a href='http://en.wikipedia.org/wiki/IPTC'>IPTC</a></b> is " + "an standard used in digital photography to store " + "photographer informations in pictures.</p>")); + + mainLayout->addWidget(ExifGroup); + mainLayout->addWidget(IptcGroup); + mainLayout->addWidget(commonGroup); + mainLayout->addSpacing(KDialog::spacingHint()); + mainLayout->addWidget(hbox); + mainLayout->addStretch(); + + readSettings(); + + // -------------------------------------------------------- + + connect(exiv2LogoLabel, TQ_SIGNAL(leftClickedURL(const TQString&)), + this, TQ_SLOT(processExiv2URL(const TQString&))); + + connect(d->ExifRotateBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotExifAutoRotateToggled(bool))); +} + +SetupMetadata::~SetupMetadata() +{ + delete d; +} + +void SetupMetadata::processExiv2URL(const TQString& url) +{ + TDEApplication::kApplication()->invokeBrowser(url); +} + +void SetupMetadata::applySettings() +{ + AlbumSettings* settings = AlbumSettings::instance(); + if (!settings) return; + + settings->setExifRotate(d->ExifRotateBox->isChecked()); + settings->setExifSetOrientation(d->ExifSetOrientationBox->isChecked()); + settings->setSaveComments(d->saveCommentsBox->isChecked()); + settings->setSaveDateTime(d->saveDateTimeBox->isChecked()); + settings->setSaveRating(d->saveRatingBox->isChecked()); + settings->setSaveIptcTags(d->saveTagsIptcBox->isChecked()); + settings->setSaveIptcPhotographerId(d->savePhotographerIdIptcBox->isChecked()); + settings->setSaveIptcCredits(d->saveCreditsIptcBox->isChecked()); + + settings->saveSettings(); +} + +void SetupMetadata::readSettings() +{ + AlbumSettings* settings = AlbumSettings::instance(); + if (!settings) return; + + d->ExifAutoRotateOrg = settings->getExifRotate(); + d->ExifRotateBox->setChecked(d->ExifAutoRotateOrg); + d->ExifSetOrientationBox->setChecked(settings->getExifSetOrientation()); + d->saveCommentsBox->setChecked(settings->getSaveComments()); + d->saveDateTimeBox->setChecked(settings->getSaveDateTime()); + d->saveRatingBox->setChecked(settings->getSaveRating()); + d->saveTagsIptcBox->setChecked(settings->getSaveIptcTags()); + d->savePhotographerIdIptcBox->setChecked(settings->getSaveIptcPhotographerId()); + d->saveCreditsIptcBox->setChecked(settings->getSaveIptcCredits()); +} + +bool SetupMetadata::exifAutoRotateAsChanged() +{ + return d->ExifAutoRotateAsChanged; +} + +void SetupMetadata::slotExifAutoRotateToggled(bool b) +{ + if ( b != d->ExifAutoRotateOrg) + d->ExifAutoRotateAsChanged = true; + else + d->ExifAutoRotateAsChanged = false; +} + +} // namespace Digikam diff --git a/src/utilities/setup/setupmetadata.h b/src/utilities/setup/setupmetadata.h new file mode 100644 index 00000000..45511065 --- /dev/null +++ b/src/utilities/setup/setupmetadata.h @@ -0,0 +1,67 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-08-03 + * Description : setup Metadata tab. + * + * Copyright (C) 2003-2004 by Ralf Holzer <ralf at well.com> + * Copyright (C) 2003-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUPMETADATA_H +#define SETUPMETADATA_H + +// TQt includes. + +#include <tqwidget.h> + +namespace Digikam +{ + +class SetupMetadataPriv; + +class SetupMetadata : public TQWidget +{ + TQ_OBJECT + + +public: + + SetupMetadata(TQWidget* parent = 0); + ~SetupMetadata(); + + void applySettings(); + + bool exifAutoRotateAsChanged(); + +private: + + void readSettings(); + +private slots: + + void processExiv2URL(const TQString&); + void slotExifAutoRotateToggled(bool); + +private: + + SetupMetadataPriv* d; +}; + +} // namespace Digikam + +#endif // SETUPMETADATA_H diff --git a/src/utilities/setup/setupmime.cpp b/src/utilities/setup/setupmime.cpp new file mode 100644 index 00000000..6ac069e3 --- /dev/null +++ b/src/utilities/setup/setupmime.cpp @@ -0,0 +1,280 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-05-03 + * Description : mime types setup tab + * + * Copyright (C) 2004-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlayout.h> +#include <tqhbox.h> +#include <tqhgroupbox.h> +#include <tqgroupbox.h> +#include <tqlabel.h> +#include <tqlineedit.h> +#include <tqwhatsthis.h> +#include <tqtoolbutton.h> +#include <tqtooltip.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kdialog.h> +#include <klineeditdlg.h> +#include <kiconloader.h> + +// Local includes. + +#include "albumsettings.h" +#include "setupmime.h" +#include "setupmime.moc" + +namespace Digikam +{ + +class SetupMimePriv +{ +public: + + SetupMimePriv() + { + imageFileFilterEdit = 0; + movieFileFilterEdit = 0; + audioFileFilterEdit = 0; + rawFileFilterEdit = 0; + revertImageFileFilterBtn = 0; + revertMovieFileFilterBtn = 0; + revertAudioFileFilterBtn = 0; + revertRawFileFilterBtn = 0; + } + + TQToolButton *revertImageFileFilterBtn; + TQToolButton *revertMovieFileFilterBtn; + TQToolButton *revertAudioFileFilterBtn; + TQToolButton *revertRawFileFilterBtn; + + TQLineEdit *imageFileFilterEdit; + TQLineEdit *movieFileFilterEdit; + TQLineEdit *audioFileFilterEdit; + TQLineEdit *rawFileFilterEdit; +}; + +SetupMime::SetupMime(TQWidget* parent ) + : TQWidget(parent) +{ + d = new SetupMimePriv; + TQVBoxLayout *layout = new TQVBoxLayout(parent, 0, KDialog::spacingHint()); + + // -------------------------------------------------------- + + TQGroupBox *imageFileFilterBox = new TQGroupBox(0, TQt::Horizontal, i18n("Image Files"), parent); + TQGridLayout* grid1 = new TQGridLayout(imageFileFilterBox->layout(), 1, 1, KDialog::spacingHint()); + + TQLabel *logoLabel1 = new TQLabel(imageFileFilterBox); + logoLabel1->setPixmap(DesktopIcon("image-x-generic")); + + TQLabel *imageFileFilterLabel = new TQLabel(imageFileFilterBox); + imageFileFilterLabel->setText(i18n("Show only &image files with extensions:")); + + TQHBox *hbox1 = new TQHBox(imageFileFilterBox); + d->imageFileFilterEdit = new TQLineEdit(hbox1); + TQWhatsThis::add( d->imageFileFilterEdit, i18n("<p>Here you can set the extensions of image files " + "to be displayed in Albums (such as JPEG or TIFF); " + "clicking on these files will " + "open them in the digiKam Image Editor.")); + imageFileFilterLabel->setBuddy(d->imageFileFilterEdit); + hbox1->setStretchFactor(d->imageFileFilterEdit, 10); + + d->revertImageFileFilterBtn = new TQToolButton(hbox1); + d->revertImageFileFilterBtn->setIconSet(SmallIcon("reload_page")); + TQToolTip::add(d->revertImageFileFilterBtn, i18n("Revert to default settings")); + + grid1->addMultiCellWidget(logoLabel1, 0, 1, 0, 0); + grid1->addMultiCellWidget(imageFileFilterLabel, 0, 0, 1, 1); + grid1->addMultiCellWidget(hbox1, 1, 1, 1, 1); + grid1->setColStretch(1, 10); + + layout->addWidget(imageFileFilterBox); + + // -------------------------------------------------------- + + TQGroupBox *movieFileFilterBox = new TQGroupBox(0, TQt::Horizontal, i18n("Movie Files"), parent); + TQGridLayout* grid2 = new TQGridLayout(movieFileFilterBox->layout(), 1, 1, KDialog::spacingHint()); + + TQLabel *logoLabel2 = new TQLabel(movieFileFilterBox); + logoLabel2->setPixmap(DesktopIcon("video-x-generic")); + + TQLabel *movieFileFilterLabel = new TQLabel(movieFileFilterBox); + movieFileFilterLabel->setText(i18n("Show only &movie files with extensions:")); + + TQHBox *hbox2 = new TQHBox(movieFileFilterBox); + d->movieFileFilterEdit = new TQLineEdit(hbox2); + TQWhatsThis::add( d->movieFileFilterEdit, i18n("<p>Here you can set the extensions of movie files " + "to be displayed in Albums (such as MPEG or AVI); " + "clicking on these files will " + "open them with the default TDE movie player.")); + movieFileFilterLabel->setBuddy(d->movieFileFilterEdit); + hbox2->setStretchFactor(d->movieFileFilterEdit, 10); + + d->revertMovieFileFilterBtn = new TQToolButton(hbox2); + d->revertMovieFileFilterBtn->setIconSet(SmallIcon("reload_page")); + TQToolTip::add(d->revertMovieFileFilterBtn, i18n("Revert to default settings")); + + grid2->addMultiCellWidget(logoLabel2, 0, 1, 0, 0); + grid2->addMultiCellWidget(movieFileFilterLabel, 0, 0, 1, 1); + grid2->addMultiCellWidget(hbox2, 1, 1, 1, 1); + grid2->setColStretch(1, 10); + + layout->addWidget(movieFileFilterBox); + + // -------------------------------------------------------- + + TQGroupBox *audioFileFilterBox = new TQGroupBox(0, TQt::Horizontal, i18n("Audio Files"), parent); + TQGridLayout* grid3 = new TQGridLayout(audioFileFilterBox->layout(), 1, 1, KDialog::spacingHint()); + + TQLabel *logoLabel3 = new TQLabel(audioFileFilterBox); + logoLabel3->setPixmap(DesktopIcon("audio-x-generic")); + + TQLabel *audioFileFilterLabel = new TQLabel(audioFileFilterBox); + audioFileFilterLabel->setText(i18n("Show only &audio files with extensions:")); + + TQHBox *hbox3 = new TQHBox(audioFileFilterBox); + d->audioFileFilterEdit = new TQLineEdit(hbox3); + TQWhatsThis::add( d->audioFileFilterEdit, i18n("<p>Here you can set the extensions of audio files " + "to be displayed in Albums (such as MP3 or OGG); " + "clicking on these files will " + "open them with the default TDE audio player.")); + audioFileFilterLabel->setBuddy(d->audioFileFilterEdit); + hbox3->setStretchFactor(d->audioFileFilterEdit, 10); + + d->revertAudioFileFilterBtn = new TQToolButton(hbox3); + d->revertAudioFileFilterBtn->setIconSet(SmallIcon("reload_page")); + TQToolTip::add(d->revertAudioFileFilterBtn, i18n("Revert to default settings")); + + grid3->addMultiCellWidget(logoLabel3, 0, 1, 0, 0); + grid3->addMultiCellWidget(audioFileFilterLabel, 0, 0, 1, 1); + grid3->addMultiCellWidget(hbox3, 1, 1, 1, 1); + grid3->setColStretch(1, 10); + + layout->addWidget(audioFileFilterBox); + + // -------------------------------------------------------- + + TQGroupBox *rawFileFilterBox = new TQGroupBox(0, TQt::Horizontal, i18n("RAW Files"), parent); + TQGridLayout* grid4 = new TQGridLayout(rawFileFilterBox->layout(), 1, 1, KDialog::spacingHint()); + + TQLabel *logoLabel4 = new TQLabel(rawFileFilterBox); + logoLabel4->setPixmap(DesktopIcon("kdcraw")); + + TQLabel *rawFileFilterLabel = new TQLabel(rawFileFilterBox); + rawFileFilterLabel->setText(i18n("Show only &RAW files with extensions:")); + + TQHBox *hbox4 = new TQHBox(rawFileFilterBox); + d->rawFileFilterEdit = new TQLineEdit(hbox4); + TQWhatsThis::add( d->rawFileFilterEdit, i18n("<p>Here you can set the extensions of RAW image files " + "to be displayed in Albums (such as CRW, for Canon cameras, " + "or NEF, for Nikon cameras).")); + rawFileFilterLabel->setBuddy(d->rawFileFilterEdit); + hbox4->setStretchFactor(d->rawFileFilterEdit, 10); + + d->revertRawFileFilterBtn = new TQToolButton(hbox4); + d->revertRawFileFilterBtn->setIconSet(SmallIcon("reload_page")); + TQToolTip::add(d->revertRawFileFilterBtn, i18n("Revert to default settings")); + + grid4->addMultiCellWidget(logoLabel4, 0, 1, 0, 0); + grid4->addMultiCellWidget(rawFileFilterLabel, 0, 0, 1, 1); + grid4->addMultiCellWidget(hbox4, 1, 1, 1, 1); + grid4->setColStretch(1, 10); + + layout->addWidget(rawFileFilterBox); + layout->addStretch(); + + // -------------------------------------------------------- + + connect(d->revertImageFileFilterBtn, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotRevertImageFileFilter())); + + connect(d->revertMovieFileFilterBtn, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotRevertMovieFileFilter())); + + connect(d->revertAudioFileFilterBtn, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotRevertAudioFileFilter())); + + connect(d->revertRawFileFilterBtn, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotRevertRawFileFilter())); + + // -------------------------------------------------------- + + readSettings(); +} + +SetupMime::~SetupMime() +{ + delete d; +} + +void SetupMime::applySettings() +{ + AlbumSettings* settings = AlbumSettings::instance(); + + if (!settings) return; + + settings->setImageFileFilter(d->imageFileFilterEdit->text()); + settings->setMovieFileFilter(d->movieFileFilterEdit->text()); + settings->setAudioFileFilter(d->audioFileFilterEdit->text()); + settings->setRawFileFilter(d->rawFileFilterEdit->text()); + + settings->saveSettings(); +} + +void SetupMime::readSettings() +{ + AlbumSettings* settings = AlbumSettings::instance(); + + if (!settings) return; + + d->imageFileFilterEdit->setText(settings->getImageFileFilter()); + d->movieFileFilterEdit->setText(settings->getMovieFileFilter()); + d->audioFileFilterEdit->setText(settings->getAudioFileFilter()); + d->rawFileFilterEdit->setText(settings->getRawFileFilter()); +} + +void SetupMime::slotRevertImageFileFilter() +{ + d->imageFileFilterEdit->setText(AlbumSettings::instance()->getDefaultImageFileFilter()); +} + +void SetupMime::slotRevertMovieFileFilter() +{ + d->movieFileFilterEdit->setText(AlbumSettings::instance()->getDefaultMovieFileFilter()); +} + +void SetupMime::slotRevertAudioFileFilter() +{ + d->audioFileFilterEdit->setText(AlbumSettings::instance()->getDefaultAudioFileFilter()); +} + +void SetupMime::slotRevertRawFileFilter() +{ + d->rawFileFilterEdit->setText(AlbumSettings::instance()->getDefaultRawFileFilter()); +} + +} // namespace Digikam + diff --git a/src/utilities/setup/setupmime.h b/src/utilities/setup/setupmime.h new file mode 100644 index 00000000..3bff1525 --- /dev/null +++ b/src/utilities/setup/setupmime.h @@ -0,0 +1,66 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-05-03 + * Description : mime types setup tab. + * + * Copyright (C) 2004-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUPMIME_H +#define SETUPMIME_H + +// TQt includes. + +#include <tqwidget.h> + +namespace Digikam +{ + +class SetupMimePriv; + +class SetupMime : public TQWidget +{ + TQ_OBJECT + + +public: + + SetupMime(TQWidget* parent = 0); + ~SetupMime(); + + void applySettings(); + +private: + + void readSettings(); + +private slots: + + void slotRevertImageFileFilter(); + void slotRevertMovieFileFilter(); + void slotRevertAudioFileFilter(); + void slotRevertRawFileFilter(); + +private: + + SetupMimePriv* d; +}; + +} // namespace Digikam + +#endif // SETUPMIME_H diff --git a/src/utilities/setup/setupmisc.cpp b/src/utilities/setup/setupmisc.cpp new file mode 100644 index 00000000..8dc16e96 --- /dev/null +++ b/src/utilities/setup/setupmisc.cpp @@ -0,0 +1,124 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-23 + * Description : mics configuration setup tab + * + * Copyright (C) 2004 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlayout.h> +#include <tqvgroupbox.h> +#include <tqcheckbox.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kdialog.h> + +// Local includes. + +#include "albumsettings.h" +#include "setupmisc.h" + +namespace Digikam +{ + +class SetupMiscPriv +{ +public: + + SetupMiscPriv() + { + showSplashCheck = 0; + showTrashDeleteDialogCheck = 0; + sidebarApplyDirectlyCheck = 0; + scanAtStart = 0; + } + + TQCheckBox* showSplashCheck; + TQCheckBox* showTrashDeleteDialogCheck; + TQCheckBox* sidebarApplyDirectlyCheck; + TQCheckBox* scanAtStart; +}; + +SetupMisc::SetupMisc(TQWidget* parent) + : TQWidget( parent ) +{ + d = new SetupMiscPriv; + + TQVBoxLayout *mainLayout = new TQVBoxLayout(parent); + TQVBoxLayout *layout = new TQVBoxLayout( this, 0, KDialog::spacingHint() ); + + // -------------------------------------------------------- + + d->showTrashDeleteDialogCheck = new TQCheckBox(i18n("Show confirmation dialog when moving items to the &trash"), this); + layout->addWidget(d->showTrashDeleteDialogCheck); + + // -------------------------------------------------------- + + d->sidebarApplyDirectlyCheck = new TQCheckBox(i18n("Apply changes in the &right sidebar without confirmation"), this); + layout->addWidget(d->sidebarApplyDirectlyCheck); + + // -------------------------------------------------------- + + d->showSplashCheck = new TQCheckBox(i18n("&Show splash screen at startup"), this); + layout->addWidget(d->showSplashCheck); + + // -------------------------------------------------------- + + d->scanAtStart = new TQCheckBox(i18n("&Scan for new items on startup (slows down startup)"), this); + layout->addWidget(d->scanAtStart); + + // -------------------------------------------------------- + + layout->addStretch(); + readSettings(); + adjustSize(); + mainLayout->addWidget(this); +} + +SetupMisc::~SetupMisc() +{ + delete d; +} + +void SetupMisc::applySettings() +{ + AlbumSettings* settings = AlbumSettings::instance(); + + settings->setShowSplashScreen(d->showSplashCheck->isChecked()); + settings->setShowTrashDeleteDialog(d->showTrashDeleteDialogCheck->isChecked()); + settings->setApplySidebarChangesDirectly(d->sidebarApplyDirectlyCheck->isChecked()); + settings->setScanAtStart(d->scanAtStart->isChecked()); + settings->saveSettings(); +} + +void SetupMisc::readSettings() +{ + AlbumSettings* settings = AlbumSettings::instance(); + + d->showSplashCheck->setChecked(settings->getShowSplashScreen()); + d->showTrashDeleteDialogCheck->setChecked(settings->getShowTrashDeleteDialog()); + d->sidebarApplyDirectlyCheck->setChecked(settings->getApplySidebarChangesDirectly()); + d->scanAtStart->setChecked(settings->getScanAtStart()); +} + +} // namespace Digikam diff --git a/src/utilities/setup/setupmisc.h b/src/utilities/setup/setupmisc.h new file mode 100644 index 00000000..92918ba4 --- /dev/null +++ b/src/utilities/setup/setupmisc.h @@ -0,0 +1,58 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-23 + * Description : mics configuration setup tab + * + * Copyright (C) 2004 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUPMISC_H +#define SETUPMISC_H + +// TQt includes. + +#include <tqwidget.h> + +namespace Digikam +{ + +class SetupMiscPriv; + +class SetupMisc : public TQWidget +{ +public: + + SetupMisc(TQWidget* parent); + ~SetupMisc(); + + void applySettings(); + +private: + + void readSettings(); + +private: + + SetupMiscPriv* d; + +}; + +} // namespace Digikam + +#endif /* SETUPMISC_H */ diff --git a/src/utilities/setup/setupplugins.cpp b/src/utilities/setup/setupplugins.cpp new file mode 100644 index 00000000..e0d9c494 --- /dev/null +++ b/src/utilities/setup/setupplugins.cpp @@ -0,0 +1,104 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-01-02 + * Description : setup Kipi plugins tab. + * + * Copyright (C) 2004-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +// TQt includes. + +#include <tqlayout.h> +#include <tqstring.h> +#include <tqgroupbox.h> +#include <tqlabel.h> +#include <tqwhatsthis.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kdialog.h> + +// libkipi includes. + +#include <libkipi/pluginloader.h> +#include <libkipi/version.h> + +// Local includes. + +#include "setupplugins.h" +#include "setupplugins.moc" + +namespace Digikam +{ + +class SetupPluginsPriv +{ +public: + + SetupPluginsPriv() + { + pluginsNumber = 0; + kipiConfig = 0; + } + + TQLabel* pluginsNumber; + + KIPI::ConfigWidget* kipiConfig; +}; + +SetupPlugins::SetupPlugins(TQWidget* parent ) + : TQWidget(parent) +{ + d = new SetupPluginsPriv; + TQVBoxLayout *layout = new TQVBoxLayout(parent); + d->pluginsNumber = new TQLabel(parent); + d->pluginsNumber->setAlignment(TQt::AlignLeft | TQt::AlignVCenter); + + d->kipiConfig = KIPI::PluginLoader::instance()->configWidget( parent ); + TQString pluginsListHelp = i18n("<p>A list of available Kipi plugins appears below."); + TQWhatsThis::add(d->kipiConfig, pluginsListHelp); + + layout->addWidget(d->pluginsNumber); + layout->addWidget(d->kipiConfig); + layout->setMargin(0); + layout->setSpacing(KDialog::spacingHint()); +} + +SetupPlugins::~SetupPlugins() +{ + delete d; +} + +void SetupPlugins::initPlugins(int kipiPluginsNumber) +{ + d->pluginsNumber->setText(i18n("1 Kipi plugin found", + "%n Kipi plugins found", + kipiPluginsNumber)); +} + +void SetupPlugins::applyPlugins() +{ + d->kipiConfig->apply(); +} + +} // namespace Digikam diff --git a/src/utilities/setup/setupplugins.h b/src/utilities/setup/setupplugins.h new file mode 100644 index 00000000..31b71121 --- /dev/null +++ b/src/utilities/setup/setupplugins.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-01-02 + * Description : setup Kipi plugins tab. + * + * Copyright (C) 2004-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUPPLUGINS_H +#define SETUPPLUGINS_H + +// TQt includes. + +#include <tqwidget.h> + +namespace Digikam +{ + +class SetupPluginsPriv; + +class SetupPlugins : public TQWidget +{ + TQ_OBJECT + + +public: + + SetupPlugins(TQWidget* parent = 0); + ~SetupPlugins(); + + void initPlugins(int kipiPluginsNumber); + void applyPlugins(); + +private: + + SetupPluginsPriv* d; +}; + +} // namespace Digikam + +#endif // SETUPPLUGINS_H diff --git a/src/utilities/setup/setupslideshow.cpp b/src/utilities/setup/setupslideshow.cpp new file mode 100644 index 00000000..970d9c67 --- /dev/null +++ b/src/utilities/setup/setupslideshow.cpp @@ -0,0 +1,165 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-21 + * Description : setup tab for slideshow options. + * + * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlayout.h> +#include <tqlabel.h> +#include <tqwhatsthis.h> +#include <tqcheckbox.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kdialog.h> +#include <knuminput.h> +#include <tdeconfig.h> +#include <tdeapplication.h> + +// Local includes. + +#include "setupslideshow.h" +#include "setupslideshow.moc" + +namespace Digikam +{ + +class SetupSlideShowPriv +{ +public: + + SetupSlideShowPriv() + { + delayInput = 0; + startWithCurrent = 0; + loopMode = 0; + printName = 0; + printDate = 0; + printApertureFocal = 0; + printExpoSensitivity = 0; + printMakeModel = 0; + printComment = 0; + } + + TQCheckBox *startWithCurrent; + TQCheckBox *loopMode; + TQCheckBox *printName; + TQCheckBox *printDate; + TQCheckBox *printApertureFocal; + TQCheckBox *printExpoSensitivity; + TQCheckBox *printMakeModel; + TQCheckBox *printComment; + + KIntNumInput *delayInput; +}; + +SetupSlideShow::SetupSlideShow(TQWidget* parent ) + : TQWidget(parent) +{ + d = new SetupSlideShowPriv; + TQVBoxLayout *layout = new TQVBoxLayout( parent ); + + d->delayInput = new KIntNumInput(5, parent); + d->delayInput->setRange(1, 3600, 1, true ); + d->delayInput->setLabel( i18n("&Delay between images:"), AlignLeft|AlignTop ); + TQWhatsThis::add( d->delayInput, i18n("<p>The delay, in seconds, between images.")); + + d->startWithCurrent = new TQCheckBox(i18n("Start with current image"), parent); + TQWhatsThis::add( d->startWithCurrent, i18n("<p>If this option is enabled, slideshow will be started " + "with currently selected image.")); + + d->loopMode = new TQCheckBox(i18n("Display in a loop"), parent); + TQWhatsThis::add( d->loopMode, i18n("<p>Run the slideshow in endless repetition.")); + + d->printName = new TQCheckBox(i18n("Print image file name"), parent); + TQWhatsThis::add( d->printName, i18n("<p>Print the image file name at the bottom of the screen.")); + + d->printDate = new TQCheckBox(i18n("Print image creation date"), parent); + TQWhatsThis::add( d->printDate, i18n("<p>Print the image creation time/date at the bottom of the screen.")); + + d->printApertureFocal = new TQCheckBox(i18n("Print camera aperture and focal length"), parent); + TQWhatsThis::add( d->printApertureFocal, i18n("<p>Print the camera aperture and focal length at the bottom of the screen.")); + + d->printExpoSensitivity = new TQCheckBox(i18n("Print camera exposure and sensitivity"), parent); + TQWhatsThis::add( d->printExpoSensitivity, i18n("<p>Print the camera exposure and sensitivity at the bottom of the screen.")); + + d->printMakeModel = new TQCheckBox(i18n("Print camera make and model"), parent); + TQWhatsThis::add( d->printMakeModel, i18n("<p>Print the camera make and model at the bottom of the screen.")); + + d->printComment = new TQCheckBox(i18n("Print image caption"), parent); + TQWhatsThis::add( d->printComment, i18n("<p>Print the image caption at the bottom of the screen.")); + + layout->addWidget(d->delayInput); + layout->addWidget(d->startWithCurrent); + layout->addWidget(d->loopMode); + layout->addWidget(d->printName); + layout->addWidget(d->printDate); + layout->addWidget(d->printApertureFocal); + layout->addWidget(d->printExpoSensitivity); + layout->addWidget(d->printMakeModel); + layout->addWidget(d->printComment); + layout->addStretch(); + + readSettings(); +} + +SetupSlideShow::~SetupSlideShow() +{ + delete d; +} + +void SetupSlideShow::applySettings() +{ + TDEConfig* config = kapp->config(); + + config->setGroup("ImageViewer Settings"); + config->writeEntry("SlideShowDelay", d->delayInput->value()); + config->writeEntry("SlideShowStartCurrent", d->startWithCurrent->isChecked()); + config->writeEntry("SlideShowLoop", d->loopMode->isChecked()); + config->writeEntry("SlideShowPrintName", d->printName->isChecked()); + config->writeEntry("SlideShowPrintDate", d->printDate->isChecked()); + config->writeEntry("SlideShowPrintApertureFocal", d->printApertureFocal->isChecked()); + config->writeEntry("SlideShowPrintExpoSensitivity", d->printExpoSensitivity->isChecked()); + config->writeEntry("SlideShowPrintMakeModel", d->printMakeModel->isChecked()); + config->writeEntry("SlideShowPrintComment", d->printComment->isChecked()); + config->sync(); +} + +void SetupSlideShow::readSettings() +{ + TDEConfig* config = kapp->config(); + + config->setGroup("ImageViewer Settings"); + d->delayInput->setValue(config->readNumEntry("SlideShowDelay", 5)); + d->startWithCurrent->setChecked(config->readBoolEntry("SlideShowStartCurrent", false)); + d->loopMode->setChecked(config->readBoolEntry("SlideShowLoop", false)); + d->printName->setChecked(config->readBoolEntry("SlideShowPrintName", true)); + d->printDate->setChecked(config->readBoolEntry("SlideShowPrintDate", false)); + d->printApertureFocal->setChecked(config->readBoolEntry("SlideShowPrintApertureFocal", false)); + d->printExpoSensitivity->setChecked(config->readBoolEntry("SlideShowPrintExpoSensitivity", false)); + d->printMakeModel->setChecked(config->readBoolEntry("SlideShowPrintMakeModel", false)); + d->printComment->setChecked(config->readBoolEntry("SlideShowPrintComment", false)); +} + +} // namespace Digikam + diff --git a/src/utilities/setup/setupslideshow.h b/src/utilities/setup/setupslideshow.h new file mode 100644 index 00000000..9ba68035 --- /dev/null +++ b/src/utilities/setup/setupslideshow.h @@ -0,0 +1,64 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-21 + * Description : setup tab for slideshow options. + * + * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUPSLIDESHOW_H +#define SETUPSLIDESHOW_H + +// TQt includes. + +#include <tqwidget.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class SetupSlideShowPriv; + +class DIGIKAM_EXPORT SetupSlideShow : public TQWidget +{ + TQ_OBJECT + + +public: + + SetupSlideShow(TQWidget* parent = 0); + ~SetupSlideShow(); + + void applySettings(); + +private: + + void readSettings(); + +private: + + SetupSlideShowPriv* d; + +}; + +} // namespace Digikam + +#endif /* SETUPSLIDESHOW_H */ diff --git a/src/utilities/setup/setuptooltip.cpp b/src/utilities/setup/setuptooltip.cpp new file mode 100644 index 00000000..2a5c62c8 --- /dev/null +++ b/src/utilities/setup/setuptooltip.cpp @@ -0,0 +1,272 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-07-09 + * Description : album item tool tip configuration setup tab + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqlayout.h> +#include <tqvgroupbox.h> +#include <tqcheckbox.h> +#include <tqwhatsthis.h> + +// KDE includes. + +#include <tdelocale.h> +#include <kdialogbase.h> + +// Local includes. + +#include "albumsettings.h" +#include "setuptooltip.h" +#include "setuptooltip.moc" + +namespace Digikam +{ + +class SetupToolTipPriv +{ +public: + + SetupToolTipPriv() + { + showToolTipsBox = 0; + + showFileNameBox = 0; + showFileDateBox = 0; + showFileSizeBox = 0; + showImageTypeBox = 0; + showImageDimBox = 0; + + showPhotoMakeBox = 0; + showPhotoDateBox = 0; + showPhotoFocalBox = 0; + showPhotoExpoBox = 0; + showPhotoModeBox = 0; + showPhotoFlashBox = 0; + showPhotoWbBox = 0; + + showAlbumNameBox = 0; + showCommentsBox = 0; + showTagsBox = 0; + showRatingBox = 0; + + fileSettingBox = 0; + photoSettingBox = 0; + digikamSettingBox = 0; + } + + TQCheckBox *showToolTipsBox; + + TQCheckBox *showFileNameBox; + TQCheckBox *showFileDateBox; + TQCheckBox *showFileSizeBox; + TQCheckBox *showImageTypeBox; + TQCheckBox *showImageDimBox; + + TQCheckBox *showPhotoMakeBox; + TQCheckBox *showPhotoDateBox; + TQCheckBox *showPhotoFocalBox; + TQCheckBox *showPhotoExpoBox; + TQCheckBox *showPhotoModeBox; + TQCheckBox *showPhotoFlashBox; + TQCheckBox *showPhotoWbBox; + + TQCheckBox *showAlbumNameBox; + TQCheckBox *showCommentsBox; + TQCheckBox *showTagsBox; + TQCheckBox *showRatingBox; + + TQVGroupBox *fileSettingBox; + TQVGroupBox *photoSettingBox; + TQVGroupBox *digikamSettingBox; +}; + +SetupToolTip::SetupToolTip(TQWidget* parent) + : TQWidget(parent) +{ + d = new SetupToolTipPriv; + TQVBoxLayout *layout = new TQVBoxLayout( parent, 0, KDialog::spacingHint() ); + + d->showToolTipsBox = new TQCheckBox(i18n("Show album items toolti&ps"), parent); + TQWhatsThis::add( d->showToolTipsBox, i18n("<p>Set this option to display image information when " + "the mouse hovers over an album item.")); + + layout->addWidget(d->showToolTipsBox); + + // -------------------------------------------------------- + + d->fileSettingBox = new TQVGroupBox(i18n("File/Image Information"), parent); + + d->showFileNameBox = new TQCheckBox(i18n("Show file name"), d->fileSettingBox); + TQWhatsThis::add( d->showFileNameBox, i18n("<p>Set this option to display the image file name.")); + + d->showFileDateBox = new TQCheckBox(i18n("Show file date"), d->fileSettingBox); + TQWhatsThis::add( d->showFileDateBox, i18n("<p>Set this option to display the image file date.")); + + d->showFileSizeBox = new TQCheckBox(i18n("Show file size"), d->fileSettingBox); + TQWhatsThis::add( d->showFileSizeBox, i18n("<p>Set this option to display the image file size.")); + + d->showImageTypeBox = new TQCheckBox(i18n("Show image type"), d->fileSettingBox); + TQWhatsThis::add( d->showImageTypeBox, i18n("<p>Set this option to display the image type.")); + + d->showImageDimBox = new TQCheckBox(i18n("Show image dimensions"), d->fileSettingBox); + TQWhatsThis::add( d->showImageDimBox, i18n("<p>Set this option to display the image dimensions in pixels.")); + + layout->addWidget(d->fileSettingBox); + + // -------------------------------------------------------- + + d->photoSettingBox = new TQVGroupBox(i18n("Photograph Information"), parent); + + d->showPhotoMakeBox = new TQCheckBox(i18n("Show camera make and model"), d->photoSettingBox); + TQWhatsThis::add( d->showPhotoMakeBox, i18n("<p>Set this option to display the make and model of the " + "camera with which the image has been taken.")); + + d->showPhotoDateBox = new TQCheckBox(i18n("Show camera date"), d->photoSettingBox); + TQWhatsThis::add( d->showPhotoDateBox, i18n("<p>Set this option to display the date when the image was taken.")); + + d->showPhotoFocalBox = new TQCheckBox(i18n("Show camera aperture and focal"), d->photoSettingBox); + TQWhatsThis::add( d->showPhotoFocalBox, i18n("<p>Set this option to display the camera aperture and focal settings " + "used to take the image.")); + + d->showPhotoExpoBox = new TQCheckBox(i18n("Show camera exposure and sensitivity"), d->photoSettingBox); + TQWhatsThis::add( d->showPhotoExpoBox, i18n("<p>Set this option to display the camera exposure and sensitivity " + "used to take the image.")); + + d->showPhotoModeBox = new TQCheckBox(i18n("Show camera mode and program"), d->photoSettingBox); + TQWhatsThis::add( d->showPhotoModeBox, i18n("<p>Set this option to display the camera mode and program " + "used to take the image.")); + + d->showPhotoFlashBox = new TQCheckBox(i18n("Show camera flash settings"), d->photoSettingBox); + TQWhatsThis::add( d->showPhotoFlashBox, i18n("<p>Set this option to display the camera flash settings " + "used to take the image.")); + + d->showPhotoWbBox = new TQCheckBox(i18n("Show camera white balance settings"), d->photoSettingBox); + TQWhatsThis::add( d->showPhotoWbBox, i18n("<p>Set this option to display the camera white balance settings " + "used to take the image.")); + + layout->addWidget(d->photoSettingBox); + + // -------------------------------------------------------- + + d->digikamSettingBox = new TQVGroupBox(i18n("digiKam Information"), parent); + + d->showAlbumNameBox = new TQCheckBox(i18n("Show album name"), d->digikamSettingBox); + TQWhatsThis::add( d->showAlbumNameBox, i18n("<p>Set this option to display the album name.")); + + d->showCommentsBox = new TQCheckBox(i18n("Show image caption"), d->digikamSettingBox); + TQWhatsThis::add( d->showCommentsBox, i18n("<p>Set this option to display the image captions.")); + + d->showTagsBox = new TQCheckBox(i18n("Show image tags"), d->digikamSettingBox); + TQWhatsThis::add( d->showTagsBox, i18n("<p>Set this option to display the image tags.")); + + d->showRatingBox = new TQCheckBox(i18n("Show image rating"), d->digikamSettingBox); + TQWhatsThis::add( d->showRatingBox, i18n("<p>Set this option to display the image rating.")); + + layout->addWidget(d->digikamSettingBox); + layout->addStretch(); + + // -------------------------------------------------------- + + connect(d->showToolTipsBox, TQ_SIGNAL(toggled(bool)), + d->fileSettingBox, TQ_SLOT(setEnabled(bool))); + + connect(d->showToolTipsBox, TQ_SIGNAL(toggled(bool)), + d->photoSettingBox, TQ_SLOT(setEnabled(bool))); + + connect(d->showToolTipsBox, TQ_SIGNAL(toggled(bool)), + d->digikamSettingBox, TQ_SLOT(setEnabled(bool))); + + // -------------------------------------------------------- + + readSettings(); + adjustSize(); +} + +SetupToolTip::~SetupToolTip() +{ + delete d; +} + +void SetupToolTip::applySettings() +{ + AlbumSettings* settings = AlbumSettings::instance(); + if (!settings) return; + + settings->setShowToolTips(d->showToolTipsBox->isChecked()); + + settings->setToolTipsShowFileName(d->showFileNameBox->isChecked()); + settings->setToolTipsShowFileDate(d->showFileDateBox->isChecked()); + settings->setToolTipsShowFileSize(d->showFileSizeBox->isChecked()); + settings->setToolTipsShowImageType(d->showImageTypeBox->isChecked()); + settings->setToolTipsShowImageDim(d->showImageDimBox->isChecked()); + + settings->setToolTipsShowPhotoMake(d->showPhotoMakeBox->isChecked()); + settings->setToolTipsShowPhotoDate(d->showPhotoDateBox->isChecked()); + settings->setToolTipsShowPhotoFocal(d->showPhotoFocalBox->isChecked()); + settings->setToolTipsShowPhotoExpo(d->showPhotoExpoBox->isChecked()); + settings->setToolTipsShowPhotoMode(d->showPhotoModeBox->isChecked()); + settings->setToolTipsShowPhotoFlash(d->showPhotoFlashBox->isChecked()); + settings->setToolTipsShowPhotoWB(d->showPhotoWbBox->isChecked()); + + settings->setToolTipsShowAlbumName(d->showAlbumNameBox->isChecked()); + settings->setToolTipsShowComments(d->showCommentsBox->isChecked()); + settings->setToolTipsShowTags(d->showTagsBox->isChecked()); + settings->setToolTipsShowRating(d->showRatingBox->isChecked()); + + settings->saveSettings(); +} + +void SetupToolTip::readSettings() +{ + AlbumSettings* settings = AlbumSettings::instance(); + + if (!settings) return; + + d->showToolTipsBox->setChecked(settings->getShowToolTips()); + + d->showFileNameBox->setChecked(settings->getToolTipsShowFileName()); + d->showFileDateBox->setChecked(settings->getToolTipsShowFileDate()); + d->showFileSizeBox->setChecked(settings->getToolTipsShowFileSize()); + d->showImageTypeBox->setChecked(settings->getToolTipsShowImageType()); + d->showImageDimBox->setChecked(settings->getToolTipsShowImageDim()); + + d->showPhotoMakeBox->setChecked(settings->getToolTipsShowPhotoMake()); + d->showPhotoDateBox->setChecked(settings->getToolTipsShowPhotoDate()); + d->showPhotoFocalBox->setChecked(settings->getToolTipsShowPhotoFocal()); + d->showPhotoExpoBox->setChecked(settings->getToolTipsShowPhotoExpo()); + d->showPhotoModeBox->setChecked(settings->getToolTipsShowPhotoMode()); + d->showPhotoFlashBox->setChecked(settings->getToolTipsShowPhotoFlash()); + d->showPhotoWbBox->setChecked(settings->getToolTipsShowPhotoWB()); + + d->showAlbumNameBox->setChecked(settings->getToolTipsShowAlbumName()); + d->showCommentsBox->setChecked(settings->getToolTipsShowComments()); + d->showTagsBox->setChecked(settings->getToolTipsShowTags()); + d->showRatingBox->setChecked(settings->getToolTipsShowRating()); + + d->fileSettingBox->setEnabled(d->showToolTipsBox->isChecked()); + d->photoSettingBox->setEnabled(d->showToolTipsBox->isChecked()); + d->digikamSettingBox->setEnabled(d->showToolTipsBox->isChecked()); +} + +} // namespace Digikam + diff --git a/src/utilities/setup/setuptooltip.h b/src/utilities/setup/setuptooltip.h new file mode 100644 index 00000000..8474de78 --- /dev/null +++ b/src/utilities/setup/setuptooltip.h @@ -0,0 +1,59 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-07-09 + * Description : album item tool tip configuration setup tab + * + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SETUPTOOLTIP_H +#define SETUPTOOLTIP_H + +// TQt includes. + +#include <tqwidget.h> + +namespace Digikam +{ + +class SetupToolTipPriv; + +class SetupToolTip : public TQWidget +{ + TQ_OBJECT + + +public: + + SetupToolTip(TQWidget* parent = 0); + ~SetupToolTip(); + + void applySettings(); + +private: + + void readSettings(); + +private: + + SetupToolTipPriv* d; +}; + +} // namespace Digikam + +#endif // SETUPTOOLTIP_H diff --git a/src/utilities/slideshow/Makefile.am b/src/utilities/slideshow/Makefile.am new file mode 100644 index 00000000..5340e80c --- /dev/null +++ b/src/utilities/slideshow/Makefile.am @@ -0,0 +1,17 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/digikam \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/themeengine \ + -I$(top_srcdir)/src/libs/threadimageio \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +noinst_LTLIBRARIES = libslideshow.la + +libslideshow_la_SOURCES = toolbar.cpp slideshow.cpp + +libslideshow_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + + diff --git a/src/utilities/slideshow/slideshow.cpp b/src/utilities/slideshow/slideshow.cpp new file mode 100644 index 00000000..33cf9edc --- /dev/null +++ b/src/utilities/slideshow/slideshow.cpp @@ -0,0 +1,679 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-21 + * Description : slide show tool using preview of pictures. + * + * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#define MAXSTRINGLEN 80 + +// TQt includes. + +#include <tqtimer.h> +#include <tqpixmap.h> +#include <tqdesktopwidget.h> +#include <tqevent.h> +#include <tqcursor.h> +#include <tqpainter.h> +#include <tqfont.h> + +// KDE includes. + +#include <tdeapplication.h> +#include <kiconloader.h> +#include <tdelocale.h> +#include <tdeversion.h> +#include <tdeglobalsettings.h> + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "toolbar.h" +#include "previewloadthread.h" +#include "slideshow.h" +#include "slideshow.moc" + +namespace Digikam +{ + +class SlideShowPriv +{ +public: + + SlideShowPriv() + { + previewThread = 0; + mouseMoveTimer = 0; + timer = 0; + toolBar = 0; + fileIndex = -1; + endOfShow = false; + pause = false; + } + + bool endOfShow; + bool pause; + + int deskX; + int deskY; + int deskWidth; + int deskHeight; + int fileIndex; + + TQTimer *mouseMoveTimer; // To hide cursor when not moved. + TQTimer *timer; + + TQPixmap pixmap; + + DImg preview; + + KURL currentImage; + + PreviewLoadThread *previewThread; + PreviewLoadThread *previewPreloadThread; + + ToolBar *toolBar; + + SlideShowSettings settings; +}; + +SlideShow::SlideShow(const SlideShowSettings& settings) + : TQWidget(0, 0, WStyle_StaysOnTop | WType_Popup | + WX11BypassWM | WDestructiveClose) +{ + d = new SlideShowPriv; + d->settings = settings; + + // --------------------------------------------------------------- + +#if KDE_IS_VERSION(3,2,0) + TQRect deskRect = TDEGlobalSettings::desktopGeometry(this); + d->deskX = deskRect.x(); + d->deskY = deskRect.y(); + d->deskWidth = deskRect.width(); + d->deskHeight = deskRect.height(); +#else + TQRect deskRect = TQApplication::desktop()->screenGeometry(this); + d->deskX = deskRect.x(); + d->deskY = deskRect.y(); + d->deskWidth = deskRect.width(); + d->deskHeight = deskRect.height(); +#endif + + move(d->deskX, d->deskY); + resize(d->deskWidth, d->deskHeight); + setPaletteBackgroundColor(TQt::black); + + // --------------------------------------------------------------- + + d->toolBar = new ToolBar(this); + d->toolBar->hide(); + if (!d->settings.loop) + d->toolBar->setEnabledPrev(false); + + connect(d->toolBar, TQ_SIGNAL(signalPause()), + this, TQ_SLOT(slotPause())); + + connect(d->toolBar, TQ_SIGNAL(signalPlay()), + this, TQ_SLOT(slotPlay())); + + connect(d->toolBar, TQ_SIGNAL(signalNext()), + this, TQ_SLOT(slotNext())); + + connect(d->toolBar, TQ_SIGNAL(signalPrev()), + this, TQ_SLOT(slotPrev())); + + connect(d->toolBar, TQ_SIGNAL(signalClose()), + this, TQ_SLOT(slotClose())); + + // --------------------------------------------------------------- + + d->previewThread = new PreviewLoadThread(); + d->previewPreloadThread = new PreviewLoadThread(); + d->timer = new TQTimer(this); + d->mouseMoveTimer = new TQTimer(this); + + connect(d->previewThread, TQ_SIGNAL(signalImageLoaded(const LoadingDescription &, const DImg &)), + this, TQ_SLOT(slotGotImagePreview(const LoadingDescription &, const DImg&))); + + connect(d->mouseMoveTimer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotMouseMoveTimeOut())); + + connect(d->timer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotTimeOut())); + + d->timer->start(10, true); + + // --------------------------------------------------------------- + + setMouseTracking(true); + slotMouseMoveTimeOut(); +} + +SlideShow::~SlideShow() +{ + d->timer->stop(); + d->mouseMoveTimer->stop(); + + delete d->timer; + delete d->mouseMoveTimer; + delete d->previewThread; + delete d->previewPreloadThread; + delete d; +} + +void SlideShow::setCurrent(const KURL& url) +{ + int index = d->settings.fileList.findIndex(url); + if (index != -1) + { + d->currentImage = url; + d->fileIndex = index-1; + } +} + +void SlideShow::slotTimeOut() +{ + loadNextImage(); +} + +void SlideShow::loadNextImage() +{ + d->fileIndex++; + int num = d->settings.fileList.count(); + + if (d->fileIndex >= num) + { + if (d->settings.loop) + { + d->fileIndex = 0; + } + } + + if (!d->settings.loop) + { + d->toolBar->setEnabledPrev(d->fileIndex > 0); + d->toolBar->setEnabledNext(d->fileIndex < num-1); + } + + if (d->fileIndex < num) + { + d->currentImage = d->settings.fileList[d->fileIndex]; + d->previewThread->load(LoadingDescription(d->currentImage.path(), + TQMAX(d->deskWidth, d->deskHeight), d->settings.exifRotate)); + } + else + { + d->currentImage = KURL(); + d->preview = DImg(); + updatePixmap(); + update(); + } + +} + +void SlideShow::loadPrevImage() +{ + d->fileIndex--; + int num = d->settings.fileList.count(); + + if (d->fileIndex < 0) + { + if (d->settings.loop) + { + d->fileIndex = num-1; + } + } + + if (!d->settings.loop) + { + d->toolBar->setEnabledPrev(d->fileIndex > 0); + d->toolBar->setEnabledNext(d->fileIndex < num-1); + } + + if (d->fileIndex >= 0) + { + d->currentImage = d->settings.fileList[d->fileIndex]; + d->previewThread->load(LoadingDescription(d->currentImage.path(), + TQMAX(d->deskWidth, d->deskHeight), d->settings.exifRotate)); + } + else + { + d->currentImage = KURL(); + d->preview = DImg(); + updatePixmap(); + update(); + } + +} + +void SlideShow::slotGotImagePreview(const LoadingDescription&, const DImg& preview) +{ + d->preview = preview; + + updatePixmap(); + update(); + + if (!d->endOfShow) + { + if (!d->pause) + d->timer->start(d->settings.delay, true); + preloadNextImage(); + } +} + +void SlideShow::preloadNextImage() +{ + int index = d->fileIndex + 1; + int num = d->settings.fileList.count(); + + if (index >= num) + { + if (d->settings.loop) + { + index = 0; + } + } + + if (index < num) + { + d->previewPreloadThread->load(LoadingDescription(d->settings.fileList[index].path(), + TQMAX(d->deskWidth, d->deskHeight), d->settings.exifRotate)); + } +} + +void SlideShow::updatePixmap() +{ + d->pixmap = TQPixmap(size()); + d->pixmap.fill(TQt::black); + TQPainter p(&(d->pixmap)); + + if (!d->currentImage.path().isEmpty()) + { + if (!d->preview.isNull()) + { + // Preview extraction is complete... Draw the image. + + TQPixmap pix(d->preview.smoothScale(width(), height(), TQSize::ScaleMin).convertToPixmap()); + p.drawPixmap((width()-pix.width())/2, + (height()-pix.height())/2, pix, + 0, 0, pix.width(), pix.height()); + + TQString str; + PhotoInfoContainer photoInfo = d->settings.pictInfoMap[d->currentImage].photoInfo; + int offset = 0; + + // Display the Comments. + + if (d->settings.printComment) + { + str = d->settings.pictInfoMap[d->currentImage].comment; + printComments(p, offset, str); + } + + // Display the Make and Model. + + if (d->settings.printMakeModel) + { + str = TQString(); + + if (!photoInfo.make.isEmpty()) + str = photoInfo.make; + + if (!photoInfo.model.isEmpty()) + { + if (!photoInfo.make.isEmpty()) + str += TQString(" / "); + + str += photoInfo.model; + } + + printInfoText(p, offset, str); + } + + // Display the Exposure and Sensitivity. + + if (d->settings.printExpoSensitivity) + { + str = TQString(); + + if (!photoInfo.exposureTime.isEmpty()) + str = photoInfo.exposureTime; + + if (!photoInfo.sensitivity.isEmpty()) + { + if (!photoInfo.exposureTime.isEmpty()) + str += TQString(" / "); + + str += i18n("%1 ISO").arg(photoInfo.sensitivity); + } + + printInfoText(p, offset, str); + } + + // Display the Aperture and Focal. + + if (d->settings.printApertureFocal) + { + str = TQString(); + + if (!photoInfo.aperture.isEmpty()) + str = photoInfo.aperture; + + if (photoInfo.focalLength35mm.isEmpty()) + { + if (!photoInfo.focalLength.isEmpty()) + { + if (!photoInfo.aperture.isEmpty()) + str += TQString(" / "); + + str += photoInfo.focalLength; + } + } + else + { + if (!photoInfo.aperture.isEmpty()) + str += TQString(" / "); + + if (!photoInfo.focalLength.isEmpty()) + str += TQString("%1 (35mm: %2)").arg(photoInfo.focalLength).arg(photoInfo.focalLength35mm); + else + str += TQString("35mm: %1)").arg(photoInfo.focalLength35mm); + } + + printInfoText(p, offset, str); + } + + // Display the Creation Date. + + if (d->settings.printDate) + { + if (photoInfo.dateTime.isValid()) + { + str = TDEGlobal::locale()->formatDateTime(photoInfo.dateTime, true, true); + printInfoText(p, offset, str); + } + } + + // Display the image File Name. + + if (d->settings.printName) + { + str = TQString("%1 (%2/%3)").arg(d->currentImage.filename()) + .arg(TQString::number(d->fileIndex + 1)) + .arg(TQString::number(d->settings.fileList.count())); + + printInfoText(p, offset, str); + } + } + else + { + // ...or preview extraction is failed. + + p.setPen(TQt::white); + p.drawText(0, 0, d->pixmap.width(), d->pixmap.height(), + TQt::AlignCenter|TQt::WordBreak, + i18n("Cannot display image\n\"%1\"") + .arg(d->currentImage.fileName())); + } + } + else + { + // End of Slide Show. + + TQPixmap logo = kapp->iconLoader()->loadIcon("digikam", TDEIcon::NoGroup, 128, + TDEIcon::DefaultState, 0, true); + + TQFont fn(font()); + fn.setPointSize(fn.pointSize()+10); + fn.setBold(true); + + p.setFont(fn); + p.setPen(TQt::white); + p.drawPixmap(50, 100, logo); + p.drawText(60 + logo.width(), 100 + logo.height()/3, i18n("SlideShow Completed.")); + p.drawText(60 + logo.width(), 100 + 2*logo.height()/3, i18n("Click To Exit...")); + + d->endOfShow = true; + d->toolBar->setEnabledPlay(false); + d->toolBar->setEnabledNext(false); + d->toolBar->setEnabledPrev(false); + } +} + +void SlideShow::printInfoText(TQPainter &p, int &offset, const TQString& str) +{ + if (!str.isEmpty()) + { + offset += 20; + p.setPen(TQt::black); + for (int x=9; x<=11; x++) + for (int y=offset+1; y>=offset-1; y--) + p.drawText(x, height()-y, str); + + p.setPen(TQt::white); + p.drawText(10, height()-offset, str); + } +} + +void SlideShow::printComments(TQPainter &p, int &offset, const TQString& comments) +{ + TQStringList commentsByLines; + + uint commentsIndex = 0; // Comments TQString index + + while (commentsIndex < comments.length()) + { + TQString newLine; + bool breakLine = false; // End Of Line found + uint currIndex; // Comments TQString current index + + // Check miminal lines dimension + + uint commentsLinesLengthLocal = MAXSTRINGLEN; + + for (currIndex = commentsIndex; currIndex < comments.length() && !breakLine; currIndex++ ) + { + if( comments[currIndex] == TQChar('\n') || comments[currIndex].isSpace() ) + breakLine = true; + } + + if (commentsLinesLengthLocal <= (currIndex - commentsIndex)) + commentsLinesLengthLocal = (currIndex - commentsIndex); + + breakLine = false; + + for (currIndex = commentsIndex ; currIndex <= commentsIndex + commentsLinesLengthLocal && + currIndex < comments.length() && !breakLine ; + currIndex++ ) + { + breakLine = (comments[currIndex] == TQChar('\n')) ? true : false; + + if (breakLine) + newLine.append(TQString(" ")); + else + newLine.append(comments[currIndex]); + } + + commentsIndex = currIndex; // The line is ended + + if (commentsIndex != comments.length()) + { + while (!newLine.endsWith(" ")) + { + newLine.truncate(newLine.length() - 1); + commentsIndex--; + } + } + + commentsByLines.prepend(newLine.stripWhiteSpace()); + } + + for (int i = 0 ; i < (int)commentsByLines.count() ; i++ ) + { + printInfoText(p, offset, commentsByLines[i]); + } +} + +void SlideShow::paintEvent(TQPaintEvent *) +{ + bitBlt(this, 0, 0, static_cast<TQPaintDevice*>(&d->pixmap), + 0, 0, d->pixmap.width(), + d->pixmap.height(), TQt::CopyROP, true); +} + +void SlideShow::slotPause() +{ + d->timer->stop(); + d->pause = true; + + if (d->toolBar->isHidden()) + { + int w = d->toolBar->width(); + d->toolBar->move(d->deskWidth-w-1,0); + d->toolBar->show(); + } +} + +void SlideShow::slotPlay() +{ + d->toolBar->hide(); + d->pause = false; + slotTimeOut(); +} + +void SlideShow::slotPrev() +{ + loadPrevImage(); +} + +void SlideShow::slotNext() +{ + loadNextImage(); +} + +void SlideShow::slotClose() +{ + close(); +} + +void SlideShow::wheelEvent(TQWheelEvent * e) +{ + if (e->delta() < 0) + { + d->timer->stop(); + d->pause = true; + d->toolBar->setPaused(true); + slotNext(); + } + + if (e->delta() > 0 && d->fileIndex-1 >= 0) + { + d->timer->stop(); + d->pause = true; + d->toolBar->setPaused(true); + slotPrev(); + } +} + +void SlideShow::mousePressEvent(TQMouseEvent *e) +{ + if (d->endOfShow) + close(); + + if (e->button() == TQt::LeftButton) + { + d->timer->stop(); + d->pause = true; + d->toolBar->setPaused(true); + slotNext(); + } + else if (e->button() == TQt::RightButton && d->fileIndex-1 >= 0) + { + d->timer->stop(); + d->pause = true; + d->toolBar->setPaused(true); + slotPrev(); + } +} + +void SlideShow::keyPressEvent(TQKeyEvent *event) +{ + if (!event) + return; + + d->toolBar->keyPressEvent(event); +} + +void SlideShow::mouseMoveEvent(TQMouseEvent *e) +{ + setCursor(TQCursor(TQt::ArrowCursor)); + d->mouseMoveTimer->start(1000, true); + + if (!d->toolBar->canHide()) + return; + + TQPoint pos(e->pos()); + + if ((pos.y() > (d->deskY+20)) && + (pos.y() < (d->deskY+d->deskHeight-20-1))) + { + if (d->toolBar->isHidden()) + return; + else + d->toolBar->hide(); + return; + } + + int w = d->toolBar->width(); + int h = d->toolBar->height(); + + if (pos.y() < (d->deskY+20)) + { + if (pos.x() <= (d->deskX+d->deskWidth/2)) + // position top left + d->toolBar->move(d->deskX, d->deskY); + else + // position top right + d->toolBar->move(d->deskX+d->deskWidth-w-1, d->deskY); + } + else + { + if (pos.x() <= (d->deskX+d->deskWidth/2)) + // position bot left + d->toolBar->move(d->deskX, d->deskY+d->deskHeight-h-1); + else + // position bot right + d->toolBar->move(d->deskX+d->deskWidth-w-1, d->deskY+d->deskHeight-h-1); + } + d->toolBar->show(); +} + +void SlideShow::slotMouseMoveTimeOut() +{ + TQPoint pos(TQCursor::pos()); + if ((pos.y() < (d->deskY+20)) || + (pos.y() > (d->deskY+d->deskHeight-20-1))) + return; + + setCursor(TQCursor(TQt::BlankCursor)); +} + +} // NameSpace Digikam diff --git a/src/utilities/slideshow/slideshow.h b/src/utilities/slideshow/slideshow.h new file mode 100644 index 00000000..dc15ce1c --- /dev/null +++ b/src/utilities/slideshow/slideshow.h @@ -0,0 +1,91 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-21 + * Description : slide show tool using preview of pictures. + * + * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SLIDE_SHOW_H +#define SLIDE_SHOW_H + +// TQt includes. + +#include <tqwidget.h> + +// Local includes. + +#include "digikam_export.h" +#include "loadingdescription.h" +#include "slideshowsettings.h" + +namespace Digikam +{ + +class DImg; +class SlideShowPriv; + +class DIGIKAM_EXPORT SlideShow : public TQWidget +{ + TQ_OBJECT + + +public: + + SlideShow(const SlideShowSettings& settings); + ~SlideShow(); + + void setCurrent(const KURL& url); + +protected: + + void paintEvent(TQPaintEvent *); + void mousePressEvent(TQMouseEvent *); + void mouseMoveEvent(TQMouseEvent *); + void keyPressEvent(TQKeyEvent *); + void wheelEvent(TQWheelEvent *); + +private slots: + + void slotTimeOut(); + void slotMouseMoveTimeOut(); + void slotGotImagePreview(const LoadingDescription &, const DImg &); + + void slotPause(); + void slotPlay(); + void slotPrev(); + void slotNext(); + void slotClose(); + +private: + + void loadNextImage(); + void loadPrevImage(); + void preloadNextImage(); + void updatePixmap(); + void printInfoText(TQPainter &p, int &offset, const TQString& str); + void printComments(TQPainter &p, int &offset, const TQString& comments); + +private: + + SlideShowPriv *d; +}; + +} // NameSpace Digikam + +#endif /* SLIDE_SHOW_H */ diff --git a/src/utilities/slideshow/slideshowsettings.h b/src/utilities/slideshow/slideshowsettings.h new file mode 100644 index 00000000..85057f27 --- /dev/null +++ b/src/utilities/slideshow/slideshowsettings.h @@ -0,0 +1,125 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-02-13 + * Description : slide show settings container. + * + * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef SLIDESHOWSETTINGSCONTAINER_H +#define SLIDESHOWSETTINGSCONTAINER_H + +// TQt includes. + +#include <tqmap.h> + +// KDE includes. + +#include <kurl.h> + +// Local includes. + +#include "photoinfocontainer.h" +#include "digikam_export.h" + +namespace Digikam +{ + +/** This class contain the information of one picture to slide */ +class DIGIKAM_EXPORT SlidePictureInfo +{ + +public: + + SlidePictureInfo(){}; + + ~SlidePictureInfo(){}; + +public: + + /** Image Comment */ + TQString comment; + + /** Exif photo info of picture */ + PhotoInfoContainer photoInfo; +}; + +// -------------------------------------------------------------------------------- + +/** This class contain all settings to perform a slide show of a group of pictures */ +class DIGIKAM_EXPORT SlideShowSettings +{ + +public: + + SlideShowSettings() + { + exifRotate = true; + printName = true; + printDate = false; + printComment = false; + printApertureFocal = false; + printMakeModel = false; + printExpoSensitivity = false; + loop = false; + delay = 5; + }; + + ~SlideShowSettings(){}; + +public: + + // Global Slide Show Settings + + /** Auto-rotate image accordinly with Exif Rotation tag */ + bool exifRotate; + + /** Print picture file name during slide */ + bool printName; + + /** Print picture creation date during slide */ + bool printDate; + + /** Print camera Aperture and Focal during slide */ + bool printApertureFocal; + + /** Print camera Make and Model during slide */ + bool printMakeModel; + + /** Print camera Exposure and Sensitivity during slide */ + bool printExpoSensitivity; + + /** Print picture comment during slide */ + bool printComment; + + /** Slide pictures in loop */ + bool loop; + + /** Delay in seconds */ + int delay; + + /** List of pictures URL to slide */ + KURL::List fileList; + + /** Map of pictures information to slide */ + TQMap<KURL, SlidePictureInfo> pictInfoMap; +}; + +} // namespace Digikam + +#endif // SLIDESHOWSETTINGSCONTAINER_H diff --git a/src/utilities/slideshow/toolbar.cpp b/src/utilities/slideshow/toolbar.cpp new file mode 100644 index 00000000..dbecd4ad --- /dev/null +++ b/src/utilities/slideshow/toolbar.cpp @@ -0,0 +1,217 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-10-05 + * Description : a tool bar for slideshow + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +// TQt includes. + +#include <tqtoolbutton.h> +#include <tqlayout.h> +#include <tqpixmap.h> + +// KDE includes. + +#include <tdeapplication.h> +#include <kiconloader.h> +#include <tdelocale.h> + +// Local includes. + +#include "toolbar.h" +#include "toolbar.moc" + +namespace Digikam +{ + +class ToolBarPriv +{ +public: + + ToolBarPriv() + { + playBtn = 0; + stopBtn = 0; + nextBtn = 0; + prevBtn = 0; + canHide = true; + } + + bool canHide; + + TQToolButton *playBtn; + TQToolButton *stopBtn; + TQToolButton *nextBtn; + TQToolButton *prevBtn; +}; + +ToolBar::ToolBar(TQWidget* parent) + : TQWidget(parent) +{ + d = new ToolBarPriv; + + TQHBoxLayout* lay = new TQHBoxLayout(this); + d->playBtn = new TQToolButton(this); + d->prevBtn = new TQToolButton(this); + d->nextBtn = new TQToolButton(this); + d->stopBtn = new TQToolButton(this); + d->playBtn->setToggleButton(true); + + TDEIconLoader* loader = kapp->iconLoader(); + d->playBtn->setIconSet(loader->loadIcon("media-playback-pause", TDEIcon::NoGroup, 22)); + d->prevBtn->setIconSet(loader->loadIcon("back", TDEIcon::NoGroup, 22)); + d->nextBtn->setIconSet(loader->loadIcon("forward", TDEIcon::NoGroup, 22)); + d->stopBtn->setIconSet(loader->loadIcon("process-stop", TDEIcon::NoGroup, 22)); + + lay->addWidget(d->playBtn); + lay->addWidget(d->prevBtn); + lay->addWidget(d->nextBtn); + lay->addWidget(d->stopBtn); + + setBackgroundMode(TQt::NoBackground); + adjustSize(); + setSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Fixed); + + connect(d->playBtn, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotPlayBtnToggled())); + + connect(d->nextBtn, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotNexPrevClicked())); + + connect(d->prevBtn, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotNexPrevClicked())); + + connect(d->nextBtn, TQ_SIGNAL(clicked()), + this, TQ_SIGNAL(signalNext())); + + connect(d->prevBtn, TQ_SIGNAL(clicked()), + this, TQ_SIGNAL(signalPrev())); + + connect(d->stopBtn, TQ_SIGNAL(clicked()), + this, TQ_SIGNAL(signalClose())); +} + +ToolBar::~ToolBar() +{ + delete d; +} + +bool ToolBar::canHide() const +{ + return d->canHide; +} + +bool ToolBar::isPaused() const +{ + return d->playBtn->isOn(); +} + +void ToolBar::setPaused(bool val) +{ + if (val == isPaused()) + return; + + d->playBtn->setOn(val); + slotPlayBtnToggled(); +} + +void ToolBar::setEnabledPlay(bool val) +{ + d->playBtn->setEnabled(val); +} + +void ToolBar::setEnabledNext(bool val) +{ + d->nextBtn->setEnabled(val); +} + +void ToolBar::setEnabledPrev(bool val) +{ + d->prevBtn->setEnabled(val); +} + +void ToolBar::slotPlayBtnToggled() +{ + if (d->playBtn->isOn()) + { + d->canHide = false; + TDEIconLoader* loader = kapp->iconLoader(); + d->playBtn->setIconSet(loader->loadIcon("media-playback-start", TDEIcon::NoGroup, 22)); + emit signalPause(); + } + else + { + d->canHide = true; + TDEIconLoader* loader = kapp->iconLoader(); + d->playBtn->setIconSet(loader->loadIcon("media-playback-pause", TDEIcon::NoGroup, 22)); + emit signalPlay(); + } +} + +void ToolBar::slotNexPrevClicked() +{ + if (!d->playBtn->isOn()) + { + d->playBtn->setOn(true); + d->canHide = false; + TDEIconLoader* loader = kapp->iconLoader(); + d->playBtn->setIconSet(loader->loadIcon("media-playback-start", TDEIcon::NoGroup, 22)); + emit signalPause(); + } +} + +void ToolBar::keyPressEvent(TQKeyEvent *event) +{ + switch(event->key()) + { + case(TQt::Key_Space): + { + if (d->playBtn->isEnabled()) + d->playBtn->animateClick(); + break; + } + case(TQt::Key_Prior): + { + if (d->prevBtn->isEnabled()) + d->prevBtn->animateClick(); + break; + } + case(TQt::Key_Next): + { + if (d->nextBtn->isEnabled()) + d->nextBtn->animateClick(); + break; + } + case(TQt::Key_Escape): + { + if (d->stopBtn->isEnabled()) + d->stopBtn->animateClick(); + break; + } + default: + break; + } + + event->accept(); +} + +} // Namespace Digikam + diff --git a/src/utilities/slideshow/toolbar.h b/src/utilities/slideshow/toolbar.h new file mode 100644 index 00000000..97262c12 --- /dev/null +++ b/src/utilities/slideshow/toolbar.h @@ -0,0 +1,85 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-10-05 + * Description : a tool bar for slideshow + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, 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. + * + * ============================================================ */ + +#ifndef TOOL_BAR_H +#define TOOL_BAR_H + +#include <tqwidget.h> + +// Local includes. + +#include "digikam_export.h" + +class TQToolButton; + +namespace Digikam +{ + +class ToolBarPriv; + +class DIGIKAM_EXPORT ToolBar : public TQWidget +{ + TQ_OBJECT + + +public: + + ToolBar(TQWidget* parent); + ~ToolBar(); + + bool canHide() const; + bool isPaused() const; + void setPaused(bool val); + + void setEnabledPlay(bool val); + void setEnabledNext(bool val); + void setEnabledPrev(bool val); + +protected: + + void keyPressEvent(TQKeyEvent *event); + +signals: + + void signalNext(); + void signalPrev(); + void signalClose(); + void signalPlay(); + void signalPause(); + +private slots: + + void slotPlayBtnToggled(); + void slotNexPrevClicked(); + +private: + + ToolBarPriv *d; + + friend class SlideShow; +}; + +} // Namespace Digikam + +#endif /* TOOL_BAR_H */ |