File indexing completed on 2025-01-19 03:59:16

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2004-11-22
0007  * Description : digiKam image editor - Internal setup
0008  *
0009  * SPDX-FileCopyrightText: 2004-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "imagewindow_p.h"
0016 
0017 namespace Digikam
0018 {
0019 
0020 void ImageWindow::setupActions()
0021 {
0022     setupStandardActions();
0023 
0024     KActionCollection* const ac = actionCollection();
0025 
0026     d->toMainWindowAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-icons")),
0027                                         i18nc("@action Finish editing, close editor, back to main window", "Close Editor"), this);
0028     connect(d->toMainWindowAction, SIGNAL(triggered()), this, SLOT(slotToMainWindow()));
0029     ac->addAction(QLatin1String("imageview_tomainwindow"), d->toMainWindowAction);
0030 
0031 
0032     // -- Special Delete actions ---------------------------------------------------------------
0033 
0034     // Pop up dialog to ask user whether to permanently delete
0035 
0036     d->fileDeletePermanentlyAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete File Permanently"), this);
0037     connect(d->fileDeletePermanentlyAction, SIGNAL(triggered()),
0038             this, SLOT(slotDeleteCurrentItemPermanently()));
0039     ac->addAction(QLatin1String("image_delete_permanently"), d->fileDeletePermanentlyAction);
0040     ac->setDefaultShortcut(d->fileDeletePermanentlyAction, Qt::SHIFT | Qt::Key_Delete);
0041 
0042     // These two actions are hidden, no menu entry, no toolbar entry, no shortcut.
0043     // Power users may add them.
0044 
0045     d->fileDeletePermanentlyDirectlyAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")),
0046                                                          i18n("Delete Permanently without Confirmation"), this);
0047     connect(d->fileDeletePermanentlyDirectlyAction, SIGNAL(triggered()),
0048             this, SLOT(slotDeleteCurrentItemPermanentlyDirectly()));
0049     ac->addAction(QLatin1String("image_delete_permanently_directly"),
0050                                   d->fileDeletePermanentlyDirectlyAction);
0051 
0052     d->fileTrashDirectlyAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")),
0053                                              i18n("Move to Trash without Confirmation"), this);
0054     connect(d->fileTrashDirectlyAction, SIGNAL(triggered()),
0055             this, SLOT(slotTrashCurrentItemDirectly()));
0056     ac->addAction(QLatin1String("image_trash_directly"), d->fileTrashDirectlyAction);
0057 
0058     // ---------------------------------------------------------------------------------
0059 
0060     createHelpActions(QLatin1String("image_editor"));
0061 
0062     // Labels shortcuts must be registered here to be saved in XML GUI files if user customize it.
0063 
0064     TagsActionMngr::defaultManager()->registerLabelsActions(ac);
0065 
0066     QAction* const editTitles = new QAction(i18n("Edit Titles"), this);
0067     ac->addAction(QLatin1String("edit_titles"), editTitles);
0068     ac->setDefaultShortcut(editTitles, Qt::ALT | Qt::SHIFT | Qt::Key_T);
0069     connect(editTitles, SIGNAL(triggered()), this, SLOT(slotRightSideBarActivateTitles()));
0070 
0071     QAction* const editComments = new QAction(i18n("Edit Comments"), this);
0072     ac->addAction(QLatin1String("edit_comments"), editComments);
0073     ac->setDefaultShortcut(editComments, Qt::ALT | Qt::SHIFT | Qt::Key_C);
0074     connect(editComments, SIGNAL(triggered()), this, SLOT(slotRightSideBarActivateComments()));
0075 
0076     QAction* const assignedTags = new QAction(i18n("Show Assigned Tags"), this);
0077     ac->addAction(QLatin1String("assigned_tags"), assignedTags);
0078     ac->setDefaultShortcut(assignedTags, Qt::ALT | Qt::SHIFT | Qt::Key_A);
0079     connect(assignedTags, SIGNAL(triggered()), this, SLOT(slotRightSideBarActivateAssignedTags()));
0080 }
0081 
0082 void ImageWindow::setupConnections()
0083 {
0084     setupStandardConnections();
0085 
0086     connect(this, SIGNAL(loadCurrentLater()),
0087             this, SLOT(slotLoadCurrent()), Qt::QueuedConnection);
0088 
0089     // To toggle properly keyboards shortcuts from comments & tags side bar tab.
0090 
0091     connect(d->rightSideBar, SIGNAL(signalNextItem()),
0092             this, SLOT(slotForward()));
0093 
0094     connect(d->rightSideBar, SIGNAL(signalPrevItem()),
0095             this, SLOT(slotBackward()));
0096 
0097     connect(d->rightSideBar->getFiltersHistoryTab(), SIGNAL(actionTriggered(ItemInfo)),
0098             this, SLOT(openImage(ItemInfo)));
0099 
0100     connect(this, SIGNAL(signalSelectionChanged(QRect)),
0101             d->rightSideBar, SLOT(slotImageSelectionChanged(QRect)));
0102 
0103     connect(this, SIGNAL(signalNoCurrentItem()),
0104             d->rightSideBar, SLOT(slotNoCurrentItem()));
0105 
0106     ItemAttributesWatch* watch = ItemAttributesWatch::instance();
0107 
0108     connect(watch, SIGNAL(signalFileMetadataChanged(QUrl)),
0109             this, SLOT(slotFileMetadataChanged(QUrl)));
0110 
0111 /*
0112     connect(CoreDbAccess::databaseWatch(), SIGNAL(collectionImageChange(CollectionImageChangeset)),
0113             this, SLOT(slotCollectionImageChange(CollectionImageChangeset)),
0114             Qt::QueuedConnection);
0115 
0116     connect(d->imageFilterModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
0117             this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
0118 */
0119 
0120     connect(d->thumbBar, SIGNAL(currentChanged(ItemInfo)),
0121             this, SLOT(slotThumbBarImageSelected(ItemInfo)));
0122 
0123     connect(d->dragDropHandler, SIGNAL(itemInfosDropped(QList<ItemInfo>)),
0124             this, SLOT(slotDroppedOnThumbbar(QList<ItemInfo>)));
0125 
0126     connect(d->thumbBarDock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)),
0127             d->thumbBar, SLOT(slotDockLocationChanged(Qt::DockWidgetArea)));
0128 
0129     connect(d->imageInfoModel, SIGNAL(allRefreshingFinished()),
0130             this, SLOT(slotThumbBarModelReady()));
0131 
0132     connect(ApplicationSettings::instance(), SIGNAL(setupChanged()),
0133             this, SLOT(slotSetupChanged()));
0134 }
0135 
0136 void ImageWindow::setupUserArea()
0137 {
0138     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0139     KConfigGroup group        = config->group(configGroupName());
0140 
0141     QWidget* const widget   = new QWidget(this);
0142     QHBoxLayout* const hlay = new QHBoxLayout(widget);
0143     m_splitter              = new SidebarSplitter(widget);
0144 
0145     d->viewContainer        = new KMainWindow(widget, Qt::Widget);
0146     m_splitter->addWidget(d->viewContainer);
0147     m_stackView             = new EditorStackView(d->viewContainer);
0148     m_canvas                = new Canvas(m_stackView);
0149     d->viewContainer->setCentralWidget(m_stackView);
0150 
0151     m_splitter->setFrameStyle(QFrame::NoFrame);
0152     m_splitter->setFrameShape(QFrame::NoFrame);
0153     m_splitter->setFrameShadow(QFrame::Plain);
0154     m_splitter->setStretchFactor(m_splitter->indexOf(d->viewContainer), 10);      // set Canvas default size to max.
0155     m_splitter->setOpaqueResize(false);
0156 
0157     m_canvas->makeDefaultEditingCanvas();
0158     m_stackView->setCanvas(m_canvas);
0159     m_stackView->setViewMode(EditorStackView::CanvasMode);
0160 
0161     d->rightSideBar = new ItemPropertiesSideBarDB(widget, m_splitter, Qt::RightEdge, true);
0162     d->rightSideBar->setObjectName(QLatin1String("ImageEditor Right Sidebar"));
0163     d->rightSideBar->getFiltersHistoryTab()->addOpenImageAction();
0164 
0165     hlay->addWidget(m_splitter);
0166     hlay->addWidget(d->rightSideBar);
0167     hlay->setContentsMargins(QMargins());
0168     hlay->setSpacing(0);
0169 
0170     // Code to check for the now depreciated HorizontalThumbar directive. It
0171     // is found, it is honored and deleted. The state will from than on be saved
0172     // by d->viewContainers built-in mechanism.
0173 
0174     Qt::DockWidgetArea dockArea = Qt::LeftDockWidgetArea;
0175 
0176     if (group.hasKey(d->configHorizontalThumbbarEntry))
0177     {
0178         if (group.readEntry(d->configHorizontalThumbbarEntry, true))
0179         {
0180             // Horizontal thumbbar layout
0181 
0182             dockArea    = Qt::TopDockWidgetArea;
0183         }
0184 
0185         group.deleteEntry(d->configHorizontalThumbbarEntry);
0186     }
0187 
0188     d->imageInfoModel   = new ItemListModel(this);
0189 
0190     d->imageFilterModel = new ItemFilterModel(this);
0191     d->imageFilterModel->setSourceItemModel(d->imageInfoModel);
0192 
0193     d->imageInfoModel->setWatchFlags(d->imageFilterModel->suggestedWatchFlags());
0194     d->imageInfoModel->setThumbnailLoadThread(ThumbnailLoadThread::defaultIconViewThread());
0195 
0196     d->imageFilterModel->setCategorizationMode(ItemSortSettings::NoCategories);
0197     d->imageFilterModel->setStringTypeNatural(ApplicationSettings::instance()->isStringTypeNatural());
0198     d->imageFilterModel->setSortRole((ItemSortSettings::SortRole)ApplicationSettings::instance()->getImageSortOrder());
0199     d->imageFilterModel->setSortOrder((ItemSortSettings::SortOrder)ApplicationSettings::instance()->getImageSorting());
0200     d->imageFilterModel->setAllGroupsOpen(true);    // disable filtering out by group, see bug #283847
0201     d->imageFilterModel->sort(0);                   // an initial sorting is necessary
0202 
0203     d->dragDropHandler  = new ItemDragDropHandler(d->imageInfoModel);
0204     d->dragDropHandler->setReadOnlyDrop(true);
0205     d->imageInfoModel->setDragDropHandler(d->dragDropHandler);
0206 
0207     // The thumb bar is placed in a detachable/dockable widget.
0208 
0209     d->thumbBarDock     = new ThumbBarDock(d->viewContainer, Qt::Tool);
0210     d->thumbBarDock->setObjectName(QLatin1String("editor_thumbbar"));
0211 
0212     d->thumbBar         = new ItemThumbnailBar(d->thumbBarDock);
0213     d->thumbBar->setModels(d->imageInfoModel, d->imageFilterModel);
0214 
0215     d->thumbBarDock->setWidget(d->thumbBar);
0216     d->viewContainer->addDockWidget(dockArea, d->thumbBarDock);
0217     d->thumbBarDock->setFloating(false);
0218 /*
0219     d->thumbBar->slotDockLocationChanged(dockArea);
0220 */
0221     setCentralWidget(widget);
0222 }
0223 
0224 void ImageWindow::addServicesMenu()
0225 {
0226     addServicesMenuForUrl(d->currentUrl());
0227 }
0228 
0229 void ImageWindow::slotContextMenu()
0230 {
0231     if (m_contextMenu)
0232     {
0233         m_contextMenu->addSeparator();
0234         addServicesMenu();
0235         m_contextMenu->addSeparator();
0236 
0237         // Bulk assignment/removal of tags --------------------------
0238 
0239         QList<qlonglong> idList;
0240         idList << d->currentItemInfo.id();
0241 
0242         TagsPopupMenu* const assignTagsMenu = new TagsPopupMenu(idList, TagsPopupMenu::RECENTLYASSIGNED, this);
0243         TagsPopupMenu* const removeTagsMenu = new TagsPopupMenu(idList, TagsPopupMenu::REMOVE, this);
0244         assignTagsMenu->menuAction()->setText(i18n("Assign Tag"));
0245         removeTagsMenu->menuAction()->setText(i18n("Remove Tag"));
0246 
0247         m_contextMenu->addSeparator();
0248 
0249         m_contextMenu->addMenu(assignTagsMenu);
0250         m_contextMenu->addMenu(removeTagsMenu);
0251 
0252         connect(assignTagsMenu, SIGNAL(signalTagActivated(int)),
0253                 this, SLOT(slotAssignTag(int)));
0254 
0255         connect(removeTagsMenu, SIGNAL(signalTagActivated(int)),
0256                 this, SLOT(slotRemoveTag(int)));
0257 
0258         connect(assignTagsMenu, SIGNAL(signalPopupTagsView()),
0259                 d->rightSideBar, SLOT(slotPopupTagsView()));
0260 
0261         if (!CoreDbAccess().db()->hasTags(idList))
0262         {
0263             m_contextMenu->menuAction()->setEnabled(false);
0264         }
0265 
0266         m_contextMenu->addSeparator();
0267 
0268         // Assign Labels -------------------------------------------
0269 
0270         QMenu* const menuLabels           = new QMenu(i18n("Assign Labels"), m_contextMenu);
0271         PickLabelMenuAction* const pmenu  = new PickLabelMenuAction(m_contextMenu);
0272         ColorLabelMenuAction* const cmenu = new ColorLabelMenuAction(m_contextMenu);
0273         RatingMenuAction* const rmenu     = new RatingMenuAction(m_contextMenu);
0274         menuLabels->addAction(pmenu->menuAction());
0275         menuLabels->addAction(cmenu->menuAction());
0276         menuLabels->addAction(rmenu->menuAction());
0277         m_contextMenu->addMenu(menuLabels);
0278 
0279         connect(pmenu, SIGNAL(signalPickLabelChanged(int)),
0280                 this, SLOT(slotAssignPickLabel(int)));
0281 
0282         connect(cmenu, SIGNAL(signalColorLabelChanged(int)),
0283                 this, SLOT(slotAssignColorLabel(int)));
0284 
0285         connect(rmenu, SIGNAL(signalRatingChanged(int)),
0286                 this, SLOT(slotAssignRating(int)));
0287 
0288         // --------------------------------------------------------------
0289 
0290         m_contextMenu->exec(QCursor::pos());
0291 
0292         delete assignTagsMenu;
0293         delete removeTagsMenu;
0294         delete cmenu;
0295         delete pmenu;
0296         delete rmenu;
0297         delete menuLabels;
0298     }
0299 }
0300 
0301 } // namespace Digikam