1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2004-11-22
7 * Description : digiKam image editor - Internal setup
8 *
9 * Copyright (C) 2004-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10 *
11 * This program is free software; you can redistribute it
12 * and/or modify it under the terms of the GNU General
13 * Public License as published by the Free Software Foundation;
14 * either version 2, or (at your option)
15 * any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * ============================================================ */
23
24 #include "imagewindow_p.h"
25
26 namespace Digikam
27 {
28
setupActions()29 void ImageWindow::setupActions()
30 {
31 setupStandardActions();
32
33 KActionCollection* const ac = actionCollection();
34
35 d->toMainWindowAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-icons")),
36 i18nc("@action Finish editing, close editor, back to main window", "Close Editor"), this);
37 connect(d->toMainWindowAction, SIGNAL(triggered()), this, SLOT(slotToMainWindow()));
38 ac->addAction(QLatin1String("imageview_tomainwindow"), d->toMainWindowAction);
39
40
41 // -- Special Delete actions ---------------------------------------------------------------
42
43 // Pop up dialog to ask user whether to permanently delete
44
45 d->fileDeletePermanentlyAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete File Permanently"), this);
46 connect(d->fileDeletePermanentlyAction, SIGNAL(triggered()),
47 this, SLOT(slotDeleteCurrentItemPermanently()));
48 ac->addAction(QLatin1String("image_delete_permanently"), d->fileDeletePermanentlyAction);
49 ac->setDefaultShortcut(d->fileDeletePermanentlyAction, Qt::SHIFT + Qt::Key_Delete);
50
51 // These two actions are hidden, no menu entry, no toolbar entry, no shortcut.
52 // Power users may add them.
53
54 d->fileDeletePermanentlyDirectlyAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")),
55 i18n("Delete Permanently without Confirmation"), this);
56 connect(d->fileDeletePermanentlyDirectlyAction, SIGNAL(triggered()),
57 this, SLOT(slotDeleteCurrentItemPermanentlyDirectly()));
58 ac->addAction(QLatin1String("image_delete_permanently_directly"),
59 d->fileDeletePermanentlyDirectlyAction);
60
61 d->fileTrashDirectlyAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")),
62 i18n("Move to Trash without Confirmation"), this);
63 connect(d->fileTrashDirectlyAction, SIGNAL(triggered()),
64 this, SLOT(slotTrashCurrentItemDirectly()));
65 ac->addAction(QLatin1String("image_trash_directly"), d->fileTrashDirectlyAction);
66
67 // ---------------------------------------------------------------------------------
68
69 createHelpActions();
70
71 // Labels shortcuts must be registered here to be saved in XML GUI files if user customize it.
72
73 TagsActionMngr::defaultManager()->registerLabelsActions(ac);
74
75 QAction* const editTitles = new QAction(i18n("Edit Titles"), this);
76 ac->addAction(QLatin1String("edit_titles"), editTitles);
77 ac->setDefaultShortcut(editTitles, Qt::ALT + Qt::SHIFT + Qt::Key_T);
78 connect(editTitles, SIGNAL(triggered()), this, SLOT(slotRightSideBarActivateTitles()));
79
80 QAction* const editComments = new QAction(i18n("Edit Comments"), this);
81 ac->addAction(QLatin1String("edit_comments"), editComments);
82 ac->setDefaultShortcut(editComments, Qt::ALT + Qt::SHIFT + Qt::Key_C);
83 connect(editComments, SIGNAL(triggered()), this, SLOT(slotRightSideBarActivateComments()));
84
85 QAction* const assignedTags = new QAction(i18n("Show Assigned Tags"), this);
86 ac->addAction(QLatin1String("assigned_tags"), assignedTags);
87 ac->setDefaultShortcut(assignedTags, Qt::ALT + Qt::SHIFT + Qt::Key_A);
88 connect(assignedTags, SIGNAL(triggered()), this, SLOT(slotRightSideBarActivateAssignedTags()));
89 }
90
setupConnections()91 void ImageWindow::setupConnections()
92 {
93 setupStandardConnections();
94
95 connect(this, SIGNAL(loadCurrentLater()),
96 this, SLOT(slotLoadCurrent()), Qt::QueuedConnection);
97
98 // To toggle properly keyboards shortcuts from comments & tags side bar tab.
99
100 connect(d->rightSideBar, SIGNAL(signalNextItem()),
101 this, SLOT(slotForward()));
102
103 connect(d->rightSideBar, SIGNAL(signalPrevItem()),
104 this, SLOT(slotBackward()));
105
106 connect(d->rightSideBar->getFiltersHistoryTab(), SIGNAL(actionTriggered(ItemInfo)),
107 this, SLOT(openImage(ItemInfo)));
108
109 connect(this, SIGNAL(signalSelectionChanged(QRect)),
110 d->rightSideBar, SLOT(slotImageSelectionChanged(QRect)));
111
112 connect(this, SIGNAL(signalNoCurrentItem()),
113 d->rightSideBar, SLOT(slotNoCurrentItem()));
114
115 ItemAttributesWatch* watch = ItemAttributesWatch::instance();
116
117 connect(watch, SIGNAL(signalFileMetadataChanged(QUrl)),
118 this, SLOT(slotFileMetadataChanged(QUrl)));
119
120 /*
121 connect(CoreDbAccess::databaseWatch(), SIGNAL(collectionImageChange(CollectionImageChangeset)),
122 this, SLOT(slotCollectionImageChange(CollectionImageChangeset)),
123 Qt::QueuedConnection);
124
125 connect(d->imageFilterModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
126 this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)));
127 */
128
129 connect(d->thumbBar, SIGNAL(currentChanged(ItemInfo)),
130 this, SLOT(slotThumbBarImageSelected(ItemInfo)));
131
132 connect(d->dragDropHandler, SIGNAL(itemInfosDropped(QList<ItemInfo>)),
133 this, SLOT(slotDroppedOnThumbbar(QList<ItemInfo>)));
134
135 connect(d->thumbBarDock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)),
136 d->thumbBar, SLOT(slotDockLocationChanged(Qt::DockWidgetArea)));
137
138 connect(d->imageInfoModel, SIGNAL(allRefreshingFinished()),
139 this, SLOT(slotThumbBarModelReady()));
140
141 connect(ApplicationSettings::instance(), SIGNAL(setupChanged()),
142 this, SLOT(slotSetupChanged()));
143 }
144
setupUserArea()145 void ImageWindow::setupUserArea()
146 {
147 KSharedConfig::Ptr config = KSharedConfig::openConfig();
148 KConfigGroup group = config->group(configGroupName());
149
150 QWidget* const widget = new QWidget(this);
151 QHBoxLayout* const hlay = new QHBoxLayout(widget);
152 m_splitter = new SidebarSplitter(widget);
153
154 d->viewContainer = new KMainWindow(widget, Qt::Widget);
155 m_splitter->addWidget(d->viewContainer);
156 m_stackView = new EditorStackView(d->viewContainer);
157 m_canvas = new Canvas(m_stackView);
158 d->viewContainer->setCentralWidget(m_stackView);
159
160 m_splitter->setFrameStyle(QFrame::NoFrame);
161 m_splitter->setFrameShape(QFrame::NoFrame);
162 m_splitter->setFrameShadow(QFrame::Plain);
163 m_splitter->setStretchFactor(0, 10); // set Canvas default size to max.
164 m_splitter->setOpaqueResize(false);
165
166 m_canvas->makeDefaultEditingCanvas();
167 m_stackView->setCanvas(m_canvas);
168 m_stackView->setViewMode(EditorStackView::CanvasMode);
169
170 d->rightSideBar = new ItemPropertiesSideBarDB(widget, m_splitter, Qt::RightEdge, true);
171 d->rightSideBar->setObjectName(QLatin1String("ImageEditor Right Sidebar"));
172 d->rightSideBar->getFiltersHistoryTab()->addOpenImageAction();
173
174 hlay->addWidget(m_splitter);
175 hlay->addWidget(d->rightSideBar);
176 hlay->setContentsMargins(QMargins());
177 hlay->setSpacing(0);
178
179 // Code to check for the now depreciated HorizontalThumbar directive. It
180 // is found, it is honored and deleted. The state will from than on be saved
181 // by d->viewContainers built-in mechanism.
182
183 Qt::DockWidgetArea dockArea = Qt::LeftDockWidgetArea;
184
185 if (group.hasKey(d->configHorizontalThumbbarEntry))
186 {
187 if (group.readEntry(d->configHorizontalThumbbarEntry, true))
188 {
189 // Horizontal thumbbar layout
190
191 dockArea = Qt::TopDockWidgetArea;
192 }
193
194 group.deleteEntry(d->configHorizontalThumbbarEntry);
195 }
196
197 d->imageInfoModel = new ItemListModel(this);
198
199 d->imageFilterModel = new ItemFilterModel(this);
200 d->imageFilterModel->setSourceItemModel(d->imageInfoModel);
201
202 d->imageInfoModel->setWatchFlags(d->imageFilterModel->suggestedWatchFlags());
203 d->imageInfoModel->setThumbnailLoadThread(ThumbnailLoadThread::defaultIconViewThread());
204
205 d->imageFilterModel->setCategorizationMode(ItemSortSettings::NoCategories);
206 d->imageFilterModel->setStringTypeNatural(ApplicationSettings::instance()->isStringTypeNatural());
207 d->imageFilterModel->setSortRole((ItemSortSettings::SortRole)ApplicationSettings::instance()->getImageSortOrder());
208 d->imageFilterModel->setSortOrder((ItemSortSettings::SortOrder)ApplicationSettings::instance()->getImageSorting());
209 d->imageFilterModel->setAllGroupsOpen(true); // disable filtering out by group, see bug #283847
210 d->imageFilterModel->sort(0); // an initial sorting is necessary
211
212 d->dragDropHandler = new ItemDragDropHandler(d->imageInfoModel);
213 d->dragDropHandler->setReadOnlyDrop(true);
214 d->imageInfoModel->setDragDropHandler(d->dragDropHandler);
215
216 // The thumb bar is placed in a detachable/dockable widget.
217
218 d->thumbBarDock = new ThumbBarDock(d->viewContainer, Qt::Tool);
219 d->thumbBarDock->setObjectName(QLatin1String("editor_thumbbar"));
220
221 d->thumbBar = new ItemThumbnailBar(d->thumbBarDock);
222 d->thumbBar->setModels(d->imageInfoModel, d->imageFilterModel);
223
224 d->thumbBarDock->setWidget(d->thumbBar);
225 d->viewContainer->addDockWidget(dockArea, d->thumbBarDock);
226 d->thumbBarDock->setFloating(false);
227 /*
228 d->thumbBar->slotDockLocationChanged(dockArea);
229 */
230 setCentralWidget(widget);
231 }
232
addServicesMenu()233 void ImageWindow::addServicesMenu()
234 {
235 addServicesMenuForUrl(d->currentUrl());
236 }
237
slotContextMenu()238 void ImageWindow::slotContextMenu()
239 {
240 if (m_contextMenu)
241 {
242 m_contextMenu->addSeparator();
243 addServicesMenu();
244 m_contextMenu->addSeparator();
245
246 // Bulk assignment/removal of tags --------------------------
247
248 QList<qlonglong> idList;
249 idList << d->currentItemInfo.id();
250
251 TagsPopupMenu* const assignTagsMenu = new TagsPopupMenu(idList, TagsPopupMenu::RECENTLYASSIGNED, this);
252 TagsPopupMenu* const removeTagsMenu = new TagsPopupMenu(idList, TagsPopupMenu::REMOVE, this);
253 assignTagsMenu->menuAction()->setText(i18n("Assign Tag"));
254 removeTagsMenu->menuAction()->setText(i18n("Remove Tag"));
255
256 m_contextMenu->addSeparator();
257
258 m_contextMenu->addMenu(assignTagsMenu);
259 m_contextMenu->addMenu(removeTagsMenu);
260
261 connect(assignTagsMenu, SIGNAL(signalTagActivated(int)),
262 this, SLOT(slotAssignTag(int)));
263
264 connect(removeTagsMenu, SIGNAL(signalTagActivated(int)),
265 this, SLOT(slotRemoveTag(int)));
266
267 connect(assignTagsMenu, SIGNAL(signalPopupTagsView()),
268 d->rightSideBar, SLOT(slotPopupTagsView()));
269
270 if (!CoreDbAccess().db()->hasTags(idList))
271 {
272 m_contextMenu->menuAction()->setEnabled(false);
273 }
274
275 m_contextMenu->addSeparator();
276
277 // Assign Labels -------------------------------------------
278
279 QMenu* const menuLabels = new QMenu(i18n("Assign Labels"), m_contextMenu);
280 PickLabelMenuAction* const pmenu = new PickLabelMenuAction(m_contextMenu);
281 ColorLabelMenuAction* const cmenu = new ColorLabelMenuAction(m_contextMenu);
282 RatingMenuAction* const rmenu = new RatingMenuAction(m_contextMenu);
283 menuLabels->addAction(pmenu->menuAction());
284 menuLabels->addAction(cmenu->menuAction());
285 menuLabels->addAction(rmenu->menuAction());
286 m_contextMenu->addMenu(menuLabels);
287
288 connect(pmenu, SIGNAL(signalPickLabelChanged(int)),
289 this, SLOT(slotAssignPickLabel(int)));
290
291 connect(cmenu, SIGNAL(signalColorLabelChanged(int)),
292 this, SLOT(slotAssignColorLabel(int)));
293
294 connect(rmenu, SIGNAL(signalRatingChanged(int)),
295 this, SLOT(slotAssignRating(int)));
296
297 // --------------------------------------------------------------
298
299 m_contextMenu->exec(QCursor::pos());
300
301 delete assignTagsMenu;
302 delete removeTagsMenu;
303 delete cmenu;
304 delete pmenu;
305 delete rmenu;
306 delete menuLabels;
307 }
308 }
309
310 } // namespace Digikam
311