File indexing completed on 2025-03-16 04:01:02
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2006-01-20 0007 * Description : core image editor GUI implementation 0008 * 0009 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2009-2011 by Andi Clemens <andi dot clemens at gmail dot com> 0011 * SPDX-FileCopyrightText: 2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "editorwindow_p.h" 0018 0019 namespace Digikam 0020 { 0021 0022 EditorWindow::EditorWindow(const QString& name, QWidget* const parent) 0023 : DXmlGuiWindow(parent), 0024 d (new Private) 0025 { 0026 setConfigGroupName(QLatin1String("ImageViewer Settings")); 0027 setObjectName(name); 0028 setWindowFlags(Qt::Window); 0029 setFullScreenOptions(FS_EDITOR); 0030 0031 m_nonDestructive = true; 0032 m_contextMenu = nullptr; 0033 m_servicesMenu = nullptr; 0034 m_serviceAction = nullptr; 0035 m_canvas = nullptr; 0036 m_openVersionAction = nullptr; 0037 m_saveAction = nullptr; 0038 m_saveAsAction = nullptr; 0039 m_saveCurrentVersionAction = nullptr; 0040 m_saveNewVersionAction = nullptr; 0041 m_saveNewVersionAsAction = nullptr; 0042 m_saveNewVersionInFormatAction = nullptr; 0043 m_resLabel = nullptr; 0044 m_nameLabel = nullptr; 0045 m_exportAction = nullptr; 0046 m_revertAction = nullptr; 0047 m_discardChangesAction = nullptr; 0048 m_fileDeleteAction = nullptr; 0049 m_forwardAction = nullptr; 0050 m_backwardAction = nullptr; 0051 m_firstAction = nullptr; 0052 m_lastAction = nullptr; 0053 m_applyToolAction = nullptr; 0054 m_closeToolAction = nullptr; 0055 m_undoAction = nullptr; 0056 m_redoAction = nullptr; 0057 m_showBarAction = nullptr; 0058 m_splitter = nullptr; 0059 m_stackView = nullptr; 0060 m_setExifOrientationTag = true; 0061 m_editingOriginalImage = true; 0062 m_actionEnabledState = false; 0063 0064 // Settings containers instance. 0065 0066 d->exposureSettings = new ExposureSettingsContainer(); 0067 d->toolIface = new EditorToolIface(this); 0068 m_IOFileSettings = new IOFileSettings(); 0069 //d->waitingLoop = new QEventLoop(this); 0070 } 0071 0072 EditorWindow::~EditorWindow() 0073 { 0074 delete m_canvas; 0075 delete m_IOFileSettings; 0076 delete d->toolIface; 0077 delete d->exposureSettings; 0078 delete d; 0079 } 0080 0081 SidebarSplitter* EditorWindow::sidebarSplitter() const 0082 { 0083 return m_splitter; 0084 } 0085 0086 EditorStackView* EditorWindow::editorStackView() const 0087 { 0088 return m_stackView; 0089 } 0090 0091 ExposureSettingsContainer* EditorWindow::exposureSettings() const 0092 { 0093 return d->exposureSettings; 0094 } 0095 0096 void EditorWindow::setupContextMenu() 0097 { 0098 m_contextMenu = new QMenu(this); 0099 0100 addAction2ContextMenu(QLatin1String("editorwindow_fullscreen"), true); 0101 addAction2ContextMenu(QLatin1String("options_show_menubar"), true); 0102 m_contextMenu->addSeparator(); 0103 0104 // -------------------------------------------------------- 0105 0106 addAction2ContextMenu(QLatin1String("editorwindow_backward"), true); 0107 addAction2ContextMenu(QLatin1String("editorwindow_forward"), true); 0108 m_contextMenu->addSeparator(); 0109 0110 // -------------------------------------------------------- 0111 0112 addAction2ContextMenu(QLatin1String("slideshow_plugin"), true); 0113 addAction2ContextMenu(QLatin1String("editorwindow_transform_rotateleft"), true); 0114 addAction2ContextMenu(QLatin1String("editorwindow_transform_rotateright"), true); 0115 addAction2ContextMenu(QLatin1String("editorwindow_transform_crop"), true); 0116 m_contextMenu->addSeparator(); 0117 0118 // -------------------------------------------------------- 0119 0120 addAction2ContextMenu(QLatin1String("editorwindow_delete"), true); 0121 } 0122 0123 void EditorWindow::setupStandardConnections() 0124 { 0125 connect(m_stackView, SIGNAL(signalToggleOffFitToWindow()), 0126 this, SLOT(slotToggleOffFitToWindow())); 0127 0128 // -- Canvas connections ------------------------------------------------ 0129 0130 connect(m_canvas, SIGNAL(signalShowNextImage()), 0131 this, SLOT(slotForward())); 0132 0133 connect(m_canvas, SIGNAL(signalShowPrevImage()), 0134 this, SLOT(slotBackward())); 0135 0136 connect(m_canvas, SIGNAL(signalRightButtonClicked()), 0137 this, SLOT(slotContextMenu())); 0138 0139 connect(m_stackView, SIGNAL(signalZoomChanged(bool,bool,double)), 0140 this, SLOT(slotZoomChanged(bool,bool,double))); 0141 0142 connect(m_canvas, SIGNAL(signalChanged()), 0143 this, SLOT(slotChanged())); 0144 0145 connect(m_canvas, SIGNAL(signalAddedDropedItems(QDropEvent*)), 0146 this, SLOT(slotAddedDropedItems(QDropEvent*))); 0147 0148 connect(m_canvas->interface(), SIGNAL(signalUndoStateChanged()), 0149 this, SLOT(slotUndoStateChanged())); 0150 0151 connect(m_canvas, SIGNAL(signalSelected(bool)), 0152 this, SLOT(slotSelected(bool))); 0153 0154 connect(m_canvas, SIGNAL(signalPrepareToLoad()), 0155 this, SLOT(slotPrepareToLoad())); 0156 0157 connect(m_canvas, SIGNAL(signalLoadingStarted(QString)), 0158 this, SLOT(slotLoadingStarted(QString))); 0159 0160 connect(m_canvas, SIGNAL(signalLoadingFinished(QString,bool)), 0161 this, SLOT(slotLoadingFinished(QString,bool))); 0162 0163 connect(m_canvas, SIGNAL(signalLoadingProgress(QString,float)), 0164 this, SLOT(slotLoadingProgress(QString,float))); 0165 0166 connect(m_canvas, SIGNAL(signalSavingStarted(QString)), 0167 this, SLOT(slotSavingStarted(QString))); 0168 0169 connect(m_canvas, SIGNAL(signalSavingFinished(QString,bool)), 0170 this, SLOT(slotSavingFinished(QString,bool))); 0171 0172 connect(m_canvas, SIGNAL(signalSavingProgress(QString,float)), 0173 this, SLOT(slotSavingProgress(QString,float))); 0174 0175 connect(m_canvas, SIGNAL(signalSelectionChanged(QRect)), 0176 this, SLOT(slotSelectionChanged(QRect))); 0177 0178 connect(m_canvas, SIGNAL(signalSelectionSetText(QRect)), 0179 this, SLOT(slotSelectionSetText(QRect))); 0180 0181 connect(m_canvas->interface(), SIGNAL(signalFileOriginChanged(QString)), 0182 this, SLOT(slotFileOriginChanged(QString))); 0183 0184 // -- status bar connections -------------------------------------- 0185 0186 connect(m_nameLabel, SIGNAL(signalCancelButtonPressed()), 0187 this, SLOT(slotNameLabelCancelButtonPressed())); 0188 0189 connect(m_nameLabel, SIGNAL(signalCancelButtonPressed()), 0190 d->toolIface, SLOT(slotToolAborted())); 0191 0192 // -- Icc settings connections -------------------------------------- 0193 0194 connect(IccSettings::instance(), SIGNAL(signalSettingsChanged()), 0195 this, SLOT(slotColorManagementOptionsChanged())); 0196 } 0197 0198 void EditorWindow::setupStandardActions() 0199 { 0200 // -- Standard 'File' menu actions --------------------------------------------- 0201 0202 KActionCollection* const ac = actionCollection(); 0203 0204 m_backwardAction = buildStdAction(StdBackAction, this, SLOT(slotBackward()), this); 0205 ac->addAction(QLatin1String("editorwindow_backward"), m_backwardAction); 0206 ac->setDefaultShortcuts(m_backwardAction, QList<QKeySequence>() << Qt::Key_PageUp << Qt::Key_Backspace 0207 << Qt::Key_Up << Qt::Key_Left); 0208 m_backwardAction->setEnabled(false); 0209 0210 m_forwardAction = buildStdAction(StdForwardAction, this, SLOT(slotForward()), this); 0211 ac->addAction(QLatin1String("editorwindow_forward"), m_forwardAction); 0212 ac->setDefaultShortcuts(m_forwardAction, QList<QKeySequence>() << Qt::Key_PageDown << Qt::Key_Space 0213 << Qt::Key_Down << Qt::Key_Right); 0214 m_forwardAction->setEnabled(false); 0215 0216 m_firstAction = new QAction(QIcon::fromTheme(QLatin1String("go-first")), 0217 i18nc("@action; go to first item", "&First"), this); 0218 connect(m_firstAction, SIGNAL(triggered()), this, SLOT(slotFirst())); 0219 ac->addAction(QLatin1String("editorwindow_first"), m_firstAction); 0220 ac->setDefaultShortcut(m_firstAction, QKeySequence(Qt::CTRL | Qt::Key_Home)); 0221 m_firstAction->setEnabled(false); 0222 0223 m_lastAction = new QAction(QIcon::fromTheme(QLatin1String("go-last")), 0224 i18nc("@action; go to last item", "&Last"), this); 0225 connect(m_lastAction, SIGNAL(triggered()), this, SLOT(slotLast())); 0226 ac->addAction(QLatin1String("editorwindow_last"), m_lastAction); 0227 ac->setDefaultShortcut(m_lastAction, QKeySequence(Qt::CTRL | Qt::Key_End)); 0228 m_lastAction->setEnabled(false); 0229 0230 m_openVersionAction = new QAction(QIcon::fromTheme(QLatin1String("view-preview")), 0231 i18nc("@action", "Open Original"), this); 0232 connect(m_openVersionAction, SIGNAL(triggered()), this, SLOT(slotOpenOriginal())); 0233 ac->addAction(QLatin1String("editorwindow_openversion"), m_openVersionAction); 0234 ac->setDefaultShortcut(m_openVersionAction, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_End)); 0235 0236 m_saveAction = buildStdAction(StdSaveAction, this, SLOT(saveOrSaveAs()), this); 0237 ac->addAction(QLatin1String("editorwindow_save"), m_saveAction); 0238 0239 m_saveAsAction = buildStdAction(StdSaveAsAction, this, SLOT(saveAs()), this); 0240 ac->addAction(QLatin1String("editorwindow_saveas"), m_saveAsAction); 0241 0242 m_saveCurrentVersionAction = new QAction(QIcon::fromTheme(QLatin1String("dialog-ok-apply")), 0243 i18nc("@action Save changes to current version", "Save Changes"), this); 0244 m_saveCurrentVersionAction->setToolTip(i18nc("@info:tooltip", "Save the modifications to the current version of the file")); 0245 connect(m_saveCurrentVersionAction, SIGNAL(triggered()), this, SLOT(saveCurrentVersion())); 0246 ac->addAction(QLatin1String("editorwindow_savecurrentversion"), m_saveCurrentVersionAction); 0247 0248 m_saveNewVersionAction = new KToolBarPopupAction(QIcon::fromTheme(QLatin1String("list-add")), 0249 i18nc("@action Save changes to a newly created version", "Save As New Version"), this); 0250 m_saveNewVersionAction->setToolTip(i18nc("@info:tooltip", "Save the current modifications to a new version of the file")); 0251 connect(m_saveNewVersionAction, SIGNAL(triggered()), this, SLOT(saveNewVersion())); 0252 ac->addAction(QLatin1String("editorwindow_savenewversion"), m_saveNewVersionAction); 0253 0254 QAction* const m_saveNewVersionAsAction = new QAction(QIcon::fromTheme(QLatin1String("document-save-as")), 0255 i18nc("@action Save changes to a newly created version, specifying the filename and format", 0256 "Save New Version As..."), this); 0257 m_saveNewVersionAsAction->setToolTip(i18nc("@info:tooltip", "Save the current modifications to a new version of the file, " 0258 "specifying the filename and format")); 0259 connect(m_saveNewVersionAsAction, SIGNAL(triggered()), this, SLOT(saveNewVersionAs())); 0260 0261 m_saveNewVersionInFormatAction = new QMenu(i18nc("@action Save As New Version...Save in format...", 0262 "Save in Format"), this); 0263 m_saveNewVersionInFormatAction->setIcon(QIcon::fromTheme(QLatin1String("view-preview"))); 0264 d->plugNewVersionInFormatAction(this, m_saveNewVersionInFormatAction, i18nc("@action:inmenu", "JPEG"), QLatin1String("JPG")); 0265 d->plugNewVersionInFormatAction(this, m_saveNewVersionInFormatAction, i18nc("@action:inmenu", "TIFF"), QLatin1String("TIFF")); 0266 d->plugNewVersionInFormatAction(this, m_saveNewVersionInFormatAction, i18nc("@action:inmenu", "PNG"), QLatin1String("PNG")); 0267 d->plugNewVersionInFormatAction(this, m_saveNewVersionInFormatAction, i18nc("@action:inmenu", "PGF"), QLatin1String("PGF")); 0268 0269 #ifdef HAVE_JASPER 0270 0271 d->plugNewVersionInFormatAction(this, m_saveNewVersionInFormatAction, i18nc("@action:inmenu", "JPEG 2000"), QLatin1String("JP2")); 0272 0273 #endif // HAVE_JASPER 0274 0275 #ifdef HAVE_X265 0276 0277 d->plugNewVersionInFormatAction(this, m_saveNewVersionInFormatAction, i18nc("@action:inmenu", "HEIF"), QLatin1String("HEIF")); 0278 0279 #endif // HAVE_X265 0280 0281 if (DPluginLoader::instance()->canExport(QLatin1String("JXL"))) 0282 { 0283 d->plugNewVersionInFormatAction(this, m_saveNewVersionInFormatAction, i18nc("@action:inmenu", "JXL"), QLatin1String("JXL")); 0284 } 0285 0286 if (DPluginLoader::instance()->canExport(QLatin1String("WEBP"))) 0287 { 0288 d->plugNewVersionInFormatAction(this, m_saveNewVersionInFormatAction, i18nc("@action:inmenu", "WEBP"), QLatin1String("WEBP")); 0289 } 0290 0291 if (DPluginLoader::instance()->canExport(QLatin1String("AVIF"))) 0292 { 0293 d->plugNewVersionInFormatAction(this, m_saveNewVersionInFormatAction, i18nc("@action:inmenu", "AVIF"), QLatin1String("AVIF")); 0294 } 0295 0296 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0297 0298 m_saveNewVersionAction->popupMenu()->addAction(m_saveNewVersionAsAction); 0299 m_saveNewVersionAction->popupMenu()->addAction(m_saveNewVersionInFormatAction->menuAction()); 0300 0301 #else 0302 0303 m_saveNewVersionAction->menu()->addAction(m_saveNewVersionAsAction); 0304 m_saveNewVersionAction->menu()->addAction(m_saveNewVersionInFormatAction->menuAction()); 0305 0306 #endif 0307 0308 // This also triggers saveAs, but in the context of non-destructive we want a slightly different appearance 0309 0310 m_exportAction = new QAction(QIcon::fromTheme(QLatin1String("document-export")), 0311 i18nc("@action", "Export"), this); 0312 m_exportAction->setToolTip(i18nc("@info:tooltip", "Save the file in a folder outside your collection")); 0313 connect(m_exportAction, SIGNAL(triggered()), this, SLOT(saveAs())); 0314 ac->addAction(QLatin1String("editorwindow_export"), m_exportAction); 0315 ac->setDefaultShortcut(m_exportAction, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_E)); // NOTE: Gimp shortcut 0316 0317 m_revertAction = buildStdAction(StdRevertAction, this, SLOT(slotRevert()), this); 0318 ac->addAction(QLatin1String("editorwindow_revert"), m_revertAction); 0319 0320 m_discardChangesAction = new QAction(QIcon::fromTheme(QLatin1String("task-reject")), 0321 i18nc("@action", "Discard Changes"), this); 0322 m_discardChangesAction->setToolTip(i18nc("@info:tooltip", "Discard all current changes to this file")); 0323 connect(m_discardChangesAction, SIGNAL(triggered()), this, SLOT(slotDiscardChanges())); 0324 ac->addAction(QLatin1String("editorwindow_discardchanges"), m_discardChangesAction); 0325 0326 m_openVersionAction->setEnabled(false); 0327 m_saveAction->setEnabled(false); 0328 m_saveAsAction->setEnabled(false); 0329 m_saveCurrentVersionAction->setEnabled(false); 0330 m_saveNewVersionAction->setEnabled(false); 0331 m_revertAction->setEnabled(false); 0332 m_discardChangesAction->setEnabled(false); 0333 0334 d->openWithAction = new QAction(QIcon::fromTheme(QLatin1String("preferences-desktop-filetype-association")), 0335 i18nc("@action", "Open With Default Application"), this); 0336 d->openWithAction->setWhatsThis(i18nc("@info", "Open the item with default assigned application.")); 0337 connect(d->openWithAction, SIGNAL(triggered()), this, SLOT(slotFileWithDefaultApplication())); 0338 ac->addAction(QLatin1String("open_with_default_application"), d->openWithAction); 0339 ac->setDefaultShortcut(d->openWithAction, QKeySequence(Qt::CTRL | Qt::Key_F4)); 0340 d->openWithAction->setEnabled(false); 0341 0342 m_fileDeleteAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), 0343 i18nc("@action: Non-pluralized", "Move to Trash"), this); 0344 connect(m_fileDeleteAction, SIGNAL(triggered()), this, SLOT(slotDeleteCurrentItem())); 0345 ac->addAction(QLatin1String("editorwindow_delete"), m_fileDeleteAction); 0346 ac->setDefaultShortcut(m_fileDeleteAction, QKeySequence(Qt::Key_Delete)); 0347 m_fileDeleteAction->setEnabled(false); 0348 0349 QAction* const closeAction = buildStdAction(StdCloseAction, this, SLOT(slotClose()), this); 0350 ac->addAction(QLatin1String("editorwindow_close"), closeAction); 0351 0352 // -- Standard 'Edit' menu actions --------------------------------------------- 0353 0354 d->copyAction = buildStdAction(StdCopyAction, m_canvas, SLOT(slotCopy()), this); 0355 ac->addAction(QLatin1String("editorwindow_copy"), d->copyAction); 0356 d->copyAction->setEnabled(false); 0357 0358 m_undoAction = new KToolBarPopupAction(QIcon::fromTheme(QLatin1String("edit-undo")), 0359 i18nc("@action", "Undo"), this); 0360 m_undoAction->setEnabled(false); 0361 ac->addAction(QLatin1String("editorwindow_undo"), m_undoAction); 0362 ac->setDefaultShortcut(m_undoAction, QKeySequence(Qt::CTRL | Qt::Key_Z)); 0363 0364 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0365 0366 connect(m_undoAction->popupMenu(), SIGNAL(aboutToShow()), 0367 this, SLOT(slotAboutToShowUndoMenu())); 0368 0369 #else 0370 0371 connect(m_undoAction->menu(), SIGNAL(aboutToShow()), 0372 this, SLOT(slotAboutToShowUndoMenu())); 0373 0374 #endif 0375 0376 // connect simple undo action 0377 0378 connect(m_undoAction, &QAction::triggered, 0379 this, [this]() 0380 { 0381 m_canvas->slotUndo(1); 0382 } 0383 ); 0384 0385 m_redoAction = new KToolBarPopupAction(QIcon::fromTheme(QLatin1String("edit-redo")), 0386 i18nc("@action", "Redo"), this); 0387 m_redoAction->setEnabled(false); 0388 ac->addAction(QLatin1String("editorwindow_redo"), m_redoAction); 0389 ac->setDefaultShortcut(m_redoAction, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Z)); 0390 0391 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0392 0393 connect(m_redoAction->popupMenu(), SIGNAL(aboutToShow()), 0394 this, SLOT(slotAboutToShowRedoMenu())); 0395 0396 #else 0397 0398 connect(m_redoAction->menu(), SIGNAL(aboutToShow()), 0399 this, SLOT(slotAboutToShowRedoMenu())); 0400 0401 #endif 0402 0403 // connect simple redo action 0404 0405 connect(m_redoAction, &QAction::triggered, 0406 this, [this]() 0407 { 0408 m_canvas->slotRedo(1); 0409 } 0410 ); 0411 0412 d->selectAllAction = new QAction(i18nc("@action: create a selection containing the full image", "Select All"), this); 0413 connect(d->selectAllAction, SIGNAL(triggered()), m_canvas, SLOT(slotSelectAll())); 0414 ac->addAction(QLatin1String("editorwindow_selectAll"), d->selectAllAction); 0415 ac->setDefaultShortcut(d->selectAllAction, QKeySequence(Qt::CTRL | Qt::Key_A)); 0416 0417 d->selectNoneAction = new QAction(i18nc("@action", "Select None"), this); 0418 connect(d->selectNoneAction, SIGNAL(triggered()), m_canvas, SLOT(slotSelectNone())); 0419 ac->addAction(QLatin1String("editorwindow_selectNone"), d->selectNoneAction); 0420 ac->setDefaultShortcut(d->selectNoneAction, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_A)); 0421 0422 // -- Standard 'View' menu actions --------------------------------------------- 0423 0424 d->zoomPlusAction = buildStdAction(StdZoomInAction, this, SLOT(slotIncreaseZoom()), this); 0425 ac->addAction(QLatin1String("editorwindow_zoomplus"), d->zoomPlusAction); 0426 0427 d->zoomMinusAction = buildStdAction(StdZoomOutAction, this, SLOT(slotDecreaseZoom()), this); 0428 ac->addAction(QLatin1String("editorwindow_zoomminus"), d->zoomMinusAction); 0429 0430 d->zoomTo100percents = new QAction(QIcon::fromTheme(QLatin1String("zoom-original")), 0431 i18nc("@action", "Zoom to 100%"), this); 0432 connect(d->zoomTo100percents, SIGNAL(triggered()), this, SLOT(slotZoomTo100Percents())); 0433 ac->addAction(QLatin1String("editorwindow_zoomto100percents"), d->zoomTo100percents); 0434 ac->setDefaultShortcut(d->zoomTo100percents, QKeySequence(Qt::CTRL | Qt::Key_Period)); 0435 0436 d->zoomFitToWindowAction = new QAction(QIcon::fromTheme(QLatin1String("zoom-fit-best")), 0437 i18nc("@action", "Fit to &Window"), this); 0438 d->zoomFitToWindowAction->setCheckable(true); 0439 connect(d->zoomFitToWindowAction, SIGNAL(triggered()), this, SLOT(slotToggleFitToWindow())); 0440 ac->addAction(QLatin1String("editorwindow_zoomfit2window"), d->zoomFitToWindowAction); 0441 ac->setDefaultShortcut(d->zoomFitToWindowAction, QKeySequence(Qt::ALT | Qt::CTRL | Qt::Key_E)); 0442 0443 d->zoomFitToSelectAction = new QAction(QIcon::fromTheme(QLatin1String("zoom-select-fit")), 0444 i18nc("@action", "Fit to &Selection"), this); 0445 connect(d->zoomFitToSelectAction, SIGNAL(triggered()), this, SLOT(slotFitToSelect())); 0446 ac->addAction(QLatin1String("editorwindow_zoomfit2select"), d->zoomFitToSelectAction); 0447 ac->setDefaultShortcut(d->zoomFitToSelectAction, QKeySequence(Qt::ALT | Qt::CTRL | Qt::Key_S)); // NOTE: Photoshop 7 use ALT+CTRL+0 0448 d->zoomFitToSelectAction->setEnabled(false); 0449 d->zoomFitToSelectAction->setWhatsThis(i18nc("@info", "This option can be used to zoom the image to the " 0450 "current selection area.")); 0451 0452 // ********************************************************** 0453 0454 QList<DPluginAction*> gactions = DPluginLoader::instance()->pluginsActions(DPluginAction::Generic, this); 0455 0456 Q_FOREACH (DPluginAction* const gac, gactions) 0457 { 0458 gac->setEnabled(false); 0459 } 0460 0461 QList<DPluginAction*> eactions = DPluginLoader::instance()->pluginsActions(DPluginAction::Editor, this); 0462 0463 Q_FOREACH (DPluginAction* const eac, eactions) 0464 { 0465 eac->setEnabled(false); 0466 } 0467 0468 // -------------------------------------------------------- 0469 0470 createFullScreenAction(QLatin1String("editorwindow_fullscreen")); 0471 createSidebarActions(); 0472 0473 d->viewUnderExpoAction = new QAction(QIcon::fromTheme(QLatin1String("underexposure")), 0474 i18nc("@action", "Under-Exposure Indicator"), this); 0475 d->viewUnderExpoAction->setCheckable(true); 0476 d->viewUnderExpoAction->setWhatsThis(i18nc("@info", "Set this option to display black " 0477 "overlaid on the image. This will help you to avoid " 0478 "under-exposing the image.")); 0479 connect(d->viewUnderExpoAction, SIGNAL(triggered(bool)), this, SLOT(slotSetUnderExposureIndicator(bool))); 0480 ac->addAction(QLatin1String("editorwindow_underexposure"), d->viewUnderExpoAction); 0481 ac->setDefaultShortcut(d->viewUnderExpoAction, QKeySequence(Qt::Key_F11)); 0482 0483 d->viewOverExpoAction = new QAction(QIcon::fromTheme(QLatin1String("overexposure")), 0484 i18nc("@action", "Over-Exposure Indicator"), this); 0485 d->viewOverExpoAction->setCheckable(true); 0486 d->viewOverExpoAction->setWhatsThis(i18nc("@info", "Set this option to display white " 0487 "overlaid on the image. This will help you to avoid " 0488 "over-exposing the image.")); 0489 connect(d->viewOverExpoAction, SIGNAL(triggered(bool)), this, SLOT(slotSetOverExposureIndicator(bool))); 0490 ac->addAction(QLatin1String("editorwindow_overexposure"), d->viewOverExpoAction); 0491 ac->setDefaultShortcut(d->viewOverExpoAction, QKeySequence(Qt::CTRL | Qt::Key_F11)); 0492 0493 d->viewCMViewAction = new QAction(QIcon::fromTheme(QLatin1String("video-display")), 0494 i18nc("@action", "Color-Managed View"), this); 0495 d->viewCMViewAction->setCheckable(true); 0496 connect(d->viewCMViewAction, SIGNAL(triggered()), this, SLOT(slotToggleColorManagedView())); 0497 ac->addAction(QLatin1String("editorwindow_cmview"), d->viewCMViewAction); 0498 ac->setDefaultShortcut(d->viewCMViewAction, QKeySequence(Qt::Key_F12)); 0499 0500 d->softProofOptionsAction = new QAction(QIcon::fromTheme(QLatin1String("document-print")), 0501 i18nc("@action", "Soft Proofing Options..."), this); 0502 connect(d->softProofOptionsAction, SIGNAL(triggered()), this, SLOT(slotSoftProofingOptions())); 0503 ac->addAction(QLatin1String("editorwindow_softproofoptions"), d->softProofOptionsAction); 0504 0505 d->viewSoftProofAction = new QAction(QIcon::fromTheme(QLatin1String("document-preview-archive")), 0506 i18nc("@action", "Soft Proofing View"), this); 0507 d->viewSoftProofAction->setCheckable(true); 0508 connect(d->viewSoftProofAction, SIGNAL(triggered()), this, SLOT(slotUpdateSoftProofingState())); 0509 ac->addAction(QLatin1String("editorwindow_softproofview"), d->viewSoftProofAction); 0510 0511 // -- Standard 'Transform' menu actions --------------------------------------------- 0512 0513 d->cropAction = new QAction(QIcon::fromTheme(QLatin1String("transform-crop-and-resize")), 0514 i18nc("@action", "Crop to Selection"), this); 0515 connect(d->cropAction, SIGNAL(triggered()), m_canvas, SLOT(slotCrop())); 0516 d->cropAction->setEnabled(false); 0517 d->cropAction->setWhatsThis(i18nc("@info", "This option can be used to crop the image. " 0518 "Select a region of the image to enable this action.")); 0519 ac->addAction(QLatin1String("editorwindow_transform_crop"), d->cropAction); 0520 ac->setDefaultShortcut(d->cropAction, QKeySequence(Qt::CTRL | Qt::Key_X)); 0521 0522 // -- Standard 'Flip' menu actions --------------------------------------------- 0523 0524 d->flipHorizAction = new QAction(QIcon::fromTheme(QLatin1String("object-flip-horizontal")), 0525 i18nc("@action", "Flip Horizontally"), this); 0526 connect(d->flipHorizAction, SIGNAL(triggered()), m_canvas, SLOT(slotFlipHoriz())); 0527 connect(d->flipHorizAction, SIGNAL(triggered()), this, SLOT(slotFlipHIntoQue())); 0528 ac->addAction(QLatin1String("editorwindow_transform_fliphoriz"), d->flipHorizAction); 0529 ac->setDefaultShortcut(d->flipHorizAction, QKeySequence(Qt::CTRL | Qt::Key_Asterisk)); 0530 d->flipHorizAction->setEnabled(false); 0531 0532 d->flipVertAction = new QAction(QIcon::fromTheme(QLatin1String("object-flip-vertical")), 0533 i18nc("@action", "Flip Vertically"), this); 0534 connect(d->flipVertAction, SIGNAL(triggered()), m_canvas, SLOT(slotFlipVert())); 0535 connect(d->flipVertAction, SIGNAL(triggered()), this, SLOT(slotFlipVIntoQue())); 0536 ac->addAction(QLatin1String("editorwindow_transform_flipvert"), d->flipVertAction); 0537 ac->setDefaultShortcut(d->flipVertAction, QKeySequence(Qt::CTRL | Qt::Key_Slash)); 0538 d->flipVertAction->setEnabled(false); 0539 0540 // -- Standard 'Rotate' menu actions ---------------------------------------- 0541 0542 d->rotateLeftAction = new QAction(QIcon::fromTheme(QLatin1String("object-rotate-left")), 0543 i18nc("@action", "Rotate Left"), this); 0544 connect(d->rotateLeftAction, SIGNAL(triggered()), m_canvas, SLOT(slotRotate270())); 0545 connect(d->rotateLeftAction, SIGNAL(triggered()), this, SLOT(slotRotateLeftIntoQue())); 0546 ac->addAction(QLatin1String("editorwindow_transform_rotateleft"), d->rotateLeftAction); 0547 ac->setDefaultShortcut(d->rotateLeftAction, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Left)); 0548 d->rotateLeftAction->setEnabled(false); 0549 0550 d->rotateRightAction = new QAction(QIcon::fromTheme(QLatin1String("object-rotate-right")), 0551 i18nc("@action", "Rotate Right"), this); 0552 connect(d->rotateRightAction, SIGNAL(triggered()), m_canvas, SLOT(slotRotate90())); 0553 connect(d->rotateRightAction, SIGNAL(triggered()), this, SLOT(slotRotateRightIntoQue())); 0554 ac->addAction(QLatin1String("editorwindow_transform_rotateright"), d->rotateRightAction); 0555 ac->setDefaultShortcut(d->rotateRightAction, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Right)); 0556 d->rotateRightAction->setEnabled(false); 0557 0558 m_showBarAction = thumbBar()->getToggleAction(this); 0559 ac->addAction(QLatin1String("editorwindow_showthumbs"), m_showBarAction); 0560 ac->setDefaultShortcut(m_showBarAction, QKeySequence(Qt::CTRL | Qt::Key_T)); 0561 0562 // Provides a menu entry that allows showing/hiding the toolbar(s) 0563 0564 setStandardToolBarMenuEnabled(true); 0565 0566 // Provides a menu entry that allows showing/hiding the statusbar 0567 0568 createStandardStatusBarAction(); 0569 0570 // Standard 'Configure' menu actions 0571 0572 createSettingsActions(); 0573 0574 // --------------------------------------------------------------------------------- 0575 0576 ThemeManager::instance()->registerThemeActions(this); 0577 0578 connect(ThemeManager::instance(), SIGNAL(signalThemeChanged()), 0579 this, SLOT(slotThemeChanged())); 0580 0581 // -- Keyboard-only actions -------------------------------------------------------- 0582 0583 QAction* const altBackwardAction = new QAction(i18nc("@action", "Previous Image"), this); 0584 ac->addAction(QLatin1String("editorwindow_backward_shift_space"), altBackwardAction); 0585 ac->setDefaultShortcut(altBackwardAction, QKeySequence(Qt::SHIFT | Qt::Key_Space)); 0586 connect(altBackwardAction, SIGNAL(triggered()), this, SLOT(slotBackward())); 0587 0588 // -- Tool control actions --------------------------------------------------------- 0589 0590 m_applyToolAction = new QAction(QIcon::fromTheme(QLatin1String("dialog-ok-apply")), 0591 i18nc("@action", "OK"), this); 0592 ac->addAction(QLatin1String("editorwindow_applytool"), m_applyToolAction); 0593 ac->setDefaultShortcut(m_applyToolAction, QKeySequence(Qt::Key_Return)); 0594 connect(m_applyToolAction, SIGNAL(triggered()), this, SLOT(slotApplyTool())); 0595 0596 m_closeToolAction = new QAction(QIcon::fromTheme(QLatin1String("dialog-cancel")), 0597 i18nc("@action", "Cancel"), this); 0598 ac->addAction(QLatin1String("editorwindow_closetool"), m_closeToolAction); 0599 ac->setDefaultShortcut(m_closeToolAction, QKeySequence(Qt::Key_Escape)); 0600 connect(m_closeToolAction, SIGNAL(triggered()), this, SLOT(slotCloseTool())); 0601 0602 toggleNonDestructiveActions(); 0603 toggleToolActions(); 0604 } 0605 0606 void EditorWindow::setupStatusBar() 0607 { 0608 m_nameLabel = new StatusProgressBar(statusBar()); 0609 m_nameLabel->setAlignment(Qt::AlignCenter); 0610 statusBar()->addWidget(m_nameLabel, 100); 0611 0612 d->infoLabel = new DAdjustableLabel(statusBar()); 0613 d->infoLabel->setAdjustedText(i18nc("@label", "No selection")); 0614 d->infoLabel->setAlignment(Qt::AlignCenter); 0615 statusBar()->addWidget(d->infoLabel, 100); 0616 d->infoLabel->setToolTip(i18nc("@info", "Information about current image selection")); 0617 0618 m_resLabel = new DAdjustableLabel(statusBar()); 0619 m_resLabel->setAlignment(Qt::AlignCenter); 0620 statusBar()->addWidget(m_resLabel, 100); 0621 m_resLabel->setToolTip(i18nc("@info", "Information about image size")); 0622 0623 d->zoomBar = new DZoomBar(statusBar()); 0624 d->zoomBar->setZoomToFitAction(d->zoomFitToWindowAction); 0625 d->zoomBar->setZoomTo100Action(d->zoomTo100percents); 0626 d->zoomBar->setZoomPlusAction(d->zoomPlusAction); 0627 d->zoomBar->setZoomMinusAction(d->zoomMinusAction); 0628 d->zoomBar->setBarMode(DZoomBar::PreviewZoomCtrl); 0629 statusBar()->addPermanentWidget(d->zoomBar); 0630 0631 connect(d->zoomBar, SIGNAL(signalZoomSliderChanged(int)), 0632 m_stackView, SLOT(slotZoomSliderChanged(int))); 0633 0634 connect(d->zoomBar, SIGNAL(signalZoomValueEdited(double)), 0635 m_stackView, SLOT(setZoomFactor(double))); 0636 0637 d->previewToolBar = new PreviewToolBar(statusBar()); 0638 d->previewToolBar->registerMenuActionGroup(this); 0639 d->previewToolBar->setEnabled(false); 0640 statusBar()->addPermanentWidget(d->previewToolBar); 0641 0642 connect(d->previewToolBar, SIGNAL(signalPreviewModeChanged(int)), 0643 this, SIGNAL(signalPreviewModeChanged(int))); 0644 0645 QWidget* const buttonsBox = new QWidget(statusBar()); 0646 QHBoxLayout* const hlay = new QHBoxLayout(buttonsBox); 0647 QButtonGroup* const buttonsGrp = new QButtonGroup(buttonsBox); 0648 buttonsGrp->setExclusive(false); 0649 0650 d->underExposureIndicator = new QToolButton(buttonsBox); 0651 d->underExposureIndicator->setDefaultAction(d->viewUnderExpoAction); 0652 d->underExposureIndicator->setFocusPolicy(Qt::NoFocus); 0653 0654 d->overExposureIndicator = new QToolButton(buttonsBox); 0655 d->overExposureIndicator->setDefaultAction(d->viewOverExpoAction); 0656 d->overExposureIndicator->setFocusPolicy(Qt::NoFocus); 0657 0658 d->cmViewIndicator = new QToolButton(buttonsBox); 0659 d->cmViewIndicator->setDefaultAction(d->viewCMViewAction); 0660 d->cmViewIndicator->setFocusPolicy(Qt::NoFocus); 0661 0662 buttonsGrp->addButton(d->underExposureIndicator); 0663 buttonsGrp->addButton(d->overExposureIndicator); 0664 buttonsGrp->addButton(d->cmViewIndicator); 0665 0666 hlay->setSpacing(0); 0667 hlay->setContentsMargins(QMargins()); 0668 hlay->addWidget(d->underExposureIndicator); 0669 hlay->addWidget(d->overExposureIndicator); 0670 hlay->addWidget(d->cmViewIndicator); 0671 0672 statusBar()->addPermanentWidget(buttonsBox); 0673 } 0674 0675 void EditorWindow::slotAboutToShowUndoMenu() 0676 { 0677 0678 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0679 0680 m_undoAction->popupMenu()->clear(); 0681 0682 #else 0683 0684 m_undoAction->menu()->clear(); 0685 0686 #endif 0687 0688 QStringList titles = m_canvas->interface()->getUndoHistory(); 0689 0690 for (int i = 0 ; i < titles.size() ; ++i) 0691 { 0692 0693 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0694 0695 QAction* const action = m_undoAction->popupMenu()->addAction(titles.at(i)); 0696 0697 #else 0698 0699 QAction* const action = m_undoAction->menu()->addAction(titles.at(i)); 0700 0701 #endif 0702 0703 int id = i + 1; 0704 0705 connect(action, &QAction::triggered, 0706 this, [this, id]() 0707 { 0708 m_canvas->slotUndo(id); 0709 } 0710 ); 0711 } 0712 } 0713 0714 void EditorWindow::slotAboutToShowRedoMenu() 0715 { 0716 0717 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0718 0719 m_redoAction->popupMenu()->clear(); 0720 0721 #else 0722 0723 m_redoAction->menu()->clear(); 0724 0725 #endif 0726 0727 QStringList titles = m_canvas->interface()->getRedoHistory(); 0728 0729 for (int i = 0 ; i < titles.size() ; ++i) 0730 { 0731 0732 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0733 0734 QAction* const action = m_redoAction->popupMenu()->addAction(titles.at(i)); 0735 0736 #else 0737 0738 QAction* const action = m_redoAction->menu()->addAction(titles.at(i)); 0739 0740 #endif 0741 0742 int id = i + 1; 0743 0744 connect(action, &QAction::triggered, 0745 this, [this, id]() 0746 { 0747 m_canvas->slotRedo(id); 0748 } 0749 ); 0750 } 0751 } 0752 0753 void EditorWindow::slotIncreaseZoom() 0754 { 0755 m_stackView->increaseZoom(); 0756 } 0757 0758 void EditorWindow::slotDecreaseZoom() 0759 { 0760 m_stackView->decreaseZoom(); 0761 } 0762 0763 void EditorWindow::slotToggleFitToWindow() 0764 { 0765 d->zoomPlusAction->setEnabled(true); 0766 d->zoomBar->setEnabled(true); 0767 d->zoomMinusAction->setEnabled(true); 0768 m_stackView->toggleFitToWindow(); 0769 } 0770 0771 void EditorWindow::slotFitToSelect() 0772 { 0773 d->zoomPlusAction->setEnabled(true); 0774 d->zoomBar->setEnabled(true); 0775 d->zoomMinusAction->setEnabled(true); 0776 m_stackView->fitToSelect(); 0777 } 0778 0779 void EditorWindow::slotZoomTo100Percents() 0780 { 0781 d->zoomPlusAction->setEnabled(true); 0782 d->zoomBar->setEnabled(true); 0783 d->zoomMinusAction->setEnabled(true); 0784 m_stackView->zoomTo100Percent(); 0785 } 0786 0787 void EditorWindow::slotZoomChanged(bool isMax, bool isMin, double zoom) 0788 { 0789 /* 0790 qCDebug(DIGIKAM_GENERAL_LOG) << "EditorWindow::slotZoomChanged"; 0791 */ 0792 d->zoomPlusAction->setEnabled(!isMax); 0793 d->zoomMinusAction->setEnabled(!isMin); 0794 0795 double zmin = m_stackView->zoomMin(); 0796 double zmax = m_stackView->zoomMax(); 0797 d->zoomBar->setZoom(zoom, zmin, zmax); 0798 } 0799 0800 void EditorWindow::slotToggleOffFitToWindow() 0801 { 0802 d->zoomFitToWindowAction->blockSignals(true); 0803 d->zoomFitToWindowAction->setChecked(false); 0804 d->zoomFitToWindowAction->blockSignals(false); 0805 } 0806 0807 void EditorWindow::readStandardSettings() 0808 { 0809 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0810 KConfigGroup group = config->group(configGroupName()); 0811 0812 // Restore full screen Mode 0813 0814 readFullScreenSettings(group); 0815 0816 // Restore Auto zoom action 0817 0818 bool autoZoom = group.readEntry(d->configAutoZoomEntry, true); 0819 0820 if (autoZoom) 0821 { 0822 d->zoomFitToWindowAction->trigger(); 0823 } 0824 0825 slotSetUnderExposureIndicator(group.readEntry(d->configUnderExposureIndicatorEntry, false)); 0826 slotSetOverExposureIndicator(group.readEntry(d->configOverExposureIndicatorEntry, false)); 0827 d->previewToolBar->readSettings(group); 0828 } 0829 0830 void EditorWindow::applyStandardSettings() 0831 { 0832 applyColorManagementSettings(); 0833 d->toolIface->updateICCSettings(); 0834 0835 applyIOSettings(); 0836 0837 // -- GUI Settings ------------------------------------------------------- 0838 0839 KConfigGroup group = KSharedConfig::openConfig()->group(configGroupName()); 0840 0841 d->legacyUpdateSplitterState(group); 0842 m_splitter->restoreState(group); 0843 readFullScreenSettings(group); 0844 0845 slotThemeChanged(); 0846 0847 // -- Exposure Indicators Settings --------------------------------------- 0848 0849 d->exposureSettings->underExposureColor = group.readEntry(d->configUnderExposureColorEntry, QColor(Qt::white)); 0850 d->exposureSettings->underExposurePercent = group.readEntry(d->configUnderExposurePercentsEntry, 1.0); 0851 d->exposureSettings->overExposureColor = group.readEntry(d->configOverExposureColorEntry, QColor(Qt::black)); 0852 d->exposureSettings->overExposurePercent = group.readEntry(d->configOverExposurePercentsEntry, 1.0); 0853 d->exposureSettings->exposureIndicatorMode = group.readEntry(d->configExpoIndicatorModeEntry, true); 0854 d->toolIface->updateExposureSettings(); 0855 0856 // -- Metadata Settings -------------------------------------------------- 0857 0858 MetaEngineSettingsContainer writeSettings = MetaEngineSettings::instance()->settings(); 0859 m_setExifOrientationTag = writeSettings.exifSetOrientation; 0860 m_canvas->setExifOrient(writeSettings.exifRotate); 0861 } 0862 0863 void EditorWindow::applyIOSettings() 0864 { 0865 // -- JPEG, PNG, TIFF, JPEG2000, PGF files format settings ---------------- 0866 0867 KConfigGroup group = KSharedConfig::openConfig()->group(configGroupName()); 0868 0869 m_IOFileSettings->JPEGCompression = DImgLoader::convertCompressionForLibJpeg(group.readEntry(d->configJpegCompressionEntry, 75)); 0870 0871 // Medium subsampling 0872 0873 m_IOFileSettings->JPEGSubSampling = group.readEntry(d->configJpegSubSamplingEntry, 1); 0874 0875 m_IOFileSettings->PNGCompression = DImgLoader::convertCompressionForLibPng(group.readEntry(d->configPngCompressionEntry, 9)); 0876 0877 // TIFF compression setting. 0878 0879 m_IOFileSettings->TIFFCompression = group.readEntry(d->configTiffCompressionEntry, false); 0880 0881 // JPEG2000 quality slider settings : 1 - 100 0882 0883 m_IOFileSettings->JPEG2000Compression = group.readEntry(d->configJpeg2000CompressionEntry, 75); 0884 0885 // JPEG2000 LossLess setting. 0886 0887 m_IOFileSettings->JPEG2000LossLess = group.readEntry(d->configJpeg2000LossLessEntry, true); 0888 0889 // PGF quality slider settings : 1 - 9 0890 0891 m_IOFileSettings->PGFCompression = group.readEntry(d->configPgfCompressionEntry, 3); 0892 0893 // PGF LossLess setting. 0894 0895 m_IOFileSettings->PGFLossLess = group.readEntry(d->configPgfLossLessEntry, true); 0896 0897 // HEIF quality slider settings : 1 - 100 0898 0899 m_IOFileSettings->HEIFCompression = group.readEntry(d->configHeifCompressionEntry, 75); 0900 0901 // HEIF LossLess setting. 0902 0903 m_IOFileSettings->HEIFLossLess = group.readEntry(d->configHeifLossLessEntry, true); 0904 0905 // JXL quality slider settings : 1 - 99 0906 0907 m_IOFileSettings->JXLCompression = group.readEntry(d->configJxlCompressionEntry, 75); 0908 0909 // JXL LossLess setting. 0910 0911 m_IOFileSettings->JXLLossLess = group.readEntry(d->configJxlLossLessEntry, true); 0912 0913 // WEBP quality slider settings : 1 - 99 0914 0915 m_IOFileSettings->WEBPCompression = group.readEntry(d->configWebpCompressionEntry, 75); 0916 0917 // WEBP LossLess setting. 0918 0919 m_IOFileSettings->WEBPLossLess = group.readEntry(d->configWebpLossLessEntry, true); 0920 0921 // AVIF quality slider settings : 1 - 99 0922 0923 m_IOFileSettings->AVIFCompression = group.readEntry(d->configAvifCompressionEntry, 75); 0924 0925 // AVIF LossLess setting. 0926 0927 m_IOFileSettings->AVIFLossLess = group.readEntry(d->configAvifLossLessEntry, true); 0928 0929 // -- RAW images decoding settings ------------------------------------------------------ 0930 0931 m_IOFileSettings->useRAWImport = group.readEntry(d->configUseRawImportToolEntry, false); 0932 m_IOFileSettings->rawImportToolIid = group.readEntry(d->configRawImportToolIidEntry, QString::fromLatin1("org.kde.digikam.plugin.rawimport.Native")); 0933 DRawDecoderWidget::readSettings(m_IOFileSettings->rawDecodingSettings.rawPrm, group); 0934 0935 // Raw Color Management settings: 0936 // If digiKam Color Management is enabled, no need to correct color of decoded RAW image, 0937 // else, sRGB color workspace will be used. 0938 0939 ICCSettingsContainer settings = IccSettings::instance()->settings(); 0940 0941 if (settings.enableCM) 0942 { 0943 if (settings.defaultUncalibratedBehavior & ICCSettingsContainer::AutomaticColors) 0944 { 0945 m_IOFileSettings->rawDecodingSettings.rawPrm.outputColorSpace = DRawDecoderSettings::CUSTOMOUTPUTCS; 0946 m_IOFileSettings->rawDecodingSettings.rawPrm.outputProfile = settings.workspaceProfile; 0947 } 0948 else 0949 { 0950 m_IOFileSettings->rawDecodingSettings.rawPrm.outputColorSpace = DRawDecoderSettings::RAWCOLOR; 0951 } 0952 } 0953 else 0954 { 0955 m_IOFileSettings->rawDecodingSettings.rawPrm.outputColorSpace = DRawDecoderSettings::SRGB; 0956 } 0957 } 0958 0959 void EditorWindow::applyColorManagementSettings() 0960 { 0961 ICCSettingsContainer settings = IccSettings::instance()->settings(); 0962 0963 d->toolIface->updateICCSettings(); 0964 m_canvas->setICCSettings(settings); 0965 0966 d->viewCMViewAction->blockSignals(true); 0967 d->viewCMViewAction->setEnabled(settings.enableCM); 0968 d->viewCMViewAction->setChecked(settings.useManagedView); 0969 setColorManagedViewIndicatorToolTip(settings.enableCM, settings.useManagedView); 0970 d->viewCMViewAction->blockSignals(false); 0971 0972 d->viewSoftProofAction->setEnabled(settings.enableCM && !settings.defaultProofProfile.isEmpty()); 0973 d->softProofOptionsAction->setEnabled(settings.enableCM); 0974 } 0975 0976 void EditorWindow::saveStandardSettings() 0977 { 0978 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0979 KConfigGroup group = config->group(configGroupName()); 0980 0981 group.writeEntry(d->configAutoZoomEntry, d->zoomFitToWindowAction->isChecked()); 0982 m_splitter->saveState(group); 0983 0984 group.writeEntry("Show Thumbbar", thumbBar()->shouldBeVisible()); 0985 group.writeEntry(d->configUnderExposureIndicatorEntry, d->exposureSettings->underExposureIndicator); 0986 group.writeEntry(d->configOverExposureIndicatorEntry, d->exposureSettings->overExposureIndicator); 0987 d->previewToolBar->writeSettings(group); 0988 config->sync(); 0989 } 0990 0991 /** 0992 * Method used by Editor Tools. Only tools based on imageregionwidget support zooming. 0993 * TODO: Fix this behavior when editor tool preview widgets will be factored. 0994 */ 0995 void EditorWindow::toggleZoomActions(bool val) 0996 { 0997 d->zoomMinusAction->setEnabled(val); 0998 d->zoomPlusAction->setEnabled(val); 0999 d->zoomTo100percents->setEnabled(val); 1000 d->zoomFitToWindowAction->setEnabled(val); 1001 d->zoomBar->setEnabled(val); 1002 } 1003 1004 void EditorWindow::readSettings() 1005 { 1006 readStandardSettings(); 1007 } 1008 1009 void EditorWindow::saveSettings() 1010 { 1011 saveStandardSettings(); 1012 } 1013 1014 void EditorWindow::toggleActions(bool val) 1015 { 1016 toggleStandardActions(val); 1017 } 1018 1019 bool EditorWindow::actionEnabledState() const 1020 { 1021 return m_actionEnabledState; 1022 } 1023 1024 void EditorWindow::toggleStandardActions(bool val) 1025 { 1026 d->zoomFitToSelectAction->setEnabled(val); 1027 toggleZoomActions(val); 1028 1029 m_actionEnabledState = val; 1030 1031 m_forwardAction->setEnabled(val); 1032 m_backwardAction->setEnabled(val); 1033 m_firstAction->setEnabled(val); 1034 m_lastAction->setEnabled(val); 1035 d->rotateLeftAction->setEnabled(val); 1036 d->rotateRightAction->setEnabled(val); 1037 d->flipHorizAction->setEnabled(val); 1038 d->flipVertAction->setEnabled(val); 1039 m_fileDeleteAction->setEnabled(val); 1040 m_saveAsAction->setEnabled(val); 1041 d->openWithAction->setEnabled(val); 1042 m_exportAction->setEnabled(val); 1043 d->selectAllAction->setEnabled(val); 1044 d->selectNoneAction->setEnabled(val); 1045 1046 QList<DPluginAction*> actions = DPluginLoader::instance()->pluginsActions(DPluginAction::Generic, this); 1047 1048 Q_FOREACH (DPluginAction* const ac, actions) 1049 { 1050 ac->setEnabled(val); 1051 } 1052 1053 // these actions are special: They are turned off if val is false, 1054 // but if val is true, they may be turned on or off. 1055 1056 if (val) 1057 { 1058 // Update actions by retrieving current values 1059 1060 slotUndoStateChanged(); 1061 } 1062 else 1063 { 1064 m_openVersionAction->setEnabled(false); 1065 m_revertAction->setEnabled(false); 1066 m_saveAction->setEnabled(false); 1067 m_saveCurrentVersionAction->setEnabled(false); 1068 m_saveNewVersionAction->setEnabled(false); 1069 m_discardChangesAction->setEnabled(false); 1070 m_undoAction->setEnabled(false); 1071 m_redoAction->setEnabled(false); 1072 } 1073 1074 // Editor Tools actions 1075 1076 QList<DPluginAction*> actions2 = DPluginLoader::instance()->pluginsActions(DPluginAction::Editor, this); 1077 1078 Q_FOREACH (DPluginAction* const ac, actions2) 1079 { 1080 ac->setEnabled(val); 1081 } 1082 } 1083 1084 void EditorWindow::toggleNonDestructiveActions() 1085 { 1086 m_saveAction->setVisible(!m_nonDestructive); 1087 m_saveAsAction->setVisible(!m_nonDestructive); 1088 m_revertAction->setVisible(!m_nonDestructive); 1089 1090 m_openVersionAction->setVisible(m_nonDestructive); 1091 m_saveCurrentVersionAction->setVisible(m_nonDestructive); 1092 m_saveNewVersionAction->setVisible(m_nonDestructive); 1093 m_exportAction->setVisible(m_nonDestructive); 1094 m_discardChangesAction->setVisible(m_nonDestructive); 1095 } 1096 1097 void EditorWindow::toggleToolActions(EditorTool* tool) 1098 { 1099 if (tool) 1100 { 1101 m_applyToolAction->setText(tool->toolSettings()->button(EditorToolSettings::Ok)->text()); 1102 m_applyToolAction->setIcon(tool->toolSettings()->button(EditorToolSettings::Ok)->icon()); 1103 m_applyToolAction->setToolTip(tool->toolSettings()->button(EditorToolSettings::Ok)->toolTip()); 1104 1105 m_closeToolAction->setText(tool->toolSettings()->button(EditorToolSettings::Cancel)->text()); 1106 m_closeToolAction->setIcon(tool->toolSettings()->button(EditorToolSettings::Cancel)->icon()); 1107 m_closeToolAction->setToolTip(tool->toolSettings()->button(EditorToolSettings::Cancel)->toolTip()); 1108 } 1109 1110 m_applyToolAction->setVisible(tool); 1111 m_closeToolAction->setVisible(tool); 1112 } 1113 1114 void EditorWindow::slotLoadingProgress(const QString&, float progress) 1115 { 1116 m_nameLabel->setProgressValue((int)(progress * 100.0)); 1117 } 1118 1119 void EditorWindow::slotSavingProgress(const QString&, float progress) 1120 { 1121 m_nameLabel->setProgressValue((int)(progress * 100.0)); 1122 1123 if (m_savingProgressDialog) 1124 { 1125 m_savingProgressDialog->setValue((int)(progress * 100.0)); 1126 } 1127 } 1128 1129 void EditorWindow::execSavingProgressDialog() 1130 { 1131 if (m_savingProgressDialog) 1132 { 1133 return; 1134 } 1135 1136 m_savingProgressDialog = new QProgressDialog(this); 1137 m_savingProgressDialog->setWindowTitle(i18nc("@title:window", "Saving Image...")); 1138 m_savingProgressDialog->setLabelText(i18nc("@label", "Please wait for the image to be saved...")); 1139 m_savingProgressDialog->setAttribute(Qt::WA_DeleteOnClose); 1140 m_savingProgressDialog->setAutoClose(true); 1141 m_savingProgressDialog->setMinimumDuration(1000); 1142 m_savingProgressDialog->setMaximum(100); 1143 1144 // we must enter a fully modal dialog, no QEventLoop is sufficient for KWin to accept longer waiting times 1145 1146 m_savingProgressDialog->setModal(true); 1147 m_savingProgressDialog->exec(); 1148 } 1149 1150 bool EditorWindow::promptForOverWrite() 1151 { 1152 1153 QUrl destination = saveDestinationUrl(); 1154 1155 if (destination.isLocalFile()) 1156 { 1157 1158 QFileInfo fi(m_canvas->currentImageFilePath()); 1159 QString warnMsg(i18nc("@info", "About to overwrite file \"%1\"\nAre you sure?", 1160 QDir::toNativeSeparators(fi.fileName()))); 1161 1162 return (DMessageBox::showContinueCancel(QMessageBox::Warning, 1163 this, 1164 i18nc("@title:window, warning while saving progress", "Warning"), 1165 warnMsg, 1166 QLatin1String("editorWindowSaveOverwrite")) 1167 == QMessageBox::Yes); 1168 1169 } 1170 else 1171 { 1172 // in this case it will handle the overwrite request 1173 1174 return true; 1175 } 1176 } 1177 1178 void EditorWindow::slotUndoStateChanged() 1179 { 1180 UndoState state = m_canvas->interface()->undoState(); 1181 1182 // RAW conversion qualifies as a "non-undoable" action 1183 // You can save as new version, but cannot undo or revert 1184 1185 m_undoAction->setEnabled(state.hasUndo); 1186 m_redoAction->setEnabled(state.hasRedo); 1187 m_revertAction->setEnabled(state.hasUndoableChanges); 1188 1189 m_saveAction->setEnabled(state.hasChanges); 1190 m_saveCurrentVersionAction->setEnabled(state.hasChanges); 1191 m_saveNewVersionAction->setEnabled(state.hasChanges); 1192 m_discardChangesAction->setEnabled(state.hasUndoableChanges); 1193 1194 m_openVersionAction->setEnabled(hasOriginalToRestore()); 1195 } 1196 1197 bool EditorWindow::hasOriginalToRestore() 1198 { 1199 return m_canvas->interface()->getResolvedInitialHistory().hasOriginalReferredImage(); 1200 } 1201 1202 DImageHistory EditorWindow::resolvedImageHistory(const DImageHistory& history) 1203 { 1204 // simple, database-less version 1205 1206 DImageHistory r = history; 1207 QList<DImageHistory::Entry>::iterator it; 1208 1209 for (it = r.entries().begin() ; it != r.entries().end() ; ++it) 1210 { 1211 QList<HistoryImageId>::iterator hit; 1212 1213 for (hit = it->referredImages.begin() ; hit != it->referredImages.end() ; ) 1214 { 1215 QFileInfo info(hit->m_filePath + QLatin1Char('/') + hit->m_fileName); 1216 1217 if (!info.exists()) 1218 { 1219 hit = it->referredImages.erase(hit); 1220 } 1221 else 1222 { 1223 ++hit; 1224 } 1225 } 1226 } 1227 1228 return r; 1229 } 1230 1231 bool EditorWindow::promptUserSave(const QUrl& url, SaveAskMode mode, bool allowCancel) 1232 { 1233 if (d->currentWindowModalDialog) 1234 { 1235 d->currentWindowModalDialog->reject(); 1236 } 1237 1238 if (m_canvas->interface()->undoState().hasUndoableChanges) 1239 { 1240 // if window is minimized, show it 1241 1242 unminimizeAndActivateWindow(); 1243 1244 bool shallSave = true; 1245 bool shallDiscard = false; 1246 bool newVersion = false; 1247 1248 if (mode == AskIfNeeded) 1249 { 1250 if (m_nonDestructive) 1251 { 1252 if (versionManager()->settings().editorClosingMode == VersionManagerSettings::AutoSave) 1253 { 1254 shallSave = true; 1255 } 1256 else 1257 { 1258 QPointer<VersioningPromptUserSaveDialog> dialog = new VersioningPromptUserSaveDialog(this); 1259 1260 if (dialog->exec() == QDialog::Rejected) 1261 { 1262 return false; 1263 } 1264 1265 shallSave = dialog->shallSave() || dialog->newVersion(); 1266 shallDiscard = dialog->shallDiscard(); 1267 newVersion = dialog->newVersion(); 1268 } 1269 } 1270 else 1271 { 1272 QString boxMessage; 1273 boxMessage = i18nc("@info", 1274 "The image \"%1\" has been modified.\n" 1275 "Do you want to save it?", url.fileName()); 1276 1277 int result; 1278 1279 if (allowCancel) 1280 { 1281 result = QMessageBox::warning(this, qApp->applicationName(), boxMessage, 1282 QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); 1283 } 1284 else 1285 { 1286 result = QMessageBox::warning(this, qApp->applicationName(), boxMessage, 1287 QMessageBox::Save | QMessageBox::Discard); 1288 } 1289 1290 shallSave = (result == QMessageBox::Save); 1291 shallDiscard = (result == QMessageBox::Discard); 1292 } 1293 } 1294 1295 if (shallSave) 1296 { 1297 bool saving = false; 1298 1299 switch (mode) 1300 { 1301 case AskIfNeeded: 1302 { 1303 if (m_nonDestructive) 1304 { 1305 if (newVersion) 1306 { 1307 saving = saveNewVersion(); 1308 } 1309 else 1310 { 1311 // will know on its own if new version is required 1312 1313 saving = saveCurrentVersion(); 1314 } 1315 } 1316 else 1317 { 1318 if (m_canvas->isReadOnly()) 1319 { 1320 saving = saveAs(); 1321 } 1322 else if (promptForOverWrite()) 1323 { 1324 saving = save(); 1325 } 1326 } 1327 1328 break; 1329 } 1330 1331 case OverwriteWithoutAsking: 1332 { 1333 if (m_nonDestructive) 1334 { 1335 if (newVersion) 1336 { 1337 saving = saveNewVersion(); 1338 } 1339 else 1340 { 1341 // will know on its own if new version is required 1342 1343 saving = saveCurrentVersion(); 1344 } 1345 } 1346 else 1347 { 1348 if (m_canvas->isReadOnly()) 1349 { 1350 saving = saveAs(); 1351 } 1352 else 1353 { 1354 saving = save(); 1355 } 1356 } 1357 1358 break; 1359 } 1360 1361 case AlwaysSaveAs: 1362 { 1363 if (m_nonDestructive) 1364 { 1365 saving = saveNewVersion(); 1366 } 1367 else 1368 { 1369 saving = saveAs(); 1370 } 1371 1372 break; 1373 } 1374 } 1375 1376 // save and saveAs return false if they were canceled and did not enter saving at all 1377 // In this case, do not call enterWaitingLoop because quitWaitingloop will not be called. 1378 1379 if (saving) 1380 { 1381 // Waiting for asynchronous image file saving operation running in separate thread. 1382 1383 m_savingContext.synchronizingState = SavingContext::SynchronousSaving; 1384 enterWaitingLoop(); 1385 m_savingContext.synchronizingState = SavingContext::NormalSaving; 1386 1387 return m_savingContext.synchronousSavingResult; 1388 } 1389 else 1390 { 1391 return false; 1392 } 1393 } 1394 else if (shallDiscard) 1395 { 1396 // Discard 1397 1398 m_saveAction->setEnabled(false); 1399 1400 return true; 1401 } 1402 else 1403 { 1404 return false; 1405 } 1406 } 1407 1408 return true; 1409 } 1410 1411 bool EditorWindow::promptUserDelete(const QUrl& url) 1412 { 1413 if (d->currentWindowModalDialog) 1414 { 1415 d->currentWindowModalDialog->reject(); 1416 } 1417 1418 if (m_canvas->interface()->undoState().hasUndoableChanges) 1419 { 1420 // if window is minimized, show it 1421 1422 unminimizeAndActivateWindow(); 1423 1424 QString boxMessage = i18nc("@info", 1425 "The image \"%1\" has been modified.\n" 1426 "All changes will be lost.", url.fileName()); 1427 1428 int result = DMessageBox::showContinueCancel(QMessageBox::Warning, 1429 this, 1430 QString(), 1431 boxMessage); 1432 1433 if (result == QMessageBox::Cancel) 1434 { 1435 return false; 1436 } 1437 } 1438 1439 return true; 1440 } 1441 1442 bool EditorWindow::waitForSavingToComplete() 1443 { 1444 // avoid reentrancy - return false means we have reentered the loop already. 1445 1446 if (m_savingContext.synchronizingState == SavingContext::SynchronousSaving) 1447 { 1448 return false; 1449 } 1450 1451 if (m_savingContext.savingState != SavingContext::SavingStateNone) 1452 { 1453 // Waiting for asynchronous image file saving operation running in separate thread. 1454 1455 m_savingContext.synchronizingState = SavingContext::SynchronousSaving; 1456 1457 enterWaitingLoop(); 1458 m_savingContext.synchronizingState = SavingContext::NormalSaving; 1459 } 1460 1461 return true; 1462 } 1463 1464 void EditorWindow::enterWaitingLoop() 1465 { 1466 //d->waitingLoop->exec(QEventLoop::ExcludeUserInputEvents); 1467 1468 execSavingProgressDialog(); 1469 } 1470 1471 void EditorWindow::quitWaitingLoop() 1472 { 1473 //d->waitingLoop->quit(); 1474 1475 if (m_savingProgressDialog) 1476 { 1477 m_savingProgressDialog->close(); 1478 } 1479 } 1480 1481 void EditorWindow::slotSelected(bool val) 1482 { 1483 // Update menu actions. 1484 1485 d->cropAction->setEnabled(val); 1486 d->zoomFitToSelectAction->setEnabled(val); 1487 d->copyAction->setEnabled(val); 1488 1489 QRect sel = m_canvas->getSelectedArea(); 1490 1491 // Update histogram into sidebar. 1492 1493 Q_EMIT signalSelectionChanged(sel); 1494 1495 // Update status bar 1496 1497 if (val) 1498 { 1499 slotSelectionSetText(sel); 1500 } 1501 else 1502 { 1503 setToolInfoMessage(i18nc("@info", "No selection")); 1504 } 1505 } 1506 1507 void EditorWindow::slotPrepareToLoad() 1508 { 1509 // Disable actions as appropriate during loading 1510 1511 Q_EMIT signalNoCurrentItem(); 1512 unsetCursor(); 1513 m_animLogo->stop(); 1514 toggleActions(false); 1515 slotUpdateItemInfo(); 1516 } 1517 1518 void EditorWindow::slotLoadingStarted(const QString& /*filename*/) 1519 { 1520 setCursor(Qt::WaitCursor); 1521 toggleActions(false); 1522 m_animLogo->start(); 1523 m_nameLabel->setProgressBarMode(StatusProgressBar::ProgressBarMode, i18nc("@label", "Loading:")); 1524 } 1525 1526 void EditorWindow::slotLoadingFinished(const QString& filename, bool success) 1527 { 1528 m_nameLabel->setProgressBarMode(StatusProgressBar::TextMode); 1529 1530 // Enable actions as appropriate after loading 1531 // No need to re-enable image properties sidebar here, it's will be done 1532 // automatically by a signal from canvas 1533 1534 toggleActions(success); 1535 slotUpdateItemInfo(); 1536 unsetCursor(); 1537 m_animLogo->stop(); 1538 1539 if (success) 1540 { 1541 colorManage(); 1542 1543 // Set a history which contains all available files as referredImages 1544 1545 DImageHistory resolved = resolvedImageHistory(m_canvas->interface()->getInitialImageHistory()); 1546 m_canvas->interface()->setResolvedInitialHistory(resolved); 1547 } 1548 else 1549 { 1550 DNotificationPopup::message(DNotificationPopup::Boxed, 1551 i18nc("@info", "Cannot load \"%1\"", filename), 1552 m_canvas, m_canvas->mapToGlobal(QPoint(30, 30))); 1553 } 1554 } 1555 1556 void EditorWindow::resetOrigin() 1557 { 1558 // With versioning, "only" resetting undo history does not work anymore 1559 // as we calculate undo state based on the initial history stored in the DImg 1560 1561 resetOriginSwitchFile(); 1562 } 1563 1564 void EditorWindow::resetOriginSwitchFile() 1565 { 1566 DImageHistory resolved = resolvedImageHistory(m_canvas->interface()->getItemHistory()); 1567 m_canvas->interface()->switchToLastSaved(resolved); 1568 } 1569 1570 void EditorWindow::colorManage() 1571 { 1572 if (!IccSettings::instance()->isEnabled()) 1573 { 1574 return; 1575 } 1576 1577 DImg image = m_canvas->currentImage(); 1578 1579 if (image.isNull()) 1580 { 1581 return; 1582 } 1583 1584 if (!IccManager::needsPostLoadingManagement(image)) 1585 { 1586 return; 1587 } 1588 1589 IccPostLoadingManager manager(image, m_canvas->currentImageFilePath()); 1590 1591 if (!manager.hasValidWorkspace()) 1592 { 1593 QString message = i18nc("@info", "Cannot open the specified working space profile (\"%1\"). " 1594 "No color transformation will be applied. " 1595 "Please check the color management " 1596 "configuration in digiKam's setup.", 1597 IccSettings::instance()->settings().workspaceProfile); 1598 QMessageBox::information(this, qApp->applicationName(), message); 1599 } 1600 1601 // Show dialog and get transform from user choice 1602 1603 IccTransform trans = manager.postLoadingManage(this); 1604 1605 // apply transform in thread. 1606 // Do _not_ test for willHaveEffect() here - there are more side effects when calling this method 1607 1608 m_canvas->applyTransform(trans); 1609 slotUpdateItemInfo(); 1610 } 1611 1612 void EditorWindow::slotNameLabelCancelButtonPressed() 1613 { 1614 // If we saving an image... 1615 1616 if (m_savingContext.savingState != SavingContext::SavingStateNone) 1617 { 1618 m_savingContext.abortingSaving = true; 1619 m_canvas->abortSaving(); 1620 } 1621 } 1622 1623 void EditorWindow::slotFileOriginChanged(const QString&) 1624 { 1625 // implemented in subclass 1626 } 1627 1628 bool EditorWindow::saveOrSaveAs() 1629 { 1630 if (m_canvas->isReadOnly()) 1631 { 1632 return saveAs(); 1633 } 1634 1635 return save(); 1636 } 1637 1638 void EditorWindow::slotSavingStarted(const QString& /*filename*/) 1639 { 1640 setCursor(Qt::WaitCursor); 1641 m_animLogo->start(); 1642 1643 // Disable actions as appropriate during saving 1644 1645 Q_EMIT signalNoCurrentItem(); 1646 1647 toggleActions(false); 1648 1649 m_nameLabel->setProgressBarMode(StatusProgressBar::CancelProgressBarMode, 1650 i18nc("@label: saving progress on status bar", "Saving:")); 1651 } 1652 1653 void EditorWindow::slotSavingFinished(const QString& filename, bool success) 1654 { 1655 Q_UNUSED(filename); 1656 1657 qCDebug(DIGIKAM_GENERAL_LOG) << filename << success 1658 << (m_savingContext.savingState != SavingContext::SavingStateNone); 1659 1660 // only handle this if we really wanted to save a file... 1661 1662 if (m_savingContext.savingState != SavingContext::SavingStateNone) 1663 { 1664 m_savingContext.executedOperation = m_savingContext.savingState; 1665 m_savingContext.savingState = SavingContext::SavingStateNone; 1666 1667 if (!success) 1668 { 1669 if (!m_savingContext.abortingSaving) 1670 { 1671 QMessageBox::critical(this, qApp->applicationName(), 1672 i18nc("@info", "Failed to save file\n\"%1\"\nto\n\"%2\".", 1673 m_savingContext.destinationURL.fileName(), 1674 m_savingContext.destinationURL.toLocalFile())); 1675 } 1676 1677 finishSaving(false); 1678 return; 1679 } 1680 1681 moveFile(); 1682 1683 } 1684 else 1685 { 1686 qCWarning(DIGIKAM_GENERAL_LOG) << "Why was slotSavingFinished called if we did not want to save a file?"; 1687 } 1688 } 1689 1690 void EditorWindow::movingSaveFileFinished(bool successful) 1691 { 1692 if (!successful) 1693 { 1694 finishSaving(false); 1695 return; 1696 } 1697 1698 // now that we know the real destination file name, pass it to be recorded in image history 1699 1700 m_canvas->interface()->setLastSaved(m_savingContext.destinationURL.toLocalFile()); 1701 1702 // remove image from cache since it has changed 1703 1704 LoadingCacheInterface::fileChanged(m_savingContext.destinationURL.toLocalFile()); 1705 ThumbnailLoadThread::deleteThumbnail(m_savingContext.destinationURL.toLocalFile()); 1706 1707 // restore state of disabled actions. saveIsComplete can start any other task 1708 // (loading!) which might itself in turn change states 1709 1710 finishSaving(true); 1711 1712 switch (m_savingContext.executedOperation) 1713 { 1714 case SavingContext::SavingStateNone: 1715 { 1716 break; 1717 } 1718 1719 case SavingContext::SavingStateSave: 1720 { 1721 saveIsComplete(); 1722 break; 1723 } 1724 1725 case SavingContext::SavingStateSaveAs: 1726 { 1727 saveAsIsComplete(); 1728 break; 1729 } 1730 1731 case SavingContext::SavingStateVersion: 1732 { 1733 saveVersionIsComplete(); 1734 break; 1735 } 1736 } 1737 1738 // Take all actions necessary to update information and re-enable sidebar 1739 1740 slotChanged(); 1741 } 1742 1743 void EditorWindow::finishSaving(bool success) 1744 { 1745 m_savingContext.synchronousSavingResult = success; 1746 1747 delete m_savingContext.saveTempFile; 1748 m_savingContext.saveTempFile = nullptr; 1749 1750 // Exit of internal Qt event loop to unlock promptUserSave() method. 1751 1752 if (m_savingContext.synchronizingState == SavingContext::SynchronousSaving) 1753 { 1754 quitWaitingLoop(); 1755 } 1756 1757 // Enable actions as appropriate after saving 1758 1759 toggleActions(true); 1760 unsetCursor(); 1761 m_animLogo->stop(); 1762 1763 m_nameLabel->setProgressBarMode(StatusProgressBar::TextMode); 1764 /* 1765 if (m_savingProgressDialog) 1766 { 1767 m_savingProgressDialog->close(); 1768 } 1769 */ 1770 1771 // On error, continue using current image 1772 1773 if (!success) 1774 { 1775 /* Why this? 1776 * m_canvas->switchToLastSaved(m_savingContext.srcURL.toLocalFile()); 1777 */ 1778 } 1779 } 1780 1781 void EditorWindow::setupTempSaveFile(const QUrl& url) 1782 { 1783 // if the destination url is on local file system, try to set the temp file 1784 // location to the destination folder, otherwise use a local default 1785 1786 QString tempDir = url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile(); 1787 1788 if (!url.isLocalFile() || tempDir.isEmpty()) 1789 { 1790 tempDir = QDir::tempPath(); 1791 } 1792 1793 QFileInfo fi(url.toLocalFile()); 1794 QString suffix = fi.suffix(); 1795 1796 // use magic file extension which tells the digikamalbums ioslave to ignore the file 1797 1798 m_savingContext.saveTempFile = new SafeTemporaryFile(tempDir + QLatin1String("/EditorWindow-XXXXXX" 1799 ".digikamtempfile.") + suffix); 1800 m_savingContext.saveTempFile->setAutoRemove(false); 1801 1802 if (!m_savingContext.saveTempFile->open()) 1803 { 1804 QMessageBox::critical(this, qApp->applicationName(), 1805 i18nc("@info", "Could not open a temporary file in the folder \"%1\": %2 (%3)", 1806 QDir::toNativeSeparators(tempDir), m_savingContext.saveTempFile->errorString(), 1807 m_savingContext.saveTempFile->error())); 1808 return; 1809 } 1810 1811 m_savingContext.saveTempFileName = m_savingContext.saveTempFile->safeFilePath(); 1812 delete m_savingContext.saveTempFile; 1813 m_savingContext.saveTempFile = nullptr; 1814 } 1815 1816 void EditorWindow::startingSave(const QUrl& url) 1817 { 1818 qCDebug(DIGIKAM_GENERAL_LOG) << "startSaving url = " << url; 1819 1820 // avoid any reentrancy. Should be impossible anyway since actions will be disabled. 1821 1822 if (m_savingContext.savingState != SavingContext::SavingStateNone) 1823 { 1824 return; 1825 } 1826 1827 m_savingContext = SavingContext(); 1828 1829 if (!checkPermissions(url)) 1830 { 1831 return; 1832 } 1833 1834 setupTempSaveFile(url); 1835 1836 m_savingContext.srcURL = url; 1837 m_savingContext.destinationURL = m_savingContext.srcURL; 1838 m_savingContext.destinationExisted = true; 1839 m_savingContext.originalFormat = m_canvas->currentImageFileFormat(); 1840 m_savingContext.format = m_savingContext.originalFormat; 1841 m_savingContext.abortingSaving = false; 1842 m_savingContext.savingState = SavingContext::SavingStateSave; 1843 m_savingContext.executedOperation = SavingContext::SavingStateNone; 1844 1845 m_canvas->interface()->saveAs(m_savingContext.saveTempFileName, m_IOFileSettings, 1846 m_setExifOrientationTag && m_canvas->exifRotated(), m_savingContext.format, 1847 m_savingContext.destinationURL.toLocalFile()); 1848 } 1849 1850 bool EditorWindow::showFileSaveDialog(const QUrl& initialUrl, QUrl& newURL) 1851 { 1852 QString all; 1853 QStringList list = supportedImageMimeTypes(QIODevice::WriteOnly, all); 1854 QPointer<DFileDialog> imageFileSaveDialog = new DFileDialog(this); 1855 imageFileSaveDialog->setWindowTitle(i18nc("@title:window", "New Image File Name")); 1856 imageFileSaveDialog->setDirectoryUrl(initialUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash)); 1857 imageFileSaveDialog->setOption(QFileDialog::DontConfirmOverwrite); 1858 imageFileSaveDialog->setAcceptMode(QFileDialog::AcceptSave); 1859 imageFileSaveDialog->setFileMode(QFileDialog::AnyFile); 1860 imageFileSaveDialog->setNameFilters(list); 1861 1862 // Restore old settings for the dialog 1863 1864 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 1865 KConfigGroup group = config->group(configGroupName()); 1866 const QString optionLastExtension = QLatin1String("LastSavedImageExtension"); 1867 QString ext = group.readEntry(optionLastExtension, "png"); 1868 1869 Q_FOREACH (const QString& s, list) 1870 { 1871 if (s.contains(QString::fromLatin1("*.%1").arg(ext))) 1872 { // cppcheck-suppress useStlAlgorithm 1873 imageFileSaveDialog->selectNameFilter(s); 1874 break; 1875 } 1876 } 1877 1878 // Adjust extension of proposed filename 1879 1880 QString fileName = initialUrl.fileName(); 1881 1882 if (!fileName.isNull()) 1883 { 1884 int lastDot = fileName.lastIndexOf(QLatin1Char('.')); 1885 QString completeBaseName = (lastDot == -1) ? fileName : fileName.left(lastDot); 1886 fileName = completeBaseName + QLatin1Char('.') + ext; 1887 } 1888 1889 if (!fileName.isNull()) 1890 { 1891 imageFileSaveDialog->selectFile(fileName); 1892 } 1893 1894 // Start dialog and check if canceled. 1895 1896 if (d->currentWindowModalDialog) 1897 { 1898 // go application-modal - we will create utter confusion if descending into more than one window-modal dialog 1899 1900 imageFileSaveDialog->setModal(true); 1901 imageFileSaveDialog->exec(); 1902 } 1903 else 1904 { 1905 imageFileSaveDialog->setWindowModality(Qt::WindowModal); 1906 d->currentWindowModalDialog = imageFileSaveDialog; 1907 imageFileSaveDialog->exec(); 1908 d->currentWindowModalDialog = nullptr; 1909 } 1910 1911 qApp->processEvents(); 1912 1913 if (!imageFileSaveDialog || !imageFileSaveDialog->hasAcceptedUrls()) 1914 { 1915 qCDebug(DIGIKAM_GENERAL_LOG) << "File Save Dialog rejected"; 1916 delete imageFileSaveDialog; 1917 1918 return false; 1919 } 1920 1921 newURL = imageFileSaveDialog->selectedUrls().first(); 1922 newURL.setPath(QDir::cleanPath(newURL.path())); 1923 1924 QFileInfo fi(newURL.fileName()); 1925 1926 if (fi.suffix().isEmpty()) 1927 { 1928 ext = imageFileSaveDialog->selectedNameFilter().section(QLatin1String("*."), 1, 1); 1929 ext = ext.left(ext.length() - 1); 1930 1931 if (ext.isEmpty()) 1932 { 1933 ext = QLatin1String("jpg"); 1934 } 1935 1936 newURL.setPath(newURL.path() + QLatin1Char('.') + ext); 1937 } 1938 1939 delete imageFileSaveDialog; 1940 1941 qCDebug(DIGIKAM_GENERAL_LOG) << "Writing file to " << newURL; 1942 1943 //-- Show Settings Dialog ---------------------------------------------- 1944 1945 const QString configShowImageSettingsDialog = QLatin1String("ShowImageSettingsDialog"); 1946 bool showDialog = group.readEntry(configShowImageSettingsDialog, true); 1947 QPointer<FileSaveOptionsBox> options = new FileSaveOptionsBox(); 1948 1949 if (showDialog && (options->discoverFormat(newURL.fileName(), FileSaveOptionsBox::NONE) != FileSaveOptionsBox::NONE)) 1950 { 1951 QPointer<FileSaveOptionsDlg> fileSaveOptionsDialog = new FileSaveOptionsDlg(this, options); 1952 options->setImageFileFormat(newURL.fileName()); 1953 int result; 1954 1955 if (d->currentWindowModalDialog) 1956 { 1957 // go application-modal - we will create utter confusion if descending into more than one window-modal dialog 1958 1959 fileSaveOptionsDialog->setModal(true); 1960 result = fileSaveOptionsDialog->exec(); 1961 } 1962 else 1963 { 1964 fileSaveOptionsDialog->setWindowModality(Qt::WindowModal); 1965 d->currentWindowModalDialog = fileSaveOptionsDialog; 1966 result = fileSaveOptionsDialog->exec(); 1967 d->currentWindowModalDialog = nullptr; 1968 } 1969 1970 if ((result != QDialog::Accepted) || !fileSaveOptionsDialog) 1971 { 1972 delete fileSaveOptionsDialog; 1973 1974 return false; 1975 } 1976 1977 // Write settings to config 1978 1979 options->applySettings(); 1980 1981 delete fileSaveOptionsDialog; 1982 1983 // Options is now also deleted 1984 } 1985 else 1986 { 1987 delete options; 1988 } 1989 1990 // Read settings from config to local container 1991 1992 applyIOSettings(); 1993 1994 // Select the format to save the image with 1995 1996 m_savingContext.format = selectValidSavingFormat(newURL); 1997 1998 if (m_savingContext.format.isNull()) 1999 { 2000 QMessageBox::critical(this, qApp->applicationName(), 2001 i18nc("@info", "Unable to determine the format to save the target image with.")); 2002 return false; 2003 } 2004 2005 if (!newURL.isValid()) 2006 { 2007 QMessageBox::critical(this, qApp->applicationName(), 2008 i18nc("@info", "Cannot Save: Found file path \"%1\" is invalid.", newURL.toDisplayString())); 2009 qCWarning(DIGIKAM_GENERAL_LOG) << "target URL is not valid !"; 2010 return false; 2011 } 2012 2013 group.writeEntry(optionLastExtension, m_savingContext.format); 2014 config->sync(); 2015 2016 return true; 2017 } 2018 2019 QString EditorWindow::selectValidSavingFormat(const QUrl& targetUrl) 2020 { 2021 qCDebug(DIGIKAM_GENERAL_LOG) << "Trying to find a saving format from targetUrl = " << targetUrl; 2022 2023 // Build a list of valid types 2024 2025 QString all; 2026 supportedImageMimeTypes(QIODevice::WriteOnly, all); 2027 qCDebug(DIGIKAM_GENERAL_LOG) << "Qt Offered types: " << all; 2028 2029 QStringList validTypes = all.split(QLatin1String("*."), QT_SKIP_EMPTY_PARTS); 2030 validTypes.replaceInStrings(QLatin1String(" "), QString()); 2031 2032 qCDebug(DIGIKAM_GENERAL_LOG) << "Writable formats: " << validTypes; 2033 2034 // Determine the format to use the format provided in the filename 2035 2036 QString suffix; 2037 2038 if (targetUrl.isLocalFile()) 2039 { 2040 // For local files QFileInfo can be used 2041 2042 QFileInfo fi(targetUrl.toLocalFile()); 2043 suffix = fi.suffix(); 2044 qCDebug(DIGIKAM_GENERAL_LOG) << "Possible format from local file: " << suffix; 2045 } 2046 else 2047 { 2048 // For remote files string manipulation is needed unfortunately 2049 2050 QString fileName = targetUrl.fileName(); 2051 const int periodLocation = fileName.lastIndexOf(QLatin1Char('.')); 2052 2053 if (periodLocation >= 0) 2054 { 2055 suffix = fileName.right(fileName.size() - periodLocation - 1); 2056 } 2057 2058 qCDebug(DIGIKAM_GENERAL_LOG) << "Possible format from remote file: " << suffix; 2059 } 2060 2061 if (!suffix.isEmpty() && validTypes.contains(suffix, Qt::CaseInsensitive)) 2062 { 2063 qCDebug(DIGIKAM_GENERAL_LOG) << "Using format from target url " << suffix; 2064 return suffix; 2065 } 2066 2067 // Another way to determine the format is to use the original file 2068 { 2069 QString originalFormat = QString::fromUtf8(QImageReader::imageFormat(m_savingContext.srcURL.toLocalFile())); 2070 2071 if (validTypes.contains(originalFormat, Qt::CaseInsensitive)) 2072 { 2073 qCDebug(DIGIKAM_GENERAL_LOG) << "Using format from original file: " << originalFormat; 2074 return originalFormat; 2075 } 2076 } 2077 2078 qCDebug(DIGIKAM_GENERAL_LOG) << "No suitable format found"; 2079 2080 return QString(); 2081 } 2082 2083 bool EditorWindow::startingSaveAs(const QUrl& url) 2084 { 2085 qCDebug(DIGIKAM_GENERAL_LOG) << "startSavingAs called"; 2086 2087 if (m_savingContext.savingState != SavingContext::SavingStateNone) 2088 { 2089 return false; 2090 } 2091 2092 m_savingContext = SavingContext(); 2093 m_savingContext.srcURL = url; 2094 QUrl suggested = m_savingContext.srcURL; 2095 2096 // Run dialog ------------------------------------------------------------------- 2097 2098 QUrl newURL; 2099 2100 if (!showFileSaveDialog(suggested, newURL)) 2101 { 2102 return false; 2103 } 2104 2105 // If new and original URL are equal use save() ------------------------------ 2106 2107 QUrl currURL(m_savingContext.srcURL); 2108 currURL.setPath(QDir::cleanPath(currURL.path())); 2109 newURL.setPath(QDir::cleanPath(newURL.path())); 2110 2111 if (currURL.matches(newURL, QUrl::None)) 2112 { 2113 save(); 2114 return false; 2115 } 2116 2117 // Check for overwrite ---------------------------------------------------------- 2118 2119 QFileInfo fi(newURL.toLocalFile()); 2120 m_savingContext.destinationExisted = fi.exists(); 2121 2122 if (m_savingContext.destinationExisted) 2123 { 2124 if (!checkOverwrite(newURL)) 2125 { 2126 return false; 2127 } 2128 2129 // There will be two message boxes if the file is not writable. 2130 // This may be controversial, and it may be changed, but it was a deliberate decision. 2131 2132 if (!checkPermissions(newURL)) 2133 { 2134 return false; 2135 } 2136 } 2137 2138 // Now do the actual saving ----------------------------------------------------- 2139 2140 setupTempSaveFile(newURL); 2141 2142 m_savingContext.destinationURL = newURL; 2143 m_savingContext.originalFormat = m_canvas->currentImageFileFormat(); 2144 m_savingContext.savingState = SavingContext::SavingStateSaveAs; 2145 m_savingContext.executedOperation = SavingContext::SavingStateNone; 2146 m_savingContext.abortingSaving = false; 2147 2148 // In any case, destructive (Save as) or non (Export), mark as New Version 2149 2150 m_canvas->interface()->setHistoryIsBranch(true); 2151 2152 m_canvas->interface()->saveAs(m_savingContext.saveTempFileName, m_IOFileSettings, 2153 m_setExifOrientationTag && m_canvas->exifRotated(), 2154 m_savingContext.format.toLower(), 2155 m_savingContext.destinationURL.toLocalFile()); 2156 2157 return true; 2158 } 2159 2160 bool EditorWindow::startingSaveCurrentVersion(const QUrl& url) 2161 { 2162 return startingSaveVersion(url, false, false, QString()); 2163 } 2164 2165 bool EditorWindow::startingSaveNewVersion(const QUrl& url) 2166 { 2167 return startingSaveVersion(url, true, false, QString()); 2168 } 2169 2170 bool EditorWindow::startingSaveNewVersionAs(const QUrl& url) 2171 { 2172 return startingSaveVersion(url, true, true, QString()); 2173 } 2174 2175 bool EditorWindow::startingSaveNewVersionInFormat(const QUrl& url, const QString& format) 2176 { 2177 return startingSaveVersion(url, true, false, format); 2178 } 2179 2180 VersionFileOperation EditorWindow::saveVersionFileOperation(const QUrl& url, bool fork) 2181 { 2182 DImageHistory resolvedHistory = m_canvas->interface()->getResolvedInitialHistory(); 2183 DImageHistory history = m_canvas->interface()->getItemHistory(); 2184 2185 VersionFileInfo currentName(url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile(), 2186 url.fileName(), m_canvas->currentImageFileFormat()); 2187 2188 return versionManager()->operation(fork ? VersionManager::NewVersionName : VersionManager::CurrentVersionName, 2189 currentName, resolvedHistory, history); 2190 } 2191 2192 VersionFileOperation EditorWindow::saveAsVersionFileOperation(const QUrl& url, const QUrl& saveUrl, const QString& format) 2193 { 2194 DImageHistory resolvedHistory = m_canvas->interface()->getResolvedInitialHistory(); 2195 DImageHistory history = m_canvas->interface()->getItemHistory(); 2196 2197 VersionFileInfo currentName(url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile(), 2198 url.fileName(), m_canvas->currentImageFileFormat()); 2199 2200 VersionFileInfo saveLocation(saveUrl.adjusted(QUrl::RemoveFilename).toLocalFile(), 2201 saveUrl.fileName(), format); 2202 2203 return versionManager()->operationNewVersionAs(currentName, saveLocation, resolvedHistory, history); 2204 } 2205 2206 VersionFileOperation EditorWindow::saveInFormatVersionFileOperation(const QUrl& url, const QString& format) 2207 { 2208 DImageHistory resolvedHistory = m_canvas->interface()->getResolvedInitialHistory(); 2209 DImageHistory history = m_canvas->interface()->getItemHistory(); 2210 2211 VersionFileInfo currentName(url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile(), 2212 url.fileName(), m_canvas->currentImageFileFormat()); 2213 2214 return versionManager()->operationNewVersionInFormat(currentName, format, resolvedHistory, history); 2215 } 2216 2217 bool EditorWindow::startingSaveVersion(const QUrl& url, bool fork, bool saveAs, const QString& format) 2218 { 2219 qCDebug(DIGIKAM_GENERAL_LOG) << "Saving image" << url << "non-destructive, new version:" 2220 << fork << ", saveAs:" << saveAs << "format:" << format; 2221 2222 if (m_savingContext.savingState != SavingContext::SavingStateNone) 2223 { 2224 return false; 2225 } 2226 2227 m_savingContext = SavingContext(); 2228 m_savingContext.versionFileOperation = saveVersionFileOperation(url, fork); 2229 m_canvas->interface()->setHistoryIsBranch(fork); 2230 2231 if (saveAs) 2232 { 2233 QUrl suggested = m_savingContext.versionFileOperation.saveFile.fileUrl(); 2234 QUrl selectedUrl; 2235 2236 if (!showFileSaveDialog(suggested, selectedUrl)) 2237 { 2238 return false; 2239 } 2240 2241 m_savingContext.versionFileOperation = saveAsVersionFileOperation(url, selectedUrl, m_savingContext.format); 2242 } 2243 else if (!format.isNull()) 2244 { 2245 m_savingContext.versionFileOperation = saveInFormatVersionFileOperation(url, format); 2246 } 2247 2248 const QUrl newURL = m_savingContext.versionFileOperation.saveFile.fileUrl(); 2249 qCDebug(DIGIKAM_GENERAL_LOG) << "Writing file to " << newURL; 2250 2251 if (!newURL.isValid()) 2252 { 2253 QMessageBox::critical(this, qApp->applicationName(), 2254 i18nc("@info", 2255 "Cannot save file \"%1\" to " 2256 "the suggested version file name \"%2\"", 2257 url.fileName(), 2258 newURL.fileName())); 2259 qCWarning(DIGIKAM_GENERAL_LOG) << "target URL is not valid !"; 2260 2261 return false; 2262 } 2263 2264 QFileInfo fi(newURL.toLocalFile()); 2265 m_savingContext.destinationExisted = fi.exists(); 2266 2267 // Check for overwrite (saveAs only) -------------------------------------------- 2268 2269 if (m_savingContext.destinationExisted) 2270 { 2271 // So, should we refuse to overwrite the original? 2272 // It's a frontal crash against non-destructive principles. 2273 // It is tempting to refuse, yet I think the user has to decide in the end 2274 /* 2275 QUrl currURL(m_savingContext.srcURL); 2276 currURL.cleanPath(); 2277 newURL.cleanPath(); 2278 if (currURL.equals(newURL)) 2279 { 2280 ... 2281 return false; 2282 } 2283 */ 2284 2285 // check for overwrite, unless the operation explicitly tells us to overwrite 2286 2287 if (!(m_savingContext.versionFileOperation.tasks & VersionFileOperation::Replace) && 2288 !checkOverwrite(newURL)) 2289 { 2290 return false; 2291 } 2292 2293 // There will be two message boxes if the file is not writable. 2294 // This may be controversial, and it may be changed, but it was a deliberate decision. 2295 2296 if (!checkPermissions(newURL)) 2297 { 2298 return false; 2299 } 2300 } 2301 2302 setupTempSaveFile(newURL); 2303 2304 m_savingContext.srcURL = url; 2305 m_savingContext.destinationURL = newURL; 2306 m_savingContext.originalFormat = m_canvas->currentImageFileFormat(); 2307 m_savingContext.format = m_savingContext.versionFileOperation.saveFile.format; 2308 m_savingContext.abortingSaving = false; 2309 m_savingContext.savingState = SavingContext::SavingStateVersion; 2310 m_savingContext.executedOperation = SavingContext::SavingStateNone; 2311 2312 m_canvas->interface()->saveAs(m_savingContext.saveTempFileName, m_IOFileSettings, 2313 m_setExifOrientationTag && m_canvas->exifRotated(), 2314 m_savingContext.format.toLower(), 2315 m_savingContext.versionFileOperation); 2316 2317 return true; 2318 } 2319 2320 bool EditorWindow::checkPermissions(const QUrl& url) 2321 { 2322 // TODO: Check that the permissions can actually be changed 2323 // if write permissions are not available. 2324 2325 QFileInfo fi(url.toLocalFile()); 2326 2327 if (fi.exists() && !fi.isWritable()) 2328 { 2329 int result = QMessageBox::warning(this, i18nc("@title:window", "Overwrite File?"), 2330 i18nc("@info", "You do not have write permissions " 2331 "for the file named \"%1\". " 2332 "Are you sure you want " 2333 "to overwrite it?", 2334 url.fileName()), 2335 QMessageBox::Save | QMessageBox::Cancel); 2336 2337 if (result != QMessageBox::Save) 2338 { 2339 return false; 2340 } 2341 } 2342 2343 return true; 2344 } 2345 2346 bool EditorWindow::checkOverwrite(const QUrl& url) 2347 { 2348 int result = QMessageBox::warning(this, i18nc("@title:window", "Overwrite File?"), 2349 i18nc("@info", "A file named \"%1\" already " 2350 "exists. Are you sure you want " 2351 "to overwrite it?", 2352 url.fileName()), 2353 QMessageBox::Save | QMessageBox::Cancel); 2354 2355 return (result == QMessageBox::Save); 2356 } 2357 2358 bool EditorWindow::moveLocalFile(const QString& org, const QString& dst) 2359 { 2360 QString sidecarOrg = DMetadata::sidecarPath(org); 2361 QString source = m_savingContext.srcURL.toLocalFile(); 2362 2363 if (QFileInfo::exists(sidecarOrg)) 2364 { 2365 QString sidecarDst = DMetadata::sidecarPath(dst); 2366 2367 if (!DFileOperations::localFileRename(source, sidecarOrg, sidecarDst)) 2368 { 2369 qCDebug(DIGIKAM_GENERAL_LOG) << "Failed to move sidecar file"; 2370 } 2371 } 2372 2373 if (!DFileOperations::localFileRename(source, org, dst)) 2374 { 2375 QMessageBox::critical(this, i18nc("@title:window", "Error Saving File"), 2376 i18nc("@info", "Failed to overwrite original file")); 2377 return false; 2378 } 2379 2380 return true; 2381 } 2382 2383 void EditorWindow::moveFile() 2384 { 2385 // Move local file. 2386 2387 if (m_savingContext.executedOperation == SavingContext::SavingStateVersion) 2388 { 2389 // Check if we need to move the current file to an intermediate name 2390 2391 if (m_savingContext.versionFileOperation.tasks & VersionFileOperation::MoveToIntermediate) 2392 { 2393 //qCDebug(DIGIKAM_GENERAL_LOG) << "MoveToIntermediate: Moving " << m_savingContext.srcURL.toLocalFile() << "to" 2394 // << m_savingContext.versionFileOperation.intermediateForLoadedFile.filePath() 2395 2396 moveLocalFile(m_savingContext.srcURL.toLocalFile(), 2397 m_savingContext.versionFileOperation.intermediateForLoadedFile.filePath()); 2398 2399 LoadingCacheInterface::fileChanged(m_savingContext.destinationURL.toLocalFile()); 2400 ThumbnailLoadThread::deleteThumbnail(m_savingContext.destinationURL.toLocalFile()); 2401 } 2402 } 2403 2404 bool moveSuccessful = moveLocalFile(m_savingContext.saveTempFileName, 2405 m_savingContext.destinationURL.toLocalFile()); 2406 2407 if (m_savingContext.executedOperation == SavingContext::SavingStateVersion) 2408 { 2409 if (moveSuccessful && 2410 m_savingContext.versionFileOperation.tasks & VersionFileOperation::SaveAndDelete) 2411 { 2412 QFile file(m_savingContext.versionFileOperation.loadedFile.filePath()); 2413 file.remove(); 2414 } 2415 } 2416 2417 movingSaveFileFinished(moveSuccessful); 2418 } 2419 2420 void EditorWindow::slotDiscardChanges() 2421 { 2422 m_canvas->interface()->rollbackToOrigin(); 2423 } 2424 2425 void EditorWindow::slotOpenOriginal() 2426 { 2427 // No-op in this base class 2428 } 2429 2430 void EditorWindow::slotColorManagementOptionsChanged() 2431 { 2432 applyColorManagementSettings(); 2433 applyIOSettings(); 2434 } 2435 2436 void EditorWindow::slotToggleColorManagedView() 2437 { 2438 if (!IccSettings::instance()->isEnabled()) 2439 { 2440 return; 2441 } 2442 2443 bool cmv = !IccSettings::instance()->settings().useManagedView; 2444 IccSettings::instance()->setUseManagedView(cmv); 2445 } 2446 2447 void EditorWindow::setColorManagedViewIndicatorToolTip(bool available, bool cmv) 2448 { 2449 QString tooltip; 2450 2451 if (available) 2452 { 2453 if (cmv) 2454 { 2455 tooltip = i18nc("@info", "Color-Managed View is enabled."); 2456 } 2457 else 2458 { 2459 tooltip = i18nc("@info", "Color-Managed View is disabled."); 2460 } 2461 } 2462 else 2463 { 2464 tooltip = i18nc("@info", "Color Management is not configured, so the Color-Managed View is not available."); 2465 } 2466 2467 d->cmViewIndicator->setToolTip(tooltip); 2468 } 2469 2470 void EditorWindow::slotSoftProofingOptions() 2471 { 2472 // Adjusts global settings 2473 2474 QPointer<SoftProofDialog> dlg = new SoftProofDialog(this); 2475 dlg->exec(); 2476 2477 d->viewSoftProofAction->setChecked(dlg->shallEnableSoftProofView()); 2478 slotUpdateSoftProofingState(); 2479 delete dlg; 2480 } 2481 2482 void EditorWindow::slotUpdateSoftProofingState() 2483 { 2484 bool on = d->viewSoftProofAction->isChecked(); 2485 m_canvas->setSoftProofingEnabled(on); 2486 d->toolIface->updateICCSettings(); 2487 } 2488 2489 void EditorWindow::slotSetUnderExposureIndicator(bool on) 2490 { 2491 d->exposureSettings->underExposureIndicator = on; 2492 d->toolIface->updateExposureSettings(); 2493 d->viewUnderExpoAction->setChecked(on); 2494 setUnderExposureToolTip(on); 2495 } 2496 2497 void EditorWindow::setUnderExposureToolTip(bool on) 2498 { 2499 d->underExposureIndicator->setToolTip( 2500 on ? i18nc("@info", "Under-Exposure indicator is enabled") 2501 : i18nc("@info", "Under-Exposure indicator is disabled")); 2502 } 2503 2504 void EditorWindow::slotSetOverExposureIndicator(bool on) 2505 { 2506 d->exposureSettings->overExposureIndicator = on; 2507 d->toolIface->updateExposureSettings(); 2508 d->viewOverExpoAction->setChecked(on); 2509 setOverExposureToolTip(on); 2510 } 2511 2512 void EditorWindow::setOverExposureToolTip(bool on) 2513 { 2514 d->overExposureIndicator->setToolTip( 2515 on ? i18nc("@info", "Over-Exposure indicator is enabled") 2516 : i18nc("@info", "Over-Exposure indicator is disabled")); 2517 } 2518 2519 void EditorWindow::slotSelectionChanged(const QRect& sel) 2520 { 2521 slotSelectionSetText(sel); 2522 Q_EMIT signalSelectionChanged(sel); 2523 } 2524 2525 void EditorWindow::slotSelectionSetText(const QRect& sel) 2526 { 2527 setToolInfoMessage(QString::fromLatin1("(%1, %2) (%3 x %4)") 2528 .arg(sel.x()) 2529 .arg(sel.y()) 2530 .arg(sel.width()) 2531 .arg(sel.height())); 2532 } 2533 2534 void EditorWindow::slotComponentsInfo() 2535 { 2536 LibsInfoDlg* const dlg = new LibsInfoDlg(this); 2537 dlg->show(); 2538 } 2539 2540 void EditorWindow::setToolStartProgress(const QString& toolName) 2541 { 2542 m_animLogo->start(); 2543 m_nameLabel->setProgressValue(0); 2544 m_nameLabel->setProgressBarMode(StatusProgressBar::CancelProgressBarMode, 2545 QString::fromUtf8("%1:").arg(toolName)); 2546 } 2547 2548 void EditorWindow::setToolProgress(int progress) 2549 { 2550 m_nameLabel->setProgressValue(progress); 2551 } 2552 2553 void EditorWindow::setToolStopProgress() 2554 { 2555 m_animLogo->stop(); 2556 m_nameLabel->setProgressValue(0); 2557 m_nameLabel->setProgressBarMode(StatusProgressBar::TextMode); 2558 slotUpdateItemInfo(); 2559 } 2560 2561 void EditorWindow::slotCloseTool() 2562 { 2563 if (d->toolIface) 2564 { 2565 d->toolIface->slotCloseTool(); 2566 } 2567 } 2568 2569 void EditorWindow::slotApplyTool() 2570 { 2571 if (d->toolIface) 2572 { 2573 d->toolIface->slotApplyTool(); 2574 } 2575 } 2576 2577 void EditorWindow::setPreviewModeMask(int mask) 2578 { 2579 d->previewToolBar->setPreviewModeMask(mask); 2580 } 2581 2582 PreviewToolBar::PreviewMode EditorWindow::previewMode() const 2583 { 2584 return d->previewToolBar->previewMode(); 2585 } 2586 2587 void EditorWindow::setToolInfoMessage(const QString& txt) 2588 { 2589 d->infoLabel->setAdjustedText(txt); 2590 } 2591 2592 VersionManager* EditorWindow::versionManager() const 2593 { 2594 return &d->defaultVersionManager; 2595 } 2596 2597 void EditorWindow::setupSelectToolsAction() 2598 { 2599 // Create action model 2600 2601 ActionItemModel* const actionModel = new ActionItemModel(this); 2602 actionModel->setMode((ActionItemModel::MenuCategoryMode)(ActionItemModel::ToplevelMenuCategory | 2603 ActionItemModel::SortCategoriesByInsertionOrder)); 2604 2605 // Builtin actions 2606 2607 QString transformCategory = i18nc("@title Image Transform", "Transform"); 2608 2609 // See bug #447687 2610 actionModel->addAction(d->rotateLeftAction, transformCategory); 2611 actionModel->addAction(d->rotateRightAction, transformCategory); 2612 actionModel->addAction(d->flipHorizAction, transformCategory); 2613 actionModel->addAction(d->flipVertAction, transformCategory); 2614 actionModel->addAction(d->cropAction, transformCategory); 2615 2616 Q_FOREACH (DPluginAction* const ac, DPluginLoader::instance()->pluginsActions(DPluginAction::EditorTransform, this)) 2617 { 2618 actionModel->addAction(ac, transformCategory); 2619 } 2620 2621 QString decorateCategory = i18nc("@title Image Decorate", "Decorate"); 2622 2623 Q_FOREACH (DPluginAction* const ac, DPluginLoader::instance()->pluginsActions(DPluginAction::EditorDecorate, this)) 2624 { 2625 actionModel->addAction(ac, decorateCategory); 2626 } 2627 2628 QString effectsCategory = i18nc("@title Image Effect", "Effects"); 2629 2630 Q_FOREACH (DPluginAction* const ac, DPluginLoader::instance()->pluginsActions(DPluginAction::EditorFilters, this)) 2631 { 2632 actionModel->addAction(ac, effectsCategory); 2633 } 2634 2635 QString colorsCategory = i18nc("@title Image Colors", "Colors"); 2636 2637 Q_FOREACH (DPluginAction* const ac, DPluginLoader::instance()->pluginsActions(DPluginAction::EditorColors, this)) 2638 { 2639 actionModel->addAction(ac, effectsCategory); 2640 } 2641 2642 QString enhanceCategory = i18nc("@title Image Enhance", "Enhance"); 2643 2644 Q_FOREACH (DPluginAction* const ac, DPluginLoader::instance()->pluginsActions(DPluginAction::EditorEnhance, this)) 2645 { 2646 actionModel->addAction(ac, effectsCategory); 2647 } 2648 2649 QString postCategory = i18nc("@title Post Processing Tools", "Post-Processing"); 2650 2651 Q_FOREACH (DPluginAction* const ac, DPluginLoader::instance()->pluginsActions(DPluginAction::GenericTool, this)) 2652 { 2653 actionModel->addAction(ac, postCategory); 2654 } 2655 2656 Q_FOREACH (DPluginAction* const ac, DPluginLoader::instance()->pluginsActions(DPluginAction::EditorFile, this)) 2657 { 2658 actionModel->addAction(ac, postCategory); 2659 } 2660 2661 Q_FOREACH (DPluginAction* const ac, DPluginLoader::instance()->pluginsActions(DPluginAction::GenericMetadata, this)) 2662 { 2663 actionModel->addAction(ac, postCategory); 2664 } 2665 2666 QString exportCategory = i18nc("@title Export Tools", "Export"); 2667 2668 Q_FOREACH (DPluginAction* const ac, DPluginLoader::instance()->pluginsActions(DPluginAction::GenericExport, this)) 2669 { 2670 actionModel->addAction(ac, exportCategory); 2671 } 2672 2673 QString importCategory = i18nc("@title Import Tools", "Import"); 2674 2675 Q_FOREACH (DPluginAction* const ac, DPluginLoader::instance()->pluginsActions(DPluginAction::GenericImport, this)) 2676 { 2677 actionModel->addAction(ac, importCategory); 2678 } 2679 2680 // setup categorized view 2681 2682 DCategorizedSortFilterProxyModel* const filterModel = actionModel->createFilterModel(); 2683 2684 ActionCategorizedView* const selectToolsActionView = new ActionCategorizedView; 2685 selectToolsActionView->setIconSize(QSize(22, 22)); 2686 selectToolsActionView->setModel(filterModel); 2687 selectToolsActionView->setupIconMode(); 2688 selectToolsActionView->adjustGridSize(); 2689 2690 connect(selectToolsActionView, SIGNAL(clicked(QModelIndex)), 2691 actionModel, SLOT(trigger(QModelIndex))); 2692 2693 EditorToolIface::editorToolIface()->setToolsIconView(selectToolsActionView); 2694 } 2695 2696 void EditorWindow::slotThemeChanged() 2697 { 2698 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 2699 KConfigGroup group = config->group(configGroupName()); 2700 2701 if (!group.readEntry(d->configUseThemeBackgroundColorEntry, true)) 2702 { 2703 m_bgColor = group.readEntry(d->configBackgroundColorEntry, QColor(Qt::black)); 2704 } 2705 else 2706 { 2707 m_bgColor = palette().color(QPalette::Base); 2708 } 2709 2710 m_canvas->setBackgroundBrush(QBrush(m_bgColor)); 2711 d->toolIface->themeChanged(); 2712 } 2713 2714 void EditorWindow::addAction2ContextMenu(const QString& actionName, bool addDisabled) 2715 { 2716 if (!m_contextMenu) 2717 { 2718 return; 2719 } 2720 2721 QAction* const action = actionCollection()->action(actionName); 2722 2723 if (action && (action->isEnabled() || addDisabled)) 2724 { 2725 m_contextMenu->addAction(action); 2726 } 2727 } 2728 2729 void EditorWindow::showSideBars(bool visible) 2730 { 2731 if (visible) 2732 { 2733 rightSideBar()->restore(QList<QWidget*>() << thumbBar(), d->fullscreenSizeBackup); 2734 } 2735 else 2736 { 2737 // See bug #166472, a simple backup()/restore() will hide non-sidebar splitter child widgets 2738 // in horizontal mode thumbbar wont be member of the splitter, it is just ignored then 2739 2740 rightSideBar()->backup(QList<QWidget*>() << thumbBar(), &d->fullscreenSizeBackup); 2741 } 2742 } 2743 2744 void EditorWindow::slotToggleRightSideBar() 2745 { 2746 rightSideBar()->isExpanded() ? rightSideBar()->shrink() 2747 : rightSideBar()->expand(); 2748 } 2749 2750 void EditorWindow::slotPreviousRightSideBarTab() 2751 { 2752 rightSideBar()->activePreviousTab(); 2753 } 2754 2755 void EditorWindow::slotNextRightSideBarTab() 2756 { 2757 rightSideBar()->activeNextTab(); 2758 } 2759 2760 void EditorWindow::showThumbBar(bool visible) 2761 { 2762 visible ? thumbBar()->restoreVisibility() 2763 : thumbBar()->hide(); 2764 } 2765 2766 bool EditorWindow::thumbbarVisibility() const 2767 { 2768 return thumbBar()->isVisible(); 2769 } 2770 2771 void EditorWindow::customizedFullScreenMode(bool set) 2772 { 2773 set ? m_canvas->setBackgroundBrush(QBrush(Qt::black)) 2774 : m_canvas->setBackgroundBrush(QBrush(m_bgColor)); 2775 2776 showStatusBarAction()->setEnabled(!set); 2777 toolBarMenuAction()->setEnabled(!set); 2778 showMenuBarAction()->setEnabled(!set); 2779 m_showBarAction->setEnabled(!set); 2780 } 2781 2782 void EditorWindow::addServicesMenuForUrl(const QUrl& url) 2783 { 2784 KService::List offers = DServiceMenu::servicesForOpenWith(QList<QUrl>() << url); 2785 2786 qCDebug(DIGIKAM_GENERAL_LOG) << offers.count() << " services found to open " << url; 2787 2788 if (m_servicesMenu) 2789 { 2790 delete m_servicesMenu; 2791 m_servicesMenu = nullptr; 2792 } 2793 2794 if (m_serviceAction) 2795 { 2796 delete m_serviceAction; 2797 m_serviceAction = nullptr; 2798 } 2799 2800 if (!offers.isEmpty()) 2801 { 2802 m_servicesMenu = new QMenu(this); 2803 2804 QAction* const serviceAction = m_servicesMenu->menuAction(); 2805 serviceAction->setText(i18nc("@action", "Open With")); 2806 2807 Q_FOREACH (const KService::Ptr& service, offers) 2808 { 2809 QString name = service->name().replace(QLatin1Char('&'), QLatin1String("&&")); 2810 QAction* const action = m_servicesMenu->addAction(name); 2811 action->setIcon(QIcon::fromTheme(service->icon())); 2812 action->setData(service->name()); 2813 d->servicesMap[name] = service; 2814 } 2815 2816 #ifdef HAVE_KIO 2817 2818 m_servicesMenu->addSeparator(); 2819 m_servicesMenu->addAction(i18nc("@action: open with other application", "Other...")); 2820 2821 m_contextMenu->addAction(serviceAction); 2822 2823 connect(m_servicesMenu, SIGNAL(triggered(QAction*)), 2824 this, SLOT(slotOpenWith(QAction*))); 2825 } 2826 else 2827 { 2828 m_serviceAction = new QAction(i18nc("@action: open with desktop application", "Open With..."), this); 2829 m_contextMenu->addAction(m_serviceAction); 2830 2831 connect(m_servicesMenu, SIGNAL(triggered()), 2832 this, SLOT(slotOpenWith())); 2833 2834 #endif // HAVE_KIO 2835 2836 } 2837 } 2838 2839 void EditorWindow::openWith(const QUrl& url, QAction* action) 2840 { 2841 KService::Ptr service; 2842 QString name = action ? action->data().toString() : QString(); 2843 2844 #ifdef HAVE_KIO 2845 2846 if (name.isEmpty()) 2847 { 2848 QPointer<KOpenWithDialog> dlg = new KOpenWithDialog(QList<QUrl>() << url); 2849 2850 if (dlg->exec() != KOpenWithDialog::Accepted) 2851 { 2852 delete dlg; 2853 return; 2854 } 2855 2856 service = dlg->service(); 2857 2858 if (!service) 2859 { 2860 // User entered a custom command 2861 2862 if (!dlg->text().isEmpty()) 2863 { 2864 DServiceMenu::runFiles(dlg->text(), QList<QUrl>() << url); 2865 } 2866 2867 delete dlg; 2868 return; 2869 } 2870 2871 delete dlg; 2872 } 2873 else 2874 2875 #endif // HAVE_KIO 2876 2877 { 2878 service = d->servicesMap[name]; 2879 } 2880 2881 DServiceMenu::runFiles(service, QList<QUrl>() << url); 2882 } 2883 2884 void EditorWindow::loadTool(EditorTool* const tool) 2885 { 2886 EditorToolIface::editorToolIface()->loadTool(tool); 2887 2888 connect(tool, SIGNAL(okClicked()), 2889 this, SLOT(slotToolDone())); 2890 2891 connect(tool, SIGNAL(cancelClicked()), 2892 this, SLOT(slotToolDone())); 2893 } 2894 2895 void EditorWindow::slotToolDone() 2896 { 2897 EditorToolIface::editorToolIface()->unLoadTool(); 2898 } 2899 2900 void EditorWindow::slotRotateLeftIntoQue() 2901 { 2902 m_transformQue.append(TransformType::RotateLeft); 2903 } 2904 2905 void EditorWindow::slotRotateRightIntoQue() 2906 { 2907 m_transformQue.append(TransformType::RotateRight); 2908 } 2909 2910 void EditorWindow::slotFlipHIntoQue() 2911 { 2912 m_transformQue.append(TransformType::FlipHorizontal); 2913 } 2914 2915 void EditorWindow::slotFlipVIntoQue() 2916 { 2917 m_transformQue.append(TransformType::FlipVertical); 2918 } 2919 2920 void EditorWindow::registerExtraPluginsActions(QString& dom) 2921 { 2922 DPluginLoader* const dpl = DPluginLoader::instance(); 2923 dpl->registerEditorPlugins(this); 2924 dpl->registerRawImportPlugins(this); 2925 2926 QList<DPluginAction*> actions = dpl->pluginsActions(DPluginAction::Editor, this); 2927 2928 Q_FOREACH (DPluginAction* const ac, actions) 2929 { 2930 actionCollection()->addActions(QList<QAction*>() << ac); 2931 actionCollection()->setDefaultShortcuts(ac, ac->shortcuts()); 2932 } 2933 2934 dom.replace(QLatin1String("<!-- _DPLUGINS_EDITOR_FILES_ACTIONS_ -->"), dpl->pluginXmlSections(DPluginAction::EditorFile, this)); 2935 dom.replace(QLatin1String("<!-- _DPLUGINS_EDITOR_COLORS_ACTIONS_ -->"), dpl->pluginXmlSections(DPluginAction::EditorColors, this)); 2936 dom.replace(QLatin1String("<!-- _DPLUGINS_EDITOR_ENHANCE_ACTIONS_ -->"), dpl->pluginXmlSections(DPluginAction::EditorEnhance, this)); 2937 2938 QString transformActions; 2939 transformActions.append(QLatin1String("<Action name=\"editorwindow_transform_rotateleft\" />\n")); 2940 transformActions.append(QLatin1String("<Action name=\"editorwindow_transform_rotateright\" />\n")); 2941 transformActions.append(QLatin1String("<Action name=\"editorwindow_transform_fliphoriz\" />\n")); 2942 transformActions.append(QLatin1String("<Action name=\"editorwindow_transform_flipvert\" />\n")); 2943 transformActions.append(QLatin1String("<Action name=\"editorwindow_transform_crop\" />\n")); 2944 transformActions.append(QLatin1String("<Separator/>\n")); 2945 transformActions.append(dpl->pluginXmlSections(DPluginAction::EditorTransform, this)); 2946 2947 dom.replace(QLatin1String("<!-- _DPLUGINS_EDITOR_TRANSFORM_ACTIONS_ -->"), transformActions); 2948 dom.replace(QLatin1String("<!-- _DPLUGINS_EDITOR_DECORATE_ACTIONS_ -->"), dpl->pluginXmlSections(DPluginAction::EditorDecorate, this)); 2949 dom.replace(QLatin1String("<!-- _DPLUGINS_EDITOR_FILTERS_ACTIONS_ -->"), dpl->pluginXmlSections(DPluginAction::EditorFilters, this)); 2950 } 2951 2952 } // namespace Digikam 2953 2954 #include "moc_editorwindow.cpp"