diff --git a/utilities/imageeditor/editor/editorwindow.cpp b/utilities/imageeditor/editor/editorwindow.cpp index 4961f50cfe..97719815af 100644 --- a/utilities/imageeditor/editor/editorwindow.cpp +++ b/utilities/imageeditor/editor/editorwindow.cpp @@ -1,1610 +1,1611 @@ /* ============================================================ * Authors: Gilles Caulier * Date : 2006-01-20 * Description : main image editor GUI implementation * * Copyright 2006-2007 by Gilles Caulier * * 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 #include } // Qt includes. #include #include #include #include #include #include #include #include #include #include #include // KDE includes. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Local includes. #include "ddebug.h" #include "canvas.h" #include "dimginterface.h" #include "imageplugin.h" #include "imagepluginloader.h" #include "imageresizedlg.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 "editorwindowprivate.h" #include "editorwindow.h" #include "editorwindow.moc" void qt_enter_modal( QWidget *widget ); void qt_leave_modal( QWidget *widget ); namespace Digikam { EditorWindow::EditorWindow(const char *name) : KMainWindow(0, name, WType_TopLevel) { d = new EditorWindowPriv; 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_fullScreen = false; m_rotatedOrFlipped = false; m_setExifOrientationTag = true; // Settings containers instance. d->ICCSettings = new ICCSettingsContainer(); d->exposureSettings = new ExposureSettingsContainer(); 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; } void EditorWindow::setupStandardConnections() { // -- Canvas connections ------------------------------------------------ connect(m_canvas, SIGNAL(signalShowNextImage()), this, SLOT(slotForward())); connect(m_canvas, SIGNAL(signalShowPrevImage()), this, SLOT(slotBackward())); connect(m_canvas, SIGNAL(signalRightButtonClicked()), this, SLOT(slotContextMenu())); connect(m_canvas, SIGNAL(signalZoomChanged(float)), this, SLOT(slotZoomChanged(float))); connect(m_canvas, SIGNAL(signalChanged()), this, SLOT(slotChanged())); connect(m_canvas, SIGNAL(signalUndoStateChanged(bool, bool, bool)), this, SLOT(slotUndoStateChanged(bool, bool, bool))); connect(m_canvas, SIGNAL(signalSelected(bool)), this, SLOT(slotSelected(bool))); connect(m_canvas, SIGNAL(signalLoadingStarted(const QString &)), this, SLOT(slotLoadingStarted(const QString &))); connect(m_canvas, SIGNAL(signalLoadingFinished(const QString &, bool)), this, SLOT(slotLoadingFinished(const QString &, bool))); connect(m_canvas, SIGNAL(signalLoadingProgress(const QString &, float)), this, SLOT(slotLoadingProgress(const QString &, float))); connect(m_canvas, SIGNAL(signalSavingStarted(const QString &)), this, SLOT(slotSavingStarted(const QString &))); connect(m_canvas, SIGNAL(signalSavingFinished(const QString &, bool)), this, SLOT(slotSavingFinished(const QString &, bool))); connect(m_canvas, SIGNAL(signalSavingProgress(const QString&, float)), this, SLOT(slotSavingProgress(const QString&, float))); // -- if rotating/flipping set the rotatedflipped flag to true ----------- connect(d->rotate90Action, SIGNAL(activated()), this, SLOT(slotRotatedOrFlipped())); connect(d->rotate180Action, SIGNAL(activated()), this, SLOT(slotRotatedOrFlipped())); connect(d->rotate270Action, SIGNAL(activated()), this, SLOT(slotRotatedOrFlipped())); connect(d->flipHorzAction, SIGNAL(activated()), this, SLOT(slotRotatedOrFlipped())); connect(d->flipVertAction, SIGNAL(activated()), this, SLOT(slotRotatedOrFlipped())); // -- status bar connections -------------------------------------- connect(m_nameLabel, SIGNAL(signalCancelButtonPressed()), this, SLOT(slotNameLabelCancelButtonPressed())); // -- Core plugin connections ------------------------------------- ImagePlugin *corePlugin = m_imagePluginLoader->pluginInstance("digikamimageplugin_core"); if ( corePlugin ) { connect(m_canvas, SIGNAL(signalColorManagementTool()), corePlugin, SLOT(slotColorManagement())); } } void EditorWindow::setupStandardActions() { // -- Standard 'File' menu actions --------------------------------------------- m_backwardAction = KStdAction::back(this, SLOT(slotBackward()), actionCollection(), "editorwindow_backward"); m_forwardAction = KStdAction::forward(this, SLOT(slotForward()), actionCollection(), "editorwindow_forward"); m_firstAction = new KAction(i18n("&First"), "start", KStdAccel::shortcut( KStdAccel::Home), this, SLOT(slotFirst()), actionCollection(), "editorwindow_first"); m_lastAction = new KAction(i18n("&Last"), "finish", KStdAccel::shortcut( KStdAccel::End), this, SLOT(slotLast()), actionCollection(), "editorwindow_last"); m_saveAction = KStdAction::save(this, SLOT(slotSave()), actionCollection(), "editorwindow_save"); m_saveAsAction = KStdAction::saveAs(this, SLOT(slotSaveAs()), actionCollection(), "editorwindow_saveas"); m_revertAction = KStdAction::revert(m_canvas, SLOT(slotRestore()), actionCollection(), "editorwindow_revert"); m_saveAction->setEnabled(false); m_saveAsAction->setEnabled(false); m_revertAction->setEnabled(false); d->filePrintAction = new KAction(i18n("Print Image..."), "fileprint", CTRL+Key_P, this, SLOT(slotFilePrint()), actionCollection(), "editorwindow_print"); m_fileDeleteAction = new KAction(i18n("Move to Trash"), "edittrash", Key_Delete, this, SLOT(slotDeleteCurrentItem()), actionCollection(), "editorwindow_delete"); KStdAction::quit(this, SLOT(close()), actionCollection(), "editorwindow_exit"); // -- Standard 'Edit' menu actions --------------------------------------------- d->copyAction = KStdAction::copy(m_canvas, SLOT(slotCopy()), actionCollection(), "editorwindow_copy"); d->copyAction->setEnabled(false); m_undoAction = new KToolBarPopupAction(i18n("Undo"), "undo", KStdAccel::shortcut(KStdAccel::Undo), m_canvas, SLOT(slotUndo()), actionCollection(), "editorwindow_undo"); connect(m_undoAction->popupMenu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShowUndoMenu())); connect(m_undoAction->popupMenu(), SIGNAL(activated(int)), m_canvas, SLOT(slotUndo(int))); m_undoAction->setEnabled(false); m_redoAction = new KToolBarPopupAction(i18n("Redo"), "redo", KStdAccel::shortcut(KStdAccel::Redo), m_canvas, SLOT(slotRedo()), actionCollection(), "editorwindow_redo"); connect(m_redoAction->popupMenu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShowRedoMenu())); connect(m_redoAction->popupMenu(), SIGNAL(activated(int)), m_canvas, SLOT(slotRedo(int))); m_redoAction->setEnabled(false); // -- Standard 'View' menu actions --------------------------------------------- d->zoomPlusAction = KStdAction::zoomIn(m_canvas, SLOT(slotIncreaseZoom()), actionCollection(), "editorwindow_zoomplus"); d->zoomMinusAction = KStdAction::zoomOut(m_canvas, SLOT(slotDecreaseZoom()), actionCollection(), "editorwindow_zoomminus"); d->zoomFitAction = new KToggleAction(i18n("Zoom &AutoFit"), "viewmagfit", CTRL+SHIFT+Key_A, this, SLOT(slotToggleAutoZoom()), actionCollection(), "editorwindow_zoomfit"); #if KDE_IS_VERSION(3,2,0) m_fullScreenAction = KStdAction::fullScreen(this, SLOT(slotToggleFullScreen()), actionCollection(), this, "editorwindow_fullscreen"); #else m_fullScreenAction = new KToggleAction(i18n("Fullscreen"), "window_fullscreen", CTRL+SHIFT+Key_F, this, SLOT(slotToggleFullScreen()), actionCollection(), "editorwindow_fullscreen"); #endif d->slideShowAction = new KAction(i18n("Slide Show"), "slideshow", 0, this, SLOT(slotToggleSlideShow()), actionCollection(),"editorwindow_slideshow"); d->viewUnderExpoAction = new KToggleAction(i18n("Under-Exposure Indicator"), "underexposure", Key_F10, this, SLOT(slotToggleUnderExposureIndicator()), actionCollection(),"editorwindow_underexposure"); d->viewOverExpoAction = new KToggleAction(i18n("Over-Exposure Indicator"), "overexposure", Key_F11, this, SLOT(slotToggleOverExposureIndicator()), actionCollection(),"editorwindow_overexposure"); d->viewCMViewAction = new KToggleAction(i18n("Color Managed View"), "tv", Key_F12, this, SLOT(slotToggleColorManagedView()), actionCollection(),"editorwindow_cmview"); // -- Standard 'Transform' menu actions --------------------------------------------- d->resizeAction = new KAction(i18n("&Resize..."), "resize_image", 0, this, SLOT(slotResize()), actionCollection(), "editorwindow_resize"); d->cropAction = new KAction(i18n("Crop"), "crop", CTRL+Key_X, m_canvas, 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->flipAction = new KActionMenu(i18n("Flip"), "flip", actionCollection(), "editorwindow_flip"); d->flipAction->setDelayed(false); d->flipHorzAction = new KAction(i18n("Horizontally"), 0, CTRL+Key_Asterisk, m_canvas, SLOT(slotFlipHoriz()), actionCollection(), "editorwindow_fliphorizontal"); d->flipVertAction = new KAction(i18n("Vertically"), 0, CTRL+Key_Slash, m_canvas, SLOT(slotFlipVert()), actionCollection(), "editorwindow_flipvertical"); d->flipAction->insert(d->flipHorzAction); d->flipAction->insert(d->flipVertAction); // -- Standard 'Rotate' menu actions ---------------------------------------- d->rotateAction = new KActionMenu(i18n("&Rotate"), "rotate_cw", actionCollection(), "editorwindow_rotate"); d->rotateAction->setDelayed(false); d->rotate90Action = new KAction(i18n("90 Degrees"), 0, CTRL+Key_9, m_canvas, SLOT(slotRotate90()), actionCollection(), "rotate_90"); d->rotate180Action = new KAction(i18n("180 Degrees"), 0, CTRL+Key_8, m_canvas, SLOT(slotRotate180()), actionCollection(), "rotate_180"); d->rotate270Action = new KAction(i18n("270 Degrees"), 0, CTRL+Key_7, m_canvas, SLOT(slotRotate270()), actionCollection(), "rotate_270"); d->rotateAction->insert(d->rotate90Action); d->rotateAction->insert(d->rotate180Action); d->rotateAction->insert(d->rotate270Action); // -- Standard 'Configure' menu actions ---------------------------------------- KStdAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection()); KStdAction::configureToolbars(this, SLOT(slotConfToolbars()), actionCollection()); KStdAction::preferences(this, SLOT(slotSetup()), actionCollection()); // -- Standard 'Help' menu actions --------------------------------------------- d->imagePluginsHelpAction = new KAction(i18n("Image Plugins Handbooks"), "digikamimageplugins", 0, this, SLOT(slotImagePluginsHelp()), actionCollection(), "editorwindow_imagepluginshelp"); d->donateMoneyAction = new KAction(i18n("Donate Money..."), 0, 0, this, SLOT(slotDonateMoney()), actionCollection(), "editorwindow_donatemoney"); } void EditorWindow::setupStandardAccelerators() { d->accelerators = new KAccel(this); d->accelerators->insert("Exit fullscreen", i18n("Exit Fullscreen mode"), i18n("Exit out of the fullscreen mode"), Key_Escape, this, SLOT(slotEscapePressed()), false, true); d->accelerators->insert("Next Image Key_Space", i18n("Next Image"), i18n("Load Next Image"), Key_Space, this, SLOT(slotForward()), false, true); d->accelerators->insert("Previous Image Key_Backspace", i18n("Previous Image"), i18n("Load Previous Image"), Key_Backspace, this, SLOT(slotBackward()), false, true); d->accelerators->insert("Next Image Key_Next", i18n("Next Image"), i18n("Load Next Image"), Key_Next, this, SLOT(slotForward()), false, true); d->accelerators->insert("Previous Image Key_Prior", i18n("Previous Image"), i18n("Load Previous Image"), Key_Prior, this, SLOT(slotBackward()), false, true); d->accelerators->insert("Zoom Plus Key_Plus", i18n("Zoom In"), i18n("Zoom in on Image"), Key_Plus, m_canvas, SLOT(slotIncreaseZoom()), false, true); d->accelerators->insert("Zoom Plus Key_Minus", i18n("Zoom Out"), i18n("Zoom out of Image"), Key_Minus, m_canvas, SLOT(slotDecreaseZoom()), false, true); } void EditorWindow::setupStatusBar() { m_nameLabel = new StatusProgressBar(statusBar()); m_nameLabel->setAlignment(Qt::AlignCenter); m_nameLabel->setMaximumHeight(fontMetrics().height()+2); statusBar()->addWidget(m_nameLabel, 100); m_zoomLabel = new QLabel(statusBar()); m_zoomLabel->setAlignment(Qt::AlignCenter); m_zoomLabel->setMaximumHeight(fontMetrics().height()+2); statusBar()->addWidget(m_zoomLabel, 100); m_resLabel = new QLabel(statusBar()); m_resLabel->setAlignment(Qt::AlignCenter); m_resLabel->setMaximumHeight(fontMetrics().height()+2); statusBar()->addWidget(m_resLabel, 100); d->underExposureIndicator = new QLabel(statusBar()); d->underExposureIndicator->setPixmap(SmallIcon("underexposure")); d->underExposureIndicator->setAlignment(Qt::AlignCenter); d->underExposureIndicator->setMaximumHeight(fontMetrics().height()+2); statusBar()->addWidget(d->underExposureIndicator, 1); d->overExposureIndicator = new QLabel(statusBar()); d->overExposureIndicator->setPixmap(SmallIcon("overexposure")); d->overExposureIndicator->setAlignment(Qt::AlignCenter); d->overExposureIndicator->setMaximumHeight(fontMetrics().height()+2); statusBar()->addWidget(d->overExposureIndicator, 1); d->cmViewIndicator = new QLabel(statusBar()); d->cmViewIndicator->setPixmap(SmallIcon("tv")); d->cmViewIndicator->setAlignment(Qt::AlignCenter); d->cmViewIndicator->setMaximumHeight(fontMetrics().height()+2); statusBar()->addWidget(d->cmViewIndicator, 1); } void EditorWindow::printImage(KURL url) { uchar* ptr = DImgInterface::instance()->getImage(); int w = DImgInterface::instance()->origWidth(); int h = DImgInterface::instance()->origHeight(); bool hasAlpha = DImgInterface::instance()->hasAlpha(); bool sixteenBit = DImgInterface::instance()->sixteenBit(); if (!ptr || !w || !h) return; DImg image(w, h, sixteenBit, hasAlpha, ptr); KPrinter printer; QString appName = KApplication::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, (appName.append(" page")).ascii() )); if ( printer.setup( this, i18n("Print %1").arg(printer.docName().section('/', -1)) ) ) { ImagePrint printOperations(image, printer, url.filename()); if (!printOperations.printImageWithQt()) { KMessageBox::error(this, i18n("Failed to print file: '%1'") .arg(url.filename())); } } } void EditorWindow::slotImagePluginsHelp() { KApplication::kApplication()->invokeHelp( QString::null, "digikamimageplugins" ); } void EditorWindow::slotEditKeys() { KKeyDialog dialog(true, this); dialog.insert( actionCollection(), i18n( "General" ) ); QPtrList 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() { int width = m_canvas->imageWidth(); int height = m_canvas->imageHeight(); ImageResizeDlg dlg(this, &width, &height); if (dlg.exec() == QDialog::Accepted && (width != m_canvas->imageWidth() || height != m_canvas->imageHeight())) m_canvas->resizeImage(width, height); } void EditorWindow::slotAboutToShowUndoMenu() { m_undoAction->popupMenu()->clear(); QStringList titles; m_canvas->getUndoHistory(titles); if(!titles.isEmpty()) { int id = 1; QStringList::Iterator iter = titles.begin(); for(; iter != titles.end(); ++iter,++id) { m_undoAction->popupMenu()->insertItem(*iter, id); } } } void EditorWindow::slotAboutToShowRedoMenu() { m_redoAction->popupMenu()->clear(); QStringList titles; m_canvas->getRedoHistory(titles); if(!titles.isEmpty()) { int id = 1; QStringList::Iterator iter = titles.begin(); for(; iter != titles.end(); ++iter,++id) { m_redoAction->popupMenu()->insertItem(*iter, id); } } } void EditorWindow::slotConfToolbars() { saveMainWindowSettings(KGlobal::config(), "ImageViewer Settings"); KEditToolbar dlg(factory(), this); connect(&dlg, SIGNAL(newToolbarConfig()), this, SLOT(slotNewToolbarConfig())); dlg.exec(); } void EditorWindow::slotNewToolbarConfig() { applyMainWindowSettings(KGlobal::config(), "ImageViewer Settings"); } void EditorWindow::slotToggleAutoZoom() { bool checked = d->zoomFitAction->isChecked(); d->zoomPlusAction->setEnabled(!checked); d->zoomMinusAction->setEnabled(!checked); m_canvas->slotToggleAutoZoom(); } void EditorWindow::slotZoomChanged(float zoom) { m_zoomLabel->setText(i18n("Zoom: ") + QString::number(zoom*100, 'f', 2) + QString("%")); d->zoomPlusAction->setEnabled(!m_canvas->maxZoom() && !d->zoomFitAction->isChecked()); d->zoomMinusAction->setEnabled(!m_canvas->minZoom() && !d->zoomFitAction->isChecked()); } void EditorWindow::slotEscapePressed() { if (m_fullScreen) m_fullScreenAction->activate(); } void EditorWindow::plugActionAccel(KAction* action) { if (!action) return; d->accelerators->insert(action->text(), action->text(), action->whatsThis(), action->shortcut(), action, SLOT(activate())); } void EditorWindow::unplugActionAccel(KAction* action) { d->accelerators->remove(action->text()); } void EditorWindow::loadImagePlugins() { QPtrList pluginList = m_imagePluginLoader->pluginList(); for (ImagePlugin* plugin = pluginList.first(); plugin; plugin = pluginList.next()) { if (plugin) { guiFactory()->addClient(plugin); plugin->setParentWidget(this); plugin->setEnabledSelectionActions(false); } else DDebug() << "Invalid plugin to add!" << endl; } } void EditorWindow::unLoadImagePlugins() { QPtrList pluginList = m_imagePluginLoader->pluginList(); for (ImagePlugin* plugin = pluginList.first(); plugin; plugin = pluginList.next()) { if (plugin) { guiFactory()->removeClient(plugin); plugin->setParentWidget(0); plugin->setEnabledSelectionActions(false); } } } void EditorWindow::readStandardSettings() { KConfig* 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->zoomFitAction->activate(); d->zoomPlusAction->setEnabled(false); d->zoomMinusAction->setEnabled(false); } } void EditorWindow::applyStandardSettings() { KConfig* 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", QString::null); d->ICCSettings->workspaceSetting = config->readPathEntry("WorkProfileFile", QString::null); d->ICCSettings->monitorSetting = config->readPathEntry("MonitorProfileFile", QString::null); d->ICCSettings->proofSetting = config->readPathEntry("ProofProfileFile", QString::null); d->ICCSettings->CMInRawLoadingSetting = config->readBoolEntry("CMInRawLoading", false); d->viewCMViewAction->setEnabled(d->ICCSettings->enableCMSetting); d->viewCMViewAction->setChecked(d->ICCSettings->managedViewSetting); d->cmViewIndicator->setEnabled(d->ICCSettings->managedViewSetting && d->ICCSettings->enableCMSetting); setColorManagedViewIndicatorToolTip(d->ICCSettings->managedViewSetting && d->ICCSettings->enableCMSetting); 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)); // 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 pictures 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 = RawDecodingSettings::RAWCOLOR; else m_IOFileSettings->rawDecodingSettings.outputColorSpace = RawDecodingSettings::SRGB; m_IOFileSettings->rawDecodingSettings.sixteenBitsImage = config->readBoolEntry("SixteenBitsImage", false); m_IOFileSettings->rawDecodingSettings.automaticColorBalance = config->readBoolEntry("AutomaticColorBalance", true); m_IOFileSettings->rawDecodingSettings.cameraColorBalance = config->readBoolEntry("CameraColorBalance", true); m_IOFileSettings->rawDecodingSettings.RGBInterpolate4Colors = config->readBoolEntry("RGBInterpolate4Colors", false); m_IOFileSettings->rawDecodingSettings.SuperCCDsecondarySensor = config->readBoolEntry("SuperCCDsecondarySensor", false); m_IOFileSettings->rawDecodingSettings.enableNoiseReduction = config->readBoolEntry("EnableNoiseReduction", false); m_IOFileSettings->rawDecodingSettings.unclipColors = config->readNumEntry("UnclipColors", 0); m_IOFileSettings->rawDecodingSettings.RAWQuality = (RawDecodingSettings::DecodingQuality)config->readNumEntry("RAWQuality", RawDecodingSettings::BILINEAR); m_IOFileSettings->rawDecodingSettings.NRSigmaDomain = config->readDoubleNumEntry("NRSigmaDomain", 2.0); m_IOFileSettings->rawDecodingSettings.NRSigmaRange = config->readDoubleNumEntry("NRSigmaRange", 4.0); m_IOFileSettings->rawDecodingSettings.brightness = config->readDoubleNumEntry("RAWBrightness", 1.0); // -- GUI Settings ------------------------------------------------------- QSizePolicy rightSzPolicy(QSizePolicy::Preferred, QSizePolicy::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); // -- Exposure Indicators Settings --------------------------------------- QColor black(Qt::black); QColor white(Qt::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->setEnabled(d->exposureSettings->underExposureIndicator); d->overExposureIndicator->setEnabled(d->exposureSettings->overExposureIndicator); setUnderExposureToolTip(d->exposureSettings->underExposureIndicator); setOverExposureToolTip(d->exposureSettings->overExposureIndicator); m_canvas->setExposureSettings(d->exposureSettings); } void EditorWindow::saveStandardSettings() { KConfig* config = kapp->config(); config->setGroup("ImageViewer Settings"); config->writeEntry("AutoZoom", d->zoomFitAction->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(); } void EditorWindow::toggleStandardActions(bool val) { d->zoomFitAction->setEnabled(val); m_saveAsAction->setEnabled(val); d->rotateAction->setEnabled(val); d->flipAction->setEnabled(val); d->filePrintAction->setEnabled(val); d->resizeAction->setEnabled(val); m_fileDeleteAction->setEnabled(val); m_saveAsAction->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); } QPtrList 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); #if QT_VERSION >= 0x030300 setWindowState( windowState() & ~WindowFullScreen ); #else showNormal(); #endif menuBar()->show(); statusBar()->show(); leftDock()->show(); rightDock()->show(); topDock()->show(); bottomDock()->show(); QObject* obj = child("ToolBar","KToolBar"); if (obj) { KToolBar* toolBar = static_cast(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->zoomFitAction); unplugActionAccel(d->cropAction); unplugActionAccel(d->filePrintAction); unplugActionAccel(m_fileDeleteAction); toggleGUI2FullScreen(); m_fullScreen = false; } else // go to fullscreen { m_canvas->setBackgroundColor(QColor(Qt::black)); // hide the menubar and the statusbar menuBar()->hide(); statusBar()->hide(); topDock()->hide(); leftDock()->hide(); rightDock()->hide(); bottomDock()->hide(); QObject* obj = child("ToolBar","KToolBar"); if (obj) { KToolBar* toolBar = static_cast(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->zoomFitAction); plugActionAccel(d->cropAction); plugActionAccel(d->filePrintAction); plugActionAccel(m_fileDeleteAction); toggleGUI2FullScreen(); showFullScreen(); m_fullScreen = true; } } void EditorWindow::slotToggleSlideShow() { KConfig* config = kapp->config(); + config->setGroup("ImageViewer Settings"); bool startWithCurrent = config->readBoolEntry("SlideShowStartCurrent", false); bool loop = config->readBoolEntry("SlideShowLoop", false); bool printName = config->readBoolEntry("SlideShowPrintName", true); int delay = config->readNumEntry("SlideShowDelay", 5); slideShow(startWithCurrent, loop, delay*1000, printName); } void EditorWindow::slotRotatedOrFlipped() { m_rotatedOrFlipped = true; } void EditorWindow::slotLoadingProgress(const QString&, float progress) { m_nameLabel->setProgressValue((int)(progress*100.0)); } void EditorWindow::slotSavingProgress(const QString&, float progress) { m_nameLabel->setProgressValue((int)(progress*100.0)); } 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()), QString::null, KStdGuiItem::save(), KStdGuiItem::discard()); if (result == KMessageBox::Yes) { bool saving; if (m_canvas->isReadOnly()) saving = saveAs(); else 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() { QWidget dummy(0, 0, WType_Dialog | WShowModal); dummy.setFocusPolicy( QWidget::NoFocus ); qt_enter_modal(&dummy); qApp->enter_loop(); qt_leave_modal(&dummy); } void EditorWindow::slotSelected(bool val) { // Update menu actions. d->cropAction->setEnabled(val); d->copyAction->setEnabled(val); for (ImagePlugin* plugin = m_imagePluginLoader->pluginList().first(); plugin; plugin = m_imagePluginLoader->pluginList().next()) { if (plugin) { plugin->setEnabledSelectionActions(val); } } // Update histogram into sidebar. emit signalSelectionChanged( m_canvas->getSelectedArea() ); } void EditorWindow::hideToolBars() { QPtrListIterator it = toolBarIterator(); KToolBar* bar; for(;it.current()!=0L; ++it) { bar=it.current(); if (bar->area()) bar->area()->hide(); else bar->hide(); } } void EditorWindow::showToolBars() { QPtrListIterator it = toolBarIterator(); KToolBar* bar; for( ; it.current()!=0L ; ++it) { bar=it.current(); if (bar->area()) bar->area()->show(); else bar->show(); } } void EditorWindow::slotLoadingStarted(const QString& /*filename*/) { setCursor( KCursor::waitCursor() ); // Disable actions as appropriate during loading emit signalNoCurrentItem(); toggleActions(false); m_nameLabel->progressBarMode(StatusProgressBar::ProgressBarMode, i18n("Loading: ")); } void EditorWindow::slotLoadingFinished(const QString& 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 != QString::null) { QFileInfo fi(filename); QString 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 (m_savingContext->savingState != SavingContextContainer::SavingStateNone) { m_savingContext->abortingSaving = true; m_canvas->abortSaving(); } } void EditorWindow::slotSave() { if (m_canvas->isReadOnly()) saveAs(); else save(); } void EditorWindow::slotSavingStarted(const QString& /*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 QString& 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 informations 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 informations 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 Qt event loop to unlock promptUserSave() method. if (m_savingContext->synchronizingState == SavingContextContainer::SynchronousSaving) qApp->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; QString 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() : QDir::homeDirPath(), QString::null, this, "imageFileSaveDialog", false, options); imageFileSaveDialog.setOperationMode(KFileDialog::Saving); imageFileSaveDialog.setMode(KFile::File); imageFileSaveDialog.setSelection(m_savingContext->srcURL.fileName()); imageFileSaveDialog.setCaption(i18n("New Image File Name")); imageFileSaveDialog.setFilter(mimetypes); connect(&imageFileSaveDialog, SIGNAL(filterChanged(const QString &)), options, SLOT(slotImageFileFormatChanged(const QString &))); connect(&imageFileSaveDialog, SIGNAL(fileSelected(const QString &)), options, SLOT(slotImageFileSelected(const QString &))); options->slotImageFileSelected(m_savingContext->srcURL.path()); // 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. QFileInfo 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 = QImageIO::imageFormat(m_savingContext->srcURL.path()); } else { // Else, check if format from file name extension is include on file mime type list. QString imgExtPattern; QStringList imgExtList = QStringList::split(" ", mimetypes); for (QStringList::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 isn't valid !" << endl; return false; } // 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 ---------------------------------------------------------- QFileInfo 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. QFileInfo 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() { QCString dstFileName = QFile::encodeName(m_savingContext->destinationURL.path()); // store old permissions mode_t filePermissions = S_IREAD | S_IWRITE; if (m_savingContext->destinationExisted) { struct stat stbuf; if (::stat(dstFileName, &stbuf) == 0) { filePermissions = stbuf.st_mode; } } // rename tmp file to dest if (::rename(QFile::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 (m_savingContext->destinationExisted) { if (::chmod(dstFileName, filePermissions) != 0) { DWarning() << "Failed to restore file permissions for file " << dstFileName << endl; } } return true; } void EditorWindow::slotToggleColorManagedView() { 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. KConfig* config = kapp->config(); config->setGroup("Color Management"); config->writeEntry("ManagedView", cmv); } d->cmViewIndicator->setEnabled(cmv); setColorManagedViewIndicatorToolTip(cmv); } void EditorWindow::setColorManagedViewIndicatorToolTip(bool cmv) { QToolTip::remove(d->cmViewIndicator); QToolTip::add(d->cmViewIndicator, cmv ? i18n("Color Managed View is enabled") : i18n("Color Managed View is disabled")); } void EditorWindow::slotToggleUnderExposureIndicator() { bool uei = !d->exposureSettings->underExposureIndicator; d->underExposureIndicator->setEnabled(uei); d->exposureSettings->underExposureIndicator = uei; m_canvas->setExposureSettings(d->exposureSettings); setUnderExposureToolTip(uei); } void EditorWindow::setUnderExposureToolTip(bool uei) { QToolTip::remove(d->underExposureIndicator); QToolTip::add(d->underExposureIndicator, uei ? i18n("Under-Exposure indicator is enabled") : i18n("Under-Exposure indicator is disabled")); } void EditorWindow::slotToggleOverExposureIndicator() { bool oei = !d->exposureSettings->overExposureIndicator; d->overExposureIndicator->setEnabled(oei); d->exposureSettings->overExposureIndicator = oei; m_canvas->setExposureSettings(d->exposureSettings); setOverExposureToolTip(oei); } void EditorWindow::setOverExposureToolTip(bool oei) { QToolTip::remove(d->overExposureIndicator); QToolTip::add(d->overExposureIndicator, oei ? i18n("Over-Exposure indicator is enabled") : i18n("Over-Exposure indicator is disabled")); } void EditorWindow::slotDonateMoney() { KApplication::kApplication()->invokeBrowser("http://www.digikam.org/?q=donation"); } } // namespace Digikam diff --git a/utilities/imageeditor/editor/imagewindow.cpp b/utilities/imageeditor/editor/imagewindow.cpp index 369a8aff52..d3feb694ed 100644 --- a/utilities/imageeditor/editor/imagewindow.cpp +++ b/utilities/imageeditor/editor/imagewindow.cpp @@ -1,1014 +1,1014 @@ /* ============================================================ * Authors: Renchi Raju - * Gilles Caulier + * Gilles Caulier * Date : 2004-02-12 * Description : digiKam image editor GUI * * Copyright 2004-2005 by Renchi Raju, Gilles Caulier * Copyright 2006-2007 by Gilles Caulier * * 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 // Qt includes. #include #include #include #include #include #include #include // KDE includes. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Local includes. #include "ddebug.h" #include "canvas.h" #include "dimginterface.h" #include "themeengine.h" #include "dimg.h" #include "dmetadata.h" #include "imageplugin.h" #include "imagepluginloader.h" #include "imageresizedlg.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 "dpopupmenu.h" #include "tagspopupmenu.h" #include "ratingpopupmenu.h" #include "slideshow.h" #include "setup.h" #include "setupimgplugins.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 "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; contextMenu = 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; DPopupMenu *contextMenu; }; 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; // -- Build the GUI ------------------------------- setupUserArea(); setupStatusBar(); setupActions(); // Load image plugins to GUI m_imagePluginLoader = ImagePluginLoader::instance(); loadImagePlugins(); // Create context menu. d->contextMenu = new DPopupMenu(this); KActionCollection *ac = actionCollection(); if( ac->action("editorwindow_backward") ) ac->action("editorwindow_backward")->plug(d->contextMenu); if( ac->action("editorwindow_forward") ) ac->action("editorwindow_forward")->plug(d->contextMenu); d->contextMenu->insertSeparator(); if( ac->action("editorwindow_rotate") ) ac->action("editorwindow_rotate")->plug(d->contextMenu); if( ac->action("editorwindow_crop") ) ac->action("editorwindow_crop")->plug(d->contextMenu); d->contextMenu->insertSeparator(); if( ac->action("editorwindow_delete") ) ac->action("editorwindow_delete")->plug(d->contextMenu); // 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; } void ImageWindow::closeEvent(QCloseEvent* 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, SIGNAL(signalNextItem()), this, SLOT(slotForward())); connect(d->rightSidebar, SIGNAL(signalPrevItem()), this, SLOT(slotBackward())); connect(this, SIGNAL(signalSelectionChanged( const QRect &)), d->rightSidebar, SLOT(slotImageSelectionChanged( const QRect &))); connect(this, SIGNAL(signalNoCurrentItem()), d->rightSidebar, SLOT(slotNoCurrentItem())); ImageAttributesWatch *watch = ImageAttributesWatch::instance(); connect(watch, SIGNAL(signalFileMetadataChanged(const KURL &)), this, SLOT(slotFileMetadataChanged(const KURL &))); connect(ThemeEngine::instance(), SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); } void ImageWindow::setupUserArea() { QWidget* widget = new QWidget(this); QHBoxLayout *lay = new QHBoxLayout(widget); m_splitter = new QSplitter(widget); m_canvas = new Canvas(m_splitter); QSizePolicy rightSzPolicy(QSizePolicy::Preferred, QSizePolicy::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( QFrame::NoFrame ); m_splitter->setFrameShadow( QFrame::Plain ); m_splitter->setFrameShape( QFrame::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 Star\""), CTRL+Key_0, d->rightSidebar, SLOT(slotAssignRatingNoStar()), actionCollection(), "imageview_ratenostar"); d->star1 = new KAction(i18n("Assign Rating \"One Star\""), CTRL+Key_1, d->rightSidebar, SLOT(slotAssignRatingOneStar()), actionCollection(), "imageview_rateonestar"); d->star2 = new KAction(i18n("Assign Rating \"Two Stars\""), CTRL+Key_2, d->rightSidebar, SLOT(slotAssignRatingTwoStar()), actionCollection(), "imageview_ratetwostar"); d->star3 = new KAction(i18n("Assign Rating \"Three Stars\""), CTRL+Key_3, d->rightSidebar, SLOT(slotAssignRatingThreeStar()), actionCollection(), "imageview_ratethreestar"); d->star4 = new KAction(i18n("Assign Rating \"Four Stars\""), CTRL+Key_4, d->rightSidebar, SLOT(slotAssignRatingFourStar()), actionCollection(), "imageview_ratefourstar"); d->star5 = new KAction(i18n("Assign Rating \"Five Stars\""), CTRL+Key_5, d->rightSidebar, 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, this, 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, this, SLOT(slotDeleteCurrentItemPermanentlyDirectly()), actionCollection(), "image_delete_permanently_directly"); d->fileTrashDirectlyAction = new KAction(i18n("Move to Trash without Confirmation"), "edittrash", 0, this, SLOT(slotTrashCurrentItemDirectly()), actionCollection(), "image_trash_directly"); // --------------------------------------------------------------------------------- createGUI("digikamimagewindowui.rc", false); setupStandardAccelerators(); } void ImageWindow::applySettings() { applyStandardSettings(); KConfig* config = kapp->config(); config->setGroup("ImageViewer Settings"); if (!config->readBoolEntry("UseThemeBackgroundColor", true)) m_bgColor = config->readColorEntry("BackgroundColor", &Qt::black); else m_bgColor = ThemeEngine::instance()->baseColor(); m_canvas->setBackgroundColor(m_bgColor); AlbumSettings *settings = AlbumSettings::instance(); m_canvas->setExifOrient(settings->getExifRotate()); m_setExifOrientationTag = settings->getExifSetOrientation(); } void ImageWindow::loadURL(const KURL::List& urlList, const KURL& urlCurrent, const QString& 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 QString& 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 QString& caption, bool allowSaving) { // this method contains the code shared by loadURL and loadImageInfos // if window is iconified, show it if (isMinimized()) { KWin::deIconifyWindow(winId()); } setCaption(i18n("digiKam Image Editor - %1").arg(caption)); d->allowSaving = allowSaving; m_saveAction->setEnabled(false); m_revertAction->setEnabled(false); m_undoAction->setEnabled(false); m_redoAction->setEnabled(false); QTimer::singleShot(0, this, 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.first(); slotLoadCurrent(); } void ImageWindow::slotContextMenu() { if (d->contextMenu) { RatingPopupMenu *ratingMenu = 0; TagsPopupMenu *assignTagsMenu = 0; TagsPopupMenu *removeTagsMenu = 0; int separatorID1 = -1; int separatorID2 = -1; if (d->imageInfoCurrent) { // Bulk assignment/removal of tags -------------------------- Q_LLONG id = d->imageInfoCurrent->id(); QValueList idList; idList.append(id); assignTagsMenu = new TagsPopupMenu(idList, 1000, TagsPopupMenu::ASSIGN); removeTagsMenu = new TagsPopupMenu(idList, 2000, TagsPopupMenu::REMOVE); separatorID1 = d->contextMenu->insertSeparator(); d->contextMenu->insertItem(i18n("Assign Tag"), assignTagsMenu); int i = d->contextMenu->insertItem(i18n("Remove Tag"), removeTagsMenu); connect(assignTagsMenu, SIGNAL(signalTagActivated(int)), this, SLOT(slotAssignTag(int))); connect(removeTagsMenu, SIGNAL(signalTagActivated(int)), this, SLOT(slotRemoveTag(int))); AlbumDB* db = AlbumManager::instance()->albumDB(); if (!db->hasTags( idList )) d->contextMenu->setItemEnabled(i, false); separatorID2 = d->contextMenu->insertSeparator(); // Assign Star Rating ------------------------------------------- ratingMenu = new RatingPopupMenu(); connect(ratingMenu, SIGNAL(activated(int)), this, SLOT(slotAssignRating(int))); d->contextMenu->insertItem(i18n("Assign Rating"), ratingMenu); } d->contextMenu->exec(QCursor::pos()); if (separatorID1 != -1) d->contextMenu->removeItem(separatorID1); if (separatorID2 != -1) d->contextMenu->removeItem(separatorID2); delete assignTagsMenu; delete removeTagsMenu; delete ratingMenu; } } void ImageWindow::slotChanged() { QString mpixels; QSize dims(m_canvas->imageWidth(), m_canvas->imageHeight()); mpixels.setNum(dims.width()*dims.height()/1000000.0, 'f', 2); QString 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 = DImgInterface::instance()->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::slotAssignRating(int rating) { rating = QMIN(5, QMAX(0, 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; QString text = d->urlCurrent.filename() + i18n(" (%2 of %3)") .arg(QString::number(index+1)) .arg(QString::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 // isn't 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() != QDialog::Accepted) return false; unLoadImagePlugins(); m_imagePluginLoader->loadPluginsFromList(setup.imagePluginsPage()->getImagePluginsListEnable()); kapp->config()->sync(); loadImagePlugins(); 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(QDir::cleanDirPath(m_savingContext->srcURL.directory())); PAlbum* srcAlbum = AlbumManager::instance()->findPAlbum(srcDirURL); KURL dstDirURL(QDir::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() { startingSave(d->urlCurrent); return true; } bool ImageWindow::saveAs() { 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 (!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(); if (!SyncJob::del(d->urlCurrent, useTrash)) { QString 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::slotThemeChanged() { m_canvas->setBackgroundColor(ThemeEngine::instance()->baseColor()); } void ImageWindow::slotFilePrint() { printImage(d->urlCurrent); }; void ImageWindow::slideShow(bool startWithCurrent, bool loop, int delay, bool printName) { bool exifRotate = AlbumSettings::instance()->getExifRotate(); KURL::List urlList; if (startWithCurrent) { for (KURL::List::const_iterator it = d->urlList.find(d->urlCurrent); it != d->urlList.end(); ++it) urlList.append(*it); } else urlList = d->urlList; SlideShow *slide = new SlideShow(urlList, exifRotate, delay, printName, loop); slide->show(); } } // namespace Digikam