You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/utilities/imageeditor/editor/imagewindow.cpp

1264 lines
36 KiB

/* ============================================================
*
* 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 <klocale.h>
#include <tdeconfig.h>
#include <kstandarddirs.h>
#include <kapplication.h>
#include <kmessagebox.h>
#include <ktempfile.h>
#include <kimageio.h>
#include <tdefiledialog.h>
#include <tdeversion.h>
#include <kmenubar.h>
#include <ktoolbar.h>
#include <kaccel.h>
#include <kaction.h>
#include <kstdaccel.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.
KAction *star0;
KAction *star1;
KAction *star2;
KAction *star3;
KAction *star4;
KAction *star5;
// Delete actions
KAction *fileDeletePermanentlyAction;
KAction *fileDeletePermanentlyDirectlyAction;
KAction *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, TQT_SIGNAL(signalNextItem()),
TQT_TQOBJECT(this), TQT_SLOT(slotForward()));
connect(d->rightSidebar, TQT_SIGNAL(signalPrevItem()),
TQT_TQOBJECT(this), TQT_SLOT(slotBackward()));
connect(this, TQT_SIGNAL(signalSelectionChanged( const TQRect &)),
d->rightSidebar, TQT_SLOT(slotImageSelectionChanged( const TQRect &)));
connect(this, TQT_SIGNAL(signalNoCurrentItem()),
d->rightSidebar, TQT_SLOT(slotNoCurrentItem()));
ImageAttributesWatch *watch = ImageAttributesWatch::instance();
connect(watch, TQT_SIGNAL(signalFileMetadataChanged(const KURL &)),
TQT_TQOBJECT(this), TQT_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 KAction(i18n("Assign Rating \"No Stars\""), CTRL+Key_0,
TQT_TQOBJECT(this), TQT_SLOT(slotAssignRatingNoStar()),
actionCollection(), "imageview_ratenostar");
d->star1 = new KAction(i18n("Assign Rating \"One Star\""), CTRL+Key_1,
TQT_TQOBJECT(this), TQT_SLOT(slotAssignRatingOneStar()),
actionCollection(), "imageview_rateonestar");
d->star2 = new KAction(i18n("Assign Rating \"Two Stars\""), CTRL+Key_2,
TQT_TQOBJECT(this), TQT_SLOT(slotAssignRatingTwoStar()),
actionCollection(), "imageview_ratetwostar");
d->star3 = new KAction(i18n("Assign Rating \"Three Stars\""), CTRL+Key_3,
TQT_TQOBJECT(this), TQT_SLOT(slotAssignRatingThreeStar()),
actionCollection(), "imageview_ratethreestar");
d->star4 = new KAction(i18n("Assign Rating \"Four Stars\""), CTRL+Key_4,
TQT_TQOBJECT(this), TQT_SLOT(slotAssignRatingFourStar()),
actionCollection(), "imageview_ratefourstar");
d->star5 = new KAction(i18n("Assign Rating \"Five Stars\""), CTRL+Key_5,
TQT_TQOBJECT(this), TQT_SLOT(slotAssignRatingFiveStar()),
actionCollection(), "imageview_ratefivestar");
// -- Special Delete actions ---------------------------------------------------------------
// Pop up dialog to ask user whether to permanently delete
d->fileDeletePermanentlyAction = new KAction(i18n("Delete File Permanently"),
"editdelete",
SHIFT+Key_Delete,
TQT_TQOBJECT(this),
TQT_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 KAction(i18n("Delete Permanently without Confirmation"),
"editdelete",
0,
TQT_TQOBJECT(this),
TQT_SLOT(slotDeleteCurrentItemPermanentlyDirectly()),
actionCollection(),
"image_delete_permanently_directly");
d->fileTrashDirectlyAction = new KAction(i18n("Move to Trash without Confirmation"),
"edittrash",
0,
TQT_TQOBJECT(this),
TQT_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, TQT_TQOBJECT(this), TQT_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, TQT_SIGNAL(signalTagActivated(int)),
TQT_TQOBJECT(this), TQT_SLOT(slotAssignTag(int)));
connect(removeTagsMenu, TQT_SIGNAL(signalTagActivated(int)),
TQT_TQOBJECT(this), TQT_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, TQT_SIGNAL(activated(int)),
TQT_TQOBJECT(this), TQT_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 KIO
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