summaryrefslogtreecommitdiffstats
path: root/src/utilities/imageeditor/editor/imagewindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/utilities/imageeditor/editor/imagewindow.cpp')
-rw-r--r--src/utilities/imageeditor/editor/imagewindow.cpp1263
1 files changed, 1263 insertions, 0 deletions
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