diff --git a/app/utils/contextmenuhelper.cpp b/app/utils/contextmenuhelper.cpp index e5c95ce0fa..ff6f29eb2f 100644 --- a/app/utils/contextmenuhelper.cpp +++ b/app/utils/contextmenuhelper.cpp @@ -1,1163 +1,1170 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-02-15 * Description : contextmenu helper class * * Copyright (C) 2009-2011 by Andi Clemens * Copyright (C) 2010-2017 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "contextmenuhelper.h" // Qt includes #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include #ifdef HAVE_KIO # include #endif // Local includes #include "digikam_debug.h" #include "album.h" #include "coredb.h" #include "albummanager.h" #include "albummodificationhelper.h" #include "abstractalbummodel.h" #include "coredbaccess.h" #include "digikamapp.h" #include "dfileoperations.h" #include "imageinfo.h" #include "imagefiltermodel.h" #include "lighttablewindow.h" #include "queuemgrwindow.h" #include "picklabelwidget.h" #include "colorlabelwidget.h" #include "ratingwidget.h" #include "tagmodificationhelper.h" #include "tagspopupmenu.h" #include "fileactionmngr.h" #include "tagscache.h" #include "dimg.h" #include "dxmlguiwindow.h" #include "akonadiiface.h" #ifdef HAVE_KIPI # include "kipipluginloader.h" #endif namespace Digikam { class ContextMenuHelper::Private { public: explicit Private(ContextMenuHelper* const q) : gotoAlbumAction(0), gotoDateAction(0), setThumbnailAction(0), imageFilterModel(0), albumModel(0), parent(0), stdActionCollection(0), q(q) { } QAction* gotoAlbumAction; QAction* gotoDateAction; QAction* setThumbnailAction; QList selectedIds; QList selectedItems; QMap queueActions; QMap servicesMap; ImageFilterModel* imageFilterModel; AbstractCheckableAlbumModel* albumModel; QMenu* parent; KActionCollection* stdActionCollection; ContextMenuHelper* q; public: QModelIndex indexForAlbumFromAction(QObject* sender) const { QAction* action = 0; if ((action = qobject_cast(sender))) { Album* const album = action->data().value >(); return albumModel->indexForAlbum(album); } return QModelIndex(); } QAction* copyFromMainCollection(const QString& name) const { QAction* const mainAction = stdActionCollection->action(name); if (!mainAction) { return 0; } QAction* const action = new QAction(mainAction->icon(), mainAction->text(), q); action->setShortcut(mainAction->shortcut()); action->setToolTip(mainAction->toolTip()); return action; } }; ContextMenuHelper::ContextMenuHelper(QMenu* const parent, KActionCollection* const actionCollection) : QObject(parent), d(new Private(this)) { d->parent = parent; if (!actionCollection) { d->stdActionCollection = DigikamApp::instance()->actionCollection(); } else { d->stdActionCollection = actionCollection; } } ContextMenuHelper::~ContextMenuHelper() { delete d; } void ContextMenuHelper::addAction(const QString& name, bool addDisabled) { QAction* const action = d->stdActionCollection->action(name); addAction(action, addDisabled); } void ContextMenuHelper::addAction(QAction* action, bool addDisabled) { if (!action) { return; } if (action->isEnabled() || addDisabled) { d->parent->addAction(action); } } void ContextMenuHelper::addSubMenu(QMenu* subMenu) { d->parent->addMenu(subMenu); } void ContextMenuHelper::addSeparator() { d->parent->addSeparator(); } void ContextMenuHelper::addAction(QAction* action, QObject* recv, const char* slot, bool addDisabled) { if (!action) { return; } connect(action, SIGNAL(triggered()), recv, slot); addAction(action, addDisabled); } void ContextMenuHelper::addStandardActionLightTable() { QAction* action = 0; QStringList ltActionNames; ltActionNames << QLatin1String("image_add_to_lighttable") << QLatin1String("image_lighttable"); if (LightTableWindow::lightTableWindowCreated() && !LightTableWindow::lightTableWindow()->isEmpty()) { action = d->stdActionCollection->action(ltActionNames.at(0)); } else { action = d->stdActionCollection->action(ltActionNames.at(1)); } addAction(action); } void ContextMenuHelper::addStandardActionThumbnail(const imageIds& ids, Album* album) { if (d->setThumbnailAction) { return; } setSelectedIds(ids); if (album && ids.count() == 1) { if (album->type() == Album::PHYSICAL) { d->setThumbnailAction = new QAction(i18n("Set as Album Thumbnail"), this); } else if (album->type() == Album::TAG) { d->setThumbnailAction = new QAction(i18n("Set as Tag Thumbnail"), this); } addAction(d->setThumbnailAction); d->parent->addSeparator(); } } +void ContextMenuHelper::addOpenAndNavigateActions(const imageIds &ids) { + addAction(QLatin1String("image_edit")); + addServicesMenu(ImageInfoList(ids).toImageUrlList()); + addAction(QLatin1String("move_selection_to_album")); + addGotoMenu(ids); +} + void ContextMenuHelper::addServicesMenu(const QList& selectedItems) { setSelectedItems(selectedItems); KService::List offers = DFileOperations::servicesForOpenWith(selectedItems); if (!offers.isEmpty()) { QMenu* const servicesMenu = new QMenu(d->parent); qDeleteAll(servicesMenu->actions()); QAction* const serviceAction = servicesMenu->menuAction(); serviceAction->setText(i18n("Open With")); foreach(const KService::Ptr& service, offers) { QString name = service->name().replace(QLatin1Char('&'), QLatin1String("&&")); QAction* const action = servicesMenu->addAction(name); action->setIcon(QIcon::fromTheme(service->icon())); action->setData(service->name()); d->servicesMap[name] = service; } #ifdef HAVE_KIO servicesMenu->addSeparator(); servicesMenu->addAction(i18n("Other...")); addAction(serviceAction); connect(servicesMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotOpenWith(QAction*))); } else { QAction* const serviceAction = new QAction(i18n("Open With..."), this); addAction(serviceAction); connect(serviceAction, SIGNAL(triggered()), this, SLOT(slotOpenWith())); #endif // HAVE_KIO } } void ContextMenuHelper::slotOpenWith() { // call the slot with an "empty" action slotOpenWith(0); } void ContextMenuHelper::slotOpenWith(QAction* action) { KService::Ptr service; QList list = d->selectedItems; QString name = action ? action->data().toString() : QString(); #ifdef HAVE_KIO if (name.isEmpty()) { QPointer dlg = new KOpenWithDialog(list); if (dlg->exec() != KOpenWithDialog::Accepted) { delete dlg; return; } service = dlg->service(); if (!service) { // User entered a custom command if (!dlg->text().isEmpty()) { DFileOperations::runFiles(dlg->text(), list); } delete dlg; return; } delete dlg; } else #endif // HAVE_KIO { service = d->servicesMap[name]; } DFileOperations::runFiles(*service, list); } bool ContextMenuHelper::imageIdsHaveSameCategory(const imageIds& ids, DatabaseItem::Category category) { bool sameCategory = true; QVariantList varList; foreach(const qlonglong& id, ids) { varList = CoreDbAccess().db()->getImagesFields(id, DatabaseFields::Category); if (varList.isEmpty() || (DatabaseItem::Category)varList.first().toInt() != category) { sameCategory = false; break; } } return sameCategory; } void ContextMenuHelper::addActionNewTag(TagModificationHelper* helper, TAlbum* tag) { QAction* const newTagAction = new QAction(QIcon::fromTheme(QLatin1String("tag-new")), i18n("New Tag..."), this); addAction(newTagAction); helper->bindTag(newTagAction, tag); connect(newTagAction, SIGNAL(triggered()), helper, SLOT(slotTagNew())); } void ContextMenuHelper::addActionDeleteTag(TagModificationHelper* helper, TAlbum* tag) { QAction* const deleteTagAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18n("Delete Tag"), this); addAction(deleteTagAction); helper->bindTag(deleteTagAction, tag); connect(deleteTagAction, SIGNAL(triggered()), helper, SLOT(slotTagDelete())); } void ContextMenuHelper::addActionDeleteTags(Digikam::TagModificationHelper* helper, QList< TAlbum* > tags) { QAction* const deleteTagsAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18n("Delete Tags"), this); addAction(deleteTagsAction); helper->bindMultipleTags(deleteTagsAction, tags); connect(deleteTagsAction, SIGNAL(triggered()), helper, SLOT(slotMultipleTagDel())); } void ContextMenuHelper::addActionTagToFaceTag(TagModificationHelper* helper, TAlbum* tag) { QAction* const tagToFaceTagAction = new QAction(QIcon::fromTheme(QLatin1String("tag-properties")), i18n("Mark As Face Tag"), this); addAction(tagToFaceTagAction); helper->bindTag(tagToFaceTagAction, tag); connect(tagToFaceTagAction, SIGNAL(triggered()), helper, SLOT(slotTagToFaceTag())); } void ContextMenuHelper::addActionTagsToFaceTags(TagModificationHelper* helper, QList< TAlbum* > tags) { QAction* const tagToFaceTagsAction = new QAction(QIcon::fromTheme(QLatin1String("tag-properties")), i18n("Mark As Face Tags"), this); addAction(tagToFaceTagsAction); helper->bindMultipleTags(tagToFaceTagsAction, tags); connect(tagToFaceTagsAction, SIGNAL(triggered()), helper, SLOT(slotMultipleTagsToFaceTags())); } void ContextMenuHelper::addActionEditTag(TagModificationHelper* helper, TAlbum* tag) { QAction* const editTagAction = new QAction(QIcon::fromTheme(QLatin1String("tag-properties")), i18nc("Edit Tag Properties", "Properties..."), this); addAction(editTagAction); helper->bindTag(editTagAction, tag); connect(editTagAction, SIGNAL(triggered()), helper, SLOT(slotTagEdit())); } void ContextMenuHelper::addActionDeleteFaceTag(TagModificationHelper* helper, TAlbum* tag) { QAction* const deleteFaceTagAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18n("Remove Face Tag"), this); deleteFaceTagAction->setWhatsThis(i18n("Removes the face property from the selected tag and the face region from the contained images. Can also untag the images if wished.")); addAction(deleteFaceTagAction); helper->bindTag(deleteFaceTagAction, tag); connect(deleteFaceTagAction, SIGNAL(triggered()), helper, SLOT(slotFaceTagDelete())); } void ContextMenuHelper::addActionDeleteFaceTags(TagModificationHelper* helper, QList< TAlbum* > tags) { QAction* const deleteFaceTagsAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18n("Remove Face Tags"), this); deleteFaceTagsAction->setWhatsThis(i18n("Removes the face property from the selected tags and the face region from the contained images. Can also untag the images if wished.")); addAction(deleteFaceTagsAction); helper->bindMultipleTags(deleteFaceTagsAction, tags); connect(deleteFaceTagsAction, SIGNAL(triggered()), helper, SLOT(slotMultipleFaceTagDel())); } void ContextMenuHelper::addActionNewAlbum(AlbumModificationHelper* helper, PAlbum* parentAlbum) { QAction* const action = d->copyFromMainCollection(QLatin1String("album_new")); addAction(action); helper->bindAlbum(action, parentAlbum); connect(action, SIGNAL(triggered()), helper, SLOT(slotAlbumNew())); } void ContextMenuHelper::addActionDeleteAlbum(AlbumModificationHelper* helper, PAlbum* album) { QAction* const action = d->copyFromMainCollection(QLatin1String("album_delete")); addAction(action, !(album->isRoot() || album->isAlbumRoot())); helper->bindAlbum(action, album); connect(action, SIGNAL(triggered()), helper, SLOT(slotAlbumDelete())); } void ContextMenuHelper::addActionEditAlbum(AlbumModificationHelper* helper, PAlbum* album) { QAction* const action = d->copyFromMainCollection(QLatin1String("album_propsEdit")); addAction(action, !album->isRoot()); helper->bindAlbum(action, album); connect(action, SIGNAL(triggered()), helper, SLOT(slotAlbumEdit())); } void ContextMenuHelper::addActionRenameAlbum(AlbumModificationHelper* helper, PAlbum* album) { QAction* const action = d->copyFromMainCollection(QLatin1String("album_rename")); addAction(action, !(album->isRoot() || album->isAlbumRoot())); helper->bindAlbum(action, album); connect(action, SIGNAL(triggered()), helper, SLOT(slotAlbumRename())); } void ContextMenuHelper::addActionResetAlbumIcon(AlbumModificationHelper* helper, PAlbum* album) { QAction* const action = new QAction(QIcon::fromTheme(QLatin1String("view-refresh")), i18n("Reset Album Icon"), this); addAction(action, !album->isRoot()); helper->bindAlbum(action, album); connect(action, SIGNAL(triggered()), helper, SLOT(slotAlbumResetIcon())); } void ContextMenuHelper::addAssignTagsMenu(const imageIds &ids) { setSelectedIds(ids); QMenu* const assignTagsPopup = new TagsPopupMenu(ids, TagsPopupMenu::RECENTLYASSIGNED, d->parent); assignTagsPopup->menuAction()->setText(i18n("A&ssign Tag")); assignTagsPopup->menuAction()->setIcon(QIcon::fromTheme(QLatin1String("tag"))); d->parent->addMenu(assignTagsPopup); connect(assignTagsPopup, SIGNAL(signalTagActivated(int)), this, SIGNAL(signalAssignTag(int))); connect(assignTagsPopup, SIGNAL(signalPopupTagsView()), this, SIGNAL(signalPopupTagsView())); } void ContextMenuHelper::addRemoveTagsMenu(const imageIds &ids) { setSelectedIds(ids); QMenu* const removeTagsPopup = new TagsPopupMenu(ids, TagsPopupMenu::REMOVE, d->parent); removeTagsPopup->menuAction()->setText(i18n("R&emove Tag")); removeTagsPopup->menuAction()->setIcon(QIcon::fromTheme(QLatin1String("tag"))); d->parent->addMenu(removeTagsPopup); // Performance: Only check for tags if there are <250 images selected // Otherwise enable it regardless if there are tags or not if (ids.count() < 250) { QList tagIDs = CoreDbAccess().db()->getItemCommonTagIDs(ids); bool enable = false; foreach (int tag, tagIDs) { if (TagsCache::instance()->colorLabelForTag(tag) == -1 && TagsCache::instance()->pickLabelForTag(tag) == -1 && TagsCache::instance()->isInternalTag(tag) == false) { enable = true; break; } } removeTagsPopup->menuAction()->setEnabled(enable); } connect(removeTagsPopup, SIGNAL(signalTagActivated(int)), this, SIGNAL(signalRemoveTag(int))); } void ContextMenuHelper::addLabelsAction() { QMenu* const menuLabels = new QMenu(i18n("Assign Labe&ls"), d->parent); PickLabelMenuAction* const pmenu = new PickLabelMenuAction(d->parent); ColorLabelMenuAction* const cmenu = new ColorLabelMenuAction(d->parent); RatingMenuAction* const rmenu = new RatingMenuAction(d->parent); menuLabels->addAction(pmenu->menuAction()); menuLabels->addAction(cmenu->menuAction()); menuLabels->addAction(rmenu->menuAction()); addSubMenu(menuLabels); connect(pmenu, SIGNAL(signalPickLabelChanged(int)), this, SIGNAL(signalAssignPickLabel(int))); connect(cmenu, SIGNAL(signalColorLabelChanged(int)), this, SIGNAL(signalAssignColorLabel(int))); connect(rmenu, SIGNAL(signalRatingChanged(int)), this, SIGNAL(signalAssignRating(int))); } void ContextMenuHelper::addCreateTagFromAddressbookMenu() { AkonadiIface* const abc = new AkonadiIface(d->parent); connect(abc, SIGNAL(signalContactTriggered(QString)), this, SIGNAL(signalAddNewTagFromABCMenu(QString))); // AkonadiIface instance will be deleted with d->parent. } void ContextMenuHelper::slotDeselectAllAlbumItems() { QAction* const selectNoneAction = d->stdActionCollection->action(QLatin1String("selectNone")); QTimer::singleShot(75, selectNoneAction, SIGNAL(triggered())); } void ContextMenuHelper::addImportMenu() { QMenu* const menuImport = new QMenu(i18n("Import"), d->parent); QList importActions; #ifdef HAVE_KIPI importActions = KipiPluginLoader::instance()->kipiActionsByCategory(KIPI::ImportPlugin); #endif /* HAVE_KIPI */ if (!importActions.isEmpty()) { menuImport->addActions(importActions); } else { QAction* const noPlugins = new QAction(i18n("No import plugins available"), this); noPlugins->setEnabled(false); menuImport->addAction(noPlugins); } d->parent->addMenu(menuImport); } void ContextMenuHelper::addExportMenu() { QMenu* const menuExport = new QMenu(i18n("Export"), d->parent); QList exportActions; #ifdef HAVE_KIPI exportActions = KipiPluginLoader::instance()->kipiActionsByCategory(KIPI::ExportPlugin); #endif /* HAVE_KIPI */ #if 0 QAction* selectAllAction = 0; selectAllAction = d->stdActionCollection->action("selectAll"); #endif if (!exportActions.isEmpty()) { menuExport->addActions(exportActions); } else { QAction* const noPlugins = new QAction(i18n("No export plugins available"), this); noPlugins->setEnabled(false); menuExport->addAction(noPlugins); } d->parent->addMenu(menuExport); } void ContextMenuHelper::addAlbumActions() { QList albumActions; #ifdef HAVE_KIPI albumActions = KipiPluginLoader::instance()->kipiActionsByCategory(KIPI::CollectionsPlugin); #endif /* HAVE_KIPI */ if (!albumActions.isEmpty()) { d->parent->addActions(albumActions); } } void ContextMenuHelper::addGotoMenu(const imageIds &ids) { if (d->gotoAlbumAction && d->gotoDateAction) { return; } setSelectedIds(ids); // the currently selected image is always the first item ImageInfo item; if (!d->selectedIds.isEmpty()) { item = ImageInfo(d->selectedIds.first()); } if (item.isNull()) { return; } // when more then one item is selected, don't add the menu if (d->selectedIds.count() > 1) { return; } d->gotoAlbumAction = new QAction(QIcon::fromTheme(QLatin1String("folder-pictures")), i18n("Album"), this); d->gotoDateAction = new QAction(QIcon::fromTheme(QLatin1String("view-calendar")), i18n("Date"), this); QMenu* const gotoMenu = new QMenu(d->parent); gotoMenu->addAction(d->gotoAlbumAction); gotoMenu->addAction(d->gotoDateAction); TagsPopupMenu* const gotoTagsPopup = new TagsPopupMenu(d->selectedIds, TagsPopupMenu::DISPLAY, gotoMenu); QAction* const gotoTag = gotoMenu->addMenu(gotoTagsPopup); gotoTag->setIcon(QIcon::fromTheme(QLatin1String("tag"))); gotoTag->setText(i18n("Tag")); // Disable the goto Tag popup menu, if there are no tags at all. if (!CoreDbAccess().db()->hasTags(d->selectedIds)) { gotoTag->setEnabled(false); } /** * TODO:tags to be ported to multiple selection */ QList albumList = AlbumManager::instance()->currentAlbums(); Album* currentAlbum = 0; if(!albumList.isEmpty()) { currentAlbum = albumList.first(); } else { return; } if (currentAlbum->type() == Album::PHYSICAL) { // If the currently selected album is the same as album to // which the image belongs, then disable the "Go To" Album. // (Note that in recursive album view these can be different). if (item.albumId() == currentAlbum->id()) { d->gotoAlbumAction->setEnabled(false); } } else if (currentAlbum->type() == Album::DATE) { d->gotoDateAction->setEnabled(false); } QAction* const gotoMenuAction = gotoMenu->menuAction(); gotoMenuAction->setIcon(QIcon::fromTheme(QLatin1String("go-jump"))); gotoMenuAction->setText(i18n("Go To")); connect(gotoTagsPopup, SIGNAL(signalTagActivated(int)), this, SIGNAL(signalGotoTag(int))); addAction(gotoMenuAction); } void ContextMenuHelper::addQueueManagerMenu() { QMenu* const bqmMenu = new QMenu(i18n("Batch Queue Manager"), d->parent); bqmMenu->menuAction()->setIcon(QIcon::fromTheme(QLatin1String("run-build"))); bqmMenu->addAction(d->stdActionCollection->action(QLatin1String("image_add_to_current_queue"))); bqmMenu->addAction(d->stdActionCollection->action(QLatin1String("image_add_to_new_queue"))); // if queue list is empty, do not display the queue submenu if (QueueMgrWindow::queueManagerWindowCreated() && !QueueMgrWindow::queueManagerWindow()->queuesMap().isEmpty()) { QueueMgrWindow* const qmw = QueueMgrWindow::queueManagerWindow(); QMenu* const queueMenu = new QMenu(i18n("Add to Existing Queue"), bqmMenu); // queueActions is used by the exec() method to emit an appropriate signal. // Reset the map before filling in the actions. if (!d->queueActions.isEmpty()) { d->queueActions.clear(); } QList queueList; // get queue list from BQM window, do not access it directly, it might crash // when the list is changed QMap qmwMap = qmw->queuesMap(); for (QMap::const_iterator it = qmwMap.constBegin(); it != qmwMap.constEnd(); ++it) { QAction* const action = new QAction(it.value(), this); queueList << action; d->queueActions[it.key()] = action; } queueMenu->addActions(queueList); bqmMenu->addMenu(queueMenu); } d->parent->addMenu(bqmMenu); // NOTE: see bug #252130 : we need to disable new items to add on BQM is this one is running. bqmMenu->setDisabled(QueueMgrWindow::queueManagerWindow()->isBusy()); } void ContextMenuHelper::setAlbumModel(AbstractCheckableAlbumModel* model) { d->albumModel = model; } void ContextMenuHelper::addAlbumCheckUncheckActions(Album* album) { bool enabled = false; QString allString = i18n("All Albums"); QVariant albumData; if (album) { enabled = true; albumData = QVariant::fromValue(AlbumPointer<>(album)); if (album->type() == Album::TAG) allString = i18n("All Tags"); } QMenu* const selectTagsMenu = new QMenu(i18nc("select tags menu", "Select")); addSubMenu(selectTagsMenu); selectTagsMenu->addAction(allString, d->albumModel, SLOT(checkAllAlbums())); selectTagsMenu->addSeparator(); QAction* const selectChildrenAction = selectTagsMenu->addAction(i18n("Children"), this, SLOT(slotSelectChildren())); QAction* const selectParentsAction = selectTagsMenu->addAction(i18n("Parents"), this, SLOT(slotSelectParents())); selectChildrenAction->setData(albumData); selectParentsAction->setData(albumData); QMenu* const deselectTagsMenu = new QMenu(i18nc("deselect tags menu", "Deselect")); addSubMenu(deselectTagsMenu); deselectTagsMenu->addAction(allString, d->albumModel, SLOT(resetAllCheckedAlbums())); deselectTagsMenu->addSeparator(); QAction* const deselectChildrenAction = deselectTagsMenu->addAction(i18n("Children"), this, SLOT(slotDeselectChildren())); QAction* const deselectParentsAction = deselectTagsMenu->addAction(i18n("Parents"), this, SLOT(slotDeselectParents())); deselectChildrenAction->setData(albumData); deselectParentsAction->setData(albumData); d->parent->addAction(i18n("Invert Selection"), d->albumModel, SLOT(invertCheckedAlbums())); selectChildrenAction->setEnabled(enabled); selectParentsAction->setEnabled(enabled); deselectChildrenAction->setEnabled(enabled); deselectParentsAction->setEnabled(enabled); } void ContextMenuHelper::slotSelectChildren() { if (!d->albumModel) { return; } d->albumModel->checkAllAlbums(d->indexForAlbumFromAction(sender())); } void ContextMenuHelper::slotDeselectChildren() { if (!d->albumModel) { return; } d->albumModel->resetCheckedAlbums(d->indexForAlbumFromAction(sender())); } void ContextMenuHelper::slotSelectParents() { if (!d->albumModel) { return; } d->albumModel->checkAllParentAlbums(d->indexForAlbumFromAction(sender())); } void ContextMenuHelper::slotDeselectParents() { if (!d->albumModel) { return; } d->albumModel->resetCheckedParentAlbums(d->indexForAlbumFromAction(sender())); } void ContextMenuHelper::addGroupMenu(const imageIds &ids, const QList& extraMenuItems) { QList actions = groupMenuActions(ids); if (actions.isEmpty() && extraMenuItems.isEmpty()) { return; } if (!extraMenuItems.isEmpty()) { if (!actions.isEmpty()) { QAction* separator = new QAction(this); separator->setSeparator(true); actions << separator; } actions << extraMenuItems; } QMenu* const menu = new QMenu(i18n("Group")); foreach(QAction* const action, actions) { menu->addAction(action); } d->parent->addMenu(menu); } void ContextMenuHelper::addGroupActions(const imageIds &ids) { foreach(QAction* const action, groupMenuActions(ids)) { d->parent->addAction(action); } } void ContextMenuHelper::setImageFilterModel(ImageFilterModel* model) { d->imageFilterModel = model; } QList ContextMenuHelper::groupMenuActions(const imageIds &ids) { setSelectedIds(ids); QList actions; if (ids.isEmpty()) { if (d->imageFilterModel) { if (!d->imageFilterModel->isAllGroupsOpen()) { QAction* const openAction = new QAction(i18nc("@action:inmenu", "Open All Groups"), this); connect(openAction, SIGNAL(triggered()), this, SLOT(slotOpenGroups())); actions << openAction; } else { QAction* const closeAction = new QAction(i18nc("@action:inmenu", "Close All Groups"), this); connect(closeAction, SIGNAL(triggered()), this, SLOT(slotCloseGroups())); actions << closeAction; } } return actions; } ImageInfo info(ids.first()); if (ids.size() == 1) { if (info.hasGroupedImages()) { if (d->imageFilterModel) { if (!d->imageFilterModel->isGroupOpen(info.id())) { QAction* const action = new QAction(i18nc("@action:inmenu", "Show Grouped Images"), this); connect(action, SIGNAL(triggered()), this, SLOT(slotOpenGroups())); actions << action; } else { QAction* const action = new QAction(i18nc("@action:inmenu", "Hide Grouped Images"), this); connect(action, SIGNAL(triggered()), this, SLOT(slotCloseGroups())); actions << action; } } QAction* const separator = new QAction(this); separator->setSeparator(true); actions << separator; QAction* const clearAction = new QAction(i18nc("@action:inmenu", "Ungroup"), this); connect(clearAction, SIGNAL(triggered()), this, SIGNAL(signalUngroup())); actions << clearAction; } else if (info.isGrouped()) { QAction* const action = new QAction(i18nc("@action:inmenu", "Remove From Group"), this); connect(action, SIGNAL(triggered()), this, SIGNAL(signalRemoveFromGroup())); actions << action; // TODO: set as group leader / pick image } } else { QAction* const closeAction = new QAction(i18nc("@action:inmenu", "Group Selected Here"), this); connect(closeAction, SIGNAL(triggered()), this, SIGNAL(signalCreateGroup())); actions << closeAction; QAction* const closeActionDate = new QAction(i18nc("@action:inmenu", "Group Selected By Time"), this); connect(closeActionDate, SIGNAL(triggered()), this, SIGNAL(signalCreateGroupByTime())); actions << closeActionDate; QAction* const closeActionType = new QAction(i18nc("@action:inmenu", "Group Selected By Filename"), this); connect(closeActionType, SIGNAL(triggered()), this, SIGNAL(signalCreateGroupByFilename())); actions << closeActionType; QAction* const separator = new QAction(this); separator->setSeparator(true); actions << separator; if (d->imageFilterModel) { QAction* const openAction = new QAction(i18nc("@action:inmenu", "Show Grouped Images"), this); connect(openAction, SIGNAL(triggered()), this, SLOT(slotOpenGroups())); actions << openAction; QAction* const closeAction = new QAction(i18nc("@action:inmenu", "Hide Grouped Images"), this); connect(closeAction, SIGNAL(triggered()), this, SLOT(slotCloseGroups())); actions << closeAction; QAction* const separator2 = new QAction(this); separator2->setSeparator(true); actions << separator2; } QAction* const removeAction = new QAction(i18nc("@action:inmenu", "Remove Selected From Groups"), this); connect(removeAction, SIGNAL(triggered()), this, SIGNAL(signalRemoveFromGroup())); actions << removeAction; QAction* const clearAction = new QAction(i18nc("@action:inmenu", "Ungroup Selected"), this); connect(clearAction, SIGNAL(triggered()), this, SIGNAL(signalUngroup())); actions << clearAction; } return actions; } void ContextMenuHelper::setGroupsOpen(bool open) { if (!d->imageFilterModel || d->selectedIds.isEmpty()) { return; } GroupImageFilterSettings settings = d->imageFilterModel->groupImageFilterSettings(); foreach(qlonglong id, d->selectedIds) { ImageInfo info(id); if (info.hasGroupedImages()) { settings.setOpen(id, open); } } d->imageFilterModel->setGroupImageFilterSettings(settings); } void ContextMenuHelper::slotOpenGroups() { setGroupsOpen(true); } void ContextMenuHelper::slotCloseGroups() { setGroupsOpen(false); } void ContextMenuHelper::slotOpenAllGroups() { if (!d->imageFilterModel) { return; } d->imageFilterModel->setAllGroupsOpen(true); } void ContextMenuHelper::slotCloseAllGroups() { if (!d->imageFilterModel) { return; } d->imageFilterModel->setAllGroupsOpen(false); } void ContextMenuHelper::addStandardActionCut(QObject* recv, const char* slot) { QAction* const cut = DXmlGuiWindow::buildStdAction(StdCutAction, recv, slot, d->parent); addAction(cut); } void ContextMenuHelper::addStandardActionCopy(QObject* recv, const char* slot) { QAction* const copy = DXmlGuiWindow::buildStdAction(StdCopyAction, recv, slot, d->parent); addAction(copy); } void ContextMenuHelper::addStandardActionPaste(QObject* recv, const char* slot) { QAction* const paste = DXmlGuiWindow::buildStdAction(StdPasteAction, recv, slot, d->parent); const QMimeData* const data = qApp->clipboard()->mimeData(QClipboard::Clipboard); if (!data || !data->hasUrls()) { paste->setEnabled(false); } addAction(paste, true); } void ContextMenuHelper::addStandardActionItemDelete(QObject* recv, const char* slot, int quantity) { QAction* const trashAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18ncp("@action:inmenu Pluralized", "Move to Trash", "Move %1 Files to Trash", quantity), d->parent); connect(trashAction, SIGNAL(triggered()), recv, slot); addAction(trashAction); } QAction* ContextMenuHelper::exec(const QPoint& pos, QAction* at) { QAction* const choice = d->parent->exec(pos, at); if (choice) { if (d->selectedIds.count() == 1) { ImageInfo selectedItem(d->selectedIds.first()); if (choice == d->gotoAlbumAction) { emit signalGotoAlbum(selectedItem); } else if (choice == d->gotoDateAction) { emit signalGotoDate(selectedItem); } else if (choice == d->setThumbnailAction) { emit signalSetThumbnail(selectedItem); } } // check if a BQM action has been triggered for (QMap::const_iterator it = d->queueActions.constBegin(); it != d->queueActions.constEnd(); ++it) { if (choice == it.value()) { emit signalAddToExistingQueue(it.key()); return choice; } } } return choice; } void ContextMenuHelper::setSelectedIds(const imageIds &ids) { if (d->selectedIds.isEmpty()) { d->selectedIds = ids; } } void ContextMenuHelper::setSelectedItems(const QList& urls) { if (d->selectedItems.isEmpty()) { d->selectedItems = urls; } } } // namespace Digikam diff --git a/app/utils/contextmenuhelper.h b/app/utils/contextmenuhelper.h index 1fe9bdf057..4e62b66b92 100644 --- a/app/utils/contextmenuhelper.h +++ b/app/utils/contextmenuhelper.h @@ -1,425 +1,435 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-02-15 * Description : contextmenu helper class * * Copyright (C) 2009-2010 by Andi Clemens * Copyright (C) 2010-2017 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef CONTEXTMENUHELPER_H #define CONTEXTMENUHELPER_H // Qt includes #include #include #include // Local includes #include "digikam_export.h" #include "digikam_config.h" #include "coredbalbuminfo.h" class QAction; class QMenu; class QPoint; class QString; class KActionCollection; namespace Digikam { class AbstractCheckableAlbumModel; class Album; class AlbumIconItem; class AlbumModificationHelper; class ImageInfo; class ImageFilterModel; class PAlbum; class TagModificationHelper; class TAlbum; /** * @brief A helper class to add actions and special menus to the context menu. * * The %ContextMenuHelper class helps adding commonly used actions and menus. * Use this class to add * - actions from the actionCollection * - standard actions (copy, paste, delete) * - temporary actions * - predefined special actions * - predefined submenus * to the menu. * * All addAction() methods take a special parameter 'addDisabled'. This * parameter controls if disabled actions are added to the menu. Normally * adding disabled actions is turned off, to clean up the menu and make it * more readable. * * If the %ContextMenuHelper class is used, you need to call its own exec() method, * instead the one from the parent menu. This way signals from * special menus can be emitted and connected to the appropriate slots. */ class DIGIKAM_EXPORT ContextMenuHelper : public QObject { Q_OBJECT public: typedef const QList imageIds; Q_SIGNALS: void signalSetThumbnail(const ImageInfo&); void signalGotoAlbum(const ImageInfo&); void signalGotoDate(const ImageInfo&); void signalGotoTag(int); void signalAssignTag(int); void signalRemoveTag(int); void signalAssignPickLabel(int); void signalAssignColorLabel(int); void signalAssignRating(int); void signalAddToExistingQueue(int); void signalAddNewTagFromABCMenu(const QString&); void signalPopupTagsView(); void signalCreateGroup(); void signalCreateGroupByTime(); void signalCreateGroupByFilename(); void signalUngroup(); void signalRemoveFromGroup(); public: /** * Constructs the helper class. * * @param parent the menu the helper class is linked to * @param actionCollection the actionCollection that should be used. If not set, the standard * action from DigikamApp is used */ explicit ContextMenuHelper(QMenu* parent, KActionCollection* actionCollection = 0); virtual ~ContextMenuHelper(); /** * Add an action from the actionCollection. * * This method adds actions from the actionCollection. The actionCollection can * be set in the constructor of the ContextMenuHelper class. * * @param name the name of the action in the actionCollection * @param addDisabled if set, disabled actions are added to the menu */ void addAction(const QString& name, bool addDisabled = false); /** * Add a temporary action. * * Sometimes it is necessary to define actions that only exist in the current context menu content. * Use this method to add such an action. * * @param action the action to add * @param addDisabled if set, disabled actions are added to the menu */ void addAction(QAction* action, bool addDisabled = false); /** * Add a temporary action and assign it to a custom slot. * * Use this method if you want to add a temporary action and immediately connect it to the * receiving slot. * * @param action the action to add * @param recv the receiver of the triggered action * @param slot the slot to connect the triggered action to * @param addDisabled if set, disabled actions are added to the menu */ void addAction(QAction* action, QObject* recv, const char* slot, bool addDisabled = false); /** * Add the standard cut action and connect it to the appropriate slot * * @param recv the receiver of the triggered action * @param slot the slot to connect the triggered action to */ void addStandardActionCut(QObject* recv, const char* slot); /** * Add the standard copy action and connect it to the appropriate slot * * @param recv the receiver of the triggered action * @param slot the slot to connect the triggered action to */ void addStandardActionCopy(QObject* recv, const char* slot); /** * Add the standard paste action and connect it to the appropriate slot * * @param recv the receiver of the triggered action * @param slot the slot to connect the triggered action to */ void addStandardActionPaste(QObject* recv, const char* slot); /** * Add the standard delete action and connect it to the appropriate slot * * @param recv the receiver of the triggered action * @param slot the slot to connect the triggered action to * @param quantity the number of the files that should be deleted. This parameter is used for * the action name and is normally used when deleting more then one item. */ void addStandardActionItemDelete(QObject* recv, const char* slot, int quantity = 1); /** * Add the lighttable action to the menu. * * Do not use addAction() to add the lighttable action, because we need * to handle special cases here. Depending on whether the lighttable window * has already been created and filled with items, we set different actions. */ void addStandardActionLightTable(); /** * Add the thumbnail action to the menu. * * Do not use addAction() to add the thumbnail action, because we need * to handle special cases here. Depending on whether the current view is * album or icon view, we set different actions. * * @param ids the selected items in the current view * @param album the current album the AlbumIconView is displaying */ void addStandardActionThumbnail(const imageIds& ids, Album* album); + /** + * Add section for main views for opening and moving/going to albums. + * + * This is a convenience function to ensure consistent menues and reduce + * code duplication. + * + * @param imageIds the list of selected items + */ + void addOpenAndNavigateActions(const imageIds &ids); + /** * Add the services menu to the menu. * * The services menu is used to open the selected items in a different application. * It will query the item for registered services and provide them in a submenu. * The menu will be titled "Open With...". * * @param selectedItems the list of selected items */ void addServicesMenu(const QList& selectedItems); /** * Add the Goto menu. * * This menu will provide the following actions for the given item: * - Goto Album * - Goto Date * - Goto Tag * To make this menu work, you need to run exec() from this class, otherwise the signals * are not emitted and you will not be able to react on triggered actions from this menu. * Make sure to connect the signals to the appropriate slots in the context menu handling method. * - * @param ids the selected items + * @param imageIds the list of selected items * @see exec() * @see signalGotoAlbum() signalGotoDate() signalGotoTag() */ void addGotoMenu(const imageIds& ids); /** * Add Queue Manager actions menu. */ void addQueueManagerMenu(); /** * Add actions to add, remove or edit a tag. * The tag modification helper is used to execute the action. * You must set the parent tag to use on modification helper. */ void addActionNewTag(TagModificationHelper* helper, TAlbum* parentTag = 0); void addActionDeleteTag(TagModificationHelper* helper, TAlbum* tag); void addActionDeleteTags(TagModificationHelper* helper, QList< TAlbum* > tags); void addActionEditTag(TagModificationHelper* helper, TAlbum* tag); /** * Add action to delete tags from people sidebar. */ void addActionDeleteFaceTag(TagModificationHelper* helper, TAlbum* tag); void addActionDeleteFaceTags(TagModificationHelper* helper, QList< TAlbum* > tags); /** * Add action to set tags as face tags. */ void addActionTagToFaceTag(TagModificationHelper* helper, TAlbum* tag); void addActionTagsToFaceTags(TagModificationHelper* helper, QList< TAlbum* > tags); /** * Add actions to add, remove or edit a tag. * The tag modification helper is used to execute the action. * You must set the parent tag to use on modification helper. */ void addActionNewAlbum(AlbumModificationHelper* helper, PAlbum* parentAlbum = 0); void addActionDeleteAlbum(AlbumModificationHelper* helper, PAlbum* album); void addActionEditAlbum(AlbumModificationHelper* helper, PAlbum* album); void addActionRenameAlbum(AlbumModificationHelper* helper, PAlbum* album); void addActionResetAlbumIcon(AlbumModificationHelper* helper, PAlbum* album); /** * Add "Assign Tags" menu. * * This menu will provide a list of all tags available so that they can be assigned to the current * selected items. * * To make this menu work, you need to run exec() from this class, otherwise the signals * are not emitted and you will not be able to react on triggered actions from this menu. * Make sure to connect the signals to the appropriate slots in the context menu handling method. * * @param ids the selected items * @see exec() * @see signalAssignTag() */ void addAssignTagsMenu(const imageIds& ids); /** * Add "Remove Tags" menu. * * This menu will provide a list of all tags assigned to the current items. Actions triggered in here * will remove the selected tag from the items. * * To make this menu work, you need to run exec() from this class, otherwise the signals * are not emitted and you will not be able to react on triggered actions from this menu. * Make sure to connect the signals to the appropriate slots in the context menu handling method. * * @param ids the selected items * @see exec() * @see signalRemoveTag() */ void addRemoveTagsMenu(const imageIds& ids); /** * Add a menu to create new tags from adressbook entries. */ void addCreateTagFromAddressbookMenu(); /** * Add "Pick/Color/Rating Labels" action. * * This action will provide methods to assign pick/color/rating labels to the currently selected items. * * To make this menu work, you need to run exec() from this class, otherwise the signals * are not emitted and you will not be able to react on triggered actions from this menu. * Make sure to connect the signals to the appropriate slots in the context menu handling method. * * @see exec() * @see signalAssignPickLabel() * @see signalAssignColorLabel() * @see signalAssignRating() */ void addLabelsAction(); /** * Add a "Group" menu. * This menu will provide actions open, close, add to, remove from, or split a group. * * addGroupActions will add the actions as a flat list, not in a submenu. * Note: Call setImageFilterModel before to have Open/Close group actions. */ void addGroupMenu(const imageIds& ids, const QList& extraMenuItems = QList()); void addGroupActions(const imageIds& ids); /** * Set a filter model. * Some of the group actions will operate directly on the model. */ void setImageFilterModel(ImageFilterModel* model); /** * Add a Select and Deselect menu to check and uncheck albums. * Note: Call setAlbumModel before, or this will have no effect. */ void addAlbumCheckUncheckActions(Album* album); /** * Set an album model. * The check/uncheck actions will operate directly on the model. */ void setAlbumModel(AbstractCheckableAlbumModel* model); /** * Add Import KIPI actions menu. */ void addImportMenu(); /** * Add Export KIPI actions menu. */ void addExportMenu(); void addAlbumActions(); /** * Add a submenu to the parent context menu. * * @param subMenu the submenu to be added */ void addSubMenu(QMenu* subMenu); /** * Add a separator to the context menu */ void addSeparator(); /** * Execute the registered parent menu and evaluate the triggered actions. * * Always use this method instead the one from the parent menu. * It will ensure that the signals are emitted and special cases are handled. * * @param pos position of the triggered action in the registered menu * @param at the action that should be at the position pos * @return the triggered action */ QAction* exec(const QPoint& pos, QAction* at = 0); private Q_SLOTS: void slotOpenWith(); void slotOpenWith(QAction* action); void slotDeselectAllAlbumItems(); void slotOpenGroups(); void slotCloseGroups(); void slotOpenAllGroups(); void slotCloseAllGroups(); void slotSelectChildren(); void slotDeselectChildren(); void slotSelectParents(); void slotDeselectParents(); private: void setGroupsOpen(bool open); void setSelectedIds(const imageIds& ids); void setSelectedItems(const QList& urls); bool imageIdsHaveSameCategory(const imageIds& ids, DatabaseItem::Category category); QList groupMenuActions(const imageIds& ids); private: class Private; Private* const d; }; } // namespace Digikam #endif /* CONTEXTMENUHELPER_H */ diff --git a/app/views/digikamview.cpp b/app/views/digikamview.cpp index 4ae3a2847a..df1f80f8d2 100644 --- a/app/views/digikamview.cpp +++ b/app/views/digikamview.cpp @@ -1,2734 +1,2734 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2002-16-10 * Description : implementation of album view interface. * * Copyright (C) 2002-2005 by Renchi Raju * Copyright (C) 2002-2017 by Gilles Caulier * Copyright (C) 2009-2011 by Johannes Wienke * Copyright (C) 2010-2011 by Andi Clemens * Copyright (C) 2011-2013 by Michael G. Hansen * Copyright (C) 2014-2015 by Mohamed Anwer * Copyright (C) 2017 by Simon Frei * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "digikamview.h" // Qt includes #include #include #include #include // KDE includes #include // Local includes #include "albumhistory.h" #include "albumlabelstreeview.h" #include "coredbsearchxml.h" #include "digikam_config.h" #include "digikam_debug.h" #include "digikam_globals.h" #include "digikamapp.h" #include "digikamimageview.h" #include "dmessagebox.h" #include "dzoombar.h" #include "dtrashitemmodel.h" #include "fileactionmngr.h" #include "fileactionprogress.h" #include "filtersidebarwidget.h" #include "filterstatusbar.h" #include "imagealbummodel.h" #include "imagedescedittab.h" #include "imagepreviewview.h" #include "imagepropertiessidebardb.h" #include "imagepropertiesversionstab.h" #include "imagethumbnailbar.h" #include "imageviewutilities.h" #include "leftsidebarwidgets.h" #include "loadingcacheinterface.h" #include "metadatahub.h" #include "metadatasettings.h" #include "metadatasynchronizer.h" #include "newitemsfinder.h" #include "presentationmngr.h" #include "queuemgrwindow.h" #include "scancontroller.h" #include "setup.h" #include "sidebar.h" #include "slideshow.h" #include "slideshowbuilder.h" #include "statusprogressbar.h" #include "tableview.h" #include "tagmodificationhelper.h" #include "tagsactionmngr.h" #include "tagscache.h" #include "tagsmanager.h" #include "thumbsgenerator.h" #include "trashview.h" #include "versionmanagersettings.h" #include "contextmenuhelper.h" #ifdef HAVE_MEDIAPLAYER # include "mediaplayerview.h" #endif //HAVE_MEDIAPLAYER #ifdef HAVE_MARBLE # include "mapwidgetview.h" #endif // HAVE_MARBLE namespace Digikam { class DigikamView::Private { public: Private() : needDispatchSelection(false), useAlbumHistory(false), initialAlbumID(0), thumbSize(ThumbnailSize::Medium), dockArea(0), splitter(0), selectionTimer(0), thumbSizeTimer(0), albumFolderSideBar(0), tagViewSideBar(0), labelsSideBar(0), dateViewSideBar(0), timelineSideBar(0), searchSideBar(0), fuzzySearchSideBar(0), #ifdef HAVE_MARBLE gpsSearchSideBar(0), mapView(0), #endif // HAVE_MARBLE peopleSideBar(0), parent(0), iconView(0), tableView(0), trashView(0), utilities(0), albumManager(0), albumHistory(0), stackedview(0), lastViewMode(StackedView::IconViewMode), albumModificationHelper(0), tagModificationHelper(0), searchModificationHelper(0), leftSideBar(0), rightSideBar(0), filterWidget(0), optionAlbumViewPrefix(QLatin1String("AlbumView")), modelCollection(0), labelsSearchHandler(0) { } QString userPresentableAlbumTitle(const QString& album) const; void addPageUpDownActions(DigikamView* const q, QWidget* const w); public: bool needDispatchSelection; bool useAlbumHistory; int initialAlbumID; int thumbSize; QMainWindow* dockArea; SidebarSplitter* splitter; QTimer* selectionTimer; QTimer* thumbSizeTimer; // left side bar AlbumFolderViewSideBarWidget* albumFolderSideBar; TagViewSideBarWidget* tagViewSideBar; LabelsSideBarWidget* labelsSideBar; DateFolderViewSideBarWidget* dateViewSideBar; TimelineSideBarWidget* timelineSideBar; SearchSideBarWidget* searchSideBar; FuzzySearchSideBarWidget* fuzzySearchSideBar; #ifdef HAVE_MARBLE GPSSearchSideBarWidget* gpsSearchSideBar; MapWidgetView* mapView; #endif // HAVE_MARBLE PeopleSideBarWidget* peopleSideBar; DigikamApp* parent; DigikamImageView* iconView; TableView* tableView; TrashView* trashView; ImageViewUtilities* utilities; AlbumManager* albumManager; AlbumHistory* albumHistory; StackedView* stackedview; StackedView::StackedViewMode lastViewMode; AlbumModificationHelper* albumModificationHelper; TagModificationHelper* tagModificationHelper; SearchModificationHelper* searchModificationHelper; Sidebar* leftSideBar; ImagePropertiesSideBarDB* rightSideBar; FilterSideBarWidget* filterWidget; QString optionAlbumViewPrefix; QList leftSideBarWidgets; DigikamModelCollection* modelCollection; AlbumLabelsSearchHandler* labelsSearchHandler; }; QString DigikamView::Private::userPresentableAlbumTitle(const QString& title) const { if (title == SAlbum::getTemporaryHaarTitle(DatabaseSearch::HaarSketchSearch)) { return i18n("Fuzzy Sketch Search"); } else if (title == SAlbum::getTemporaryHaarTitle(DatabaseSearch::HaarImageSearch)) { return i18n("Fuzzy Image Search"); } else if (title == SAlbum::getTemporaryTitle(DatabaseSearch::MapSearch)) { return i18n("Map Search"); } else if (title == SAlbum::getTemporaryTitle(DatabaseSearch::AdvancedSearch) || title == SAlbum::getTemporaryTitle(DatabaseSearch::KeywordSearch)) { return i18n("Last Search"); } else if (title == SAlbum::getTemporaryTitle(DatabaseSearch::TimeLineSearch)) { return i18n("Timeline"); } return title; } void DigikamView::Private::addPageUpDownActions(DigikamView* const q, QWidget* const w) { defineShortcut(w, Qt::Key_PageDown, q, SLOT(slotNextItem())); defineShortcut(w, Qt::Key_Down, q, SLOT(slotNextItem())); defineShortcut(w, Qt::Key_Right, q, SLOT(slotNextItem())); defineShortcut(w, Qt::Key_PageUp, q, SLOT(slotPrevItem())); defineShortcut(w, Qt::Key_Up, q, SLOT(slotPrevItem())); defineShortcut(w, Qt::Key_Left, q, SLOT(slotPrevItem())); } // ------------------------------------------------------------------------------------------- DigikamView::DigikamView(QWidget* const parent, DigikamModelCollection* const modelCollection) : DHBox(parent), d(new Private) { qRegisterMetaType("SlideShowSettings"); d->parent = static_cast(parent); d->modelCollection = modelCollection; d->albumManager = AlbumManager::instance(); d->albumModificationHelper = new AlbumModificationHelper(this, this); d->tagModificationHelper = new TagModificationHelper(this, this); d->searchModificationHelper = new SearchModificationHelper(this, this); d->splitter = new SidebarSplitter; d->splitter->setFrameStyle( QFrame::NoFrame ); d->splitter->setFrameShadow( QFrame::Plain ); d->splitter->setFrameShape( QFrame::NoFrame ); d->splitter->setOpaqueResize(false); d->leftSideBar = new Sidebar(this, d->splitter, Qt::LeftEdge); d->leftSideBar->setObjectName(QLatin1String("Digikam Left Sidebar")); d->splitter->setParent(this); // The dock area where the thumbnail bar is allowed to go. d->dockArea = new QMainWindow(this, Qt::Widget); d->splitter->addWidget(d->dockArea); d->stackedview = new StackedView(d->dockArea); d->dockArea->setCentralWidget(d->stackedview); d->stackedview->setDockArea(d->dockArea); d->iconView = d->stackedview->imageIconView(); #ifdef HAVE_MARBLE d->mapView = d->stackedview->mapWidgetView(); #endif // HAVE_MARBLE d->tableView = d->stackedview->tableView(); d->trashView = d->stackedview->trashView(); d->utilities = new ImageViewUtilities(this); d->addPageUpDownActions(this, d->stackedview->imagePreviewView()); d->addPageUpDownActions(this, d->stackedview->thumbBar()); #ifdef HAVE_MEDIAPLAYER d->addPageUpDownActions(this, d->stackedview->mediaPlayerView()); #endif //HAVE_MEDIAPLAYER d->rightSideBar = new ImagePropertiesSideBarDB(this, d->splitter, Qt::RightEdge, true); d->rightSideBar->setObjectName(QLatin1String("Digikam Right Sidebar")); // album folder view d->albumFolderSideBar = new AlbumFolderViewSideBarWidget(d->leftSideBar, d->modelCollection->getAlbumModel(), d->albumModificationHelper); d->leftSideBarWidgets << d->albumFolderSideBar; connect(d->albumFolderSideBar, SIGNAL(signalFindDuplicates(PAlbum*)), this, SLOT(slotNewDuplicatesSearch(PAlbum*))); // Tags sidebar tab contents. d->tagViewSideBar = new TagViewSideBarWidget(d->leftSideBar, d->modelCollection->getTagModel()); d->leftSideBarWidgets << d->tagViewSideBar; connect(d->tagViewSideBar, SIGNAL(signalFindDuplicates(QList)), this, SLOT(slotNewDuplicatesSearch(QList))); // Labels sidebar d->labelsSideBar = new LabelsSideBarWidget(d->leftSideBar); d->leftSideBarWidgets << d->labelsSideBar; d->labelsSearchHandler = new AlbumLabelsSearchHandler(d->labelsSideBar->labelsTree()); // date view d->dateViewSideBar = new DateFolderViewSideBarWidget(d->leftSideBar, d->modelCollection->getDateAlbumModel(), d->iconView->imageAlbumFilterModel()); d->leftSideBarWidgets << d->dateViewSideBar; // timeline side bar d->timelineSideBar = new TimelineSideBarWidget(d->leftSideBar, d->modelCollection->getSearchModel(), d->searchModificationHelper); d->leftSideBarWidgets << d->timelineSideBar; // Search sidebar tab contents. d->searchSideBar = new SearchSideBarWidget(d->leftSideBar, d->modelCollection->getSearchModel(), d->searchModificationHelper); d->leftSideBarWidgets << d->searchSideBar; // Fuzzy search d->fuzzySearchSideBar = new FuzzySearchSideBarWidget(d->leftSideBar, d->modelCollection->getSearchModel(), d->searchModificationHelper); d->leftSideBarWidgets << d->fuzzySearchSideBar; connect(d->fuzzySearchSideBar,SIGNAL(signalActive(bool)), this, SIGNAL(signalFuzzySidebarActive(bool))); #ifdef HAVE_MARBLE d->gpsSearchSideBar = new GPSSearchSideBarWidget(d->leftSideBar, d->modelCollection->getSearchModel(), d->searchModificationHelper, d->iconView->imageFilterModel(), d->iconView->getSelectionModel()); d->leftSideBarWidgets << d->gpsSearchSideBar; #endif // HAVE_MARBLE // People Sidebar d->peopleSideBar = new PeopleSideBarWidget(d->leftSideBar, d->modelCollection->getTagFacesModel(), d->searchModificationHelper); connect(d->peopleSideBar, SIGNAL(requestFaceMode(bool)), d->iconView, SLOT(setFaceMode(bool))); connect(d->peopleSideBar, SIGNAL(signalFindDuplicates(QList)), this, SLOT(slotNewDuplicatesSearch(QList))); d->leftSideBarWidgets << d->peopleSideBar; foreach(SidebarWidget* const leftWidget, d->leftSideBarWidgets) { d->leftSideBar->appendTab(leftWidget, leftWidget->getIcon(), leftWidget->getCaption()); connect(leftWidget, SIGNAL(requestActiveTab(SidebarWidget*)), this, SLOT(slotLeftSideBarActivate(SidebarWidget*))); } // To the right. // NOTE: by Veaceslav, currently if you register these actions in Tags/Caption window, // the arrow up and down are not handled correctly by QCompleter // d->addPageUpDownActions(this, d->rightSideBar->imageDescEditTab()); // Tags Filter sidebar tab contents. d->filterWidget = new FilterSideBarWidget(d->rightSideBar, d->modelCollection->getTagFilterModel()); d->rightSideBar->appendTab(d->filterWidget, QIcon::fromTheme(QLatin1String("view-filter")), i18n("Filters")); // Versions sidebar overlays d->rightSideBar->getFiltersHistoryTab()->addOpenAlbumAction(d->iconView->imageModel()); d->rightSideBar->getFiltersHistoryTab()->addShowHideOverlay(); d->selectionTimer = new QTimer(this); d->selectionTimer->setSingleShot(true); d->selectionTimer->setInterval(75); d->thumbSizeTimer = new QTimer(this); d->thumbSizeTimer->setSingleShot(true); d->thumbSizeTimer->setInterval(300); d->albumHistory = new AlbumHistory(); slotSidebarTabTitleStyleChanged(); setupConnections(); connect(d->rightSideBar->imageDescEditTab()->getNewTagEdit(), SIGNAL(taggingActionFinished()), this, SLOT(slotFocusAndNextImage())); connect(d->rightSideBar, SIGNAL(signalSetupMetadataFilters(int)), this, SLOT(slotSetupMetadataFilters(int))); } DigikamView::~DigikamView() { saveViewState(); delete d->labelsSearchHandler; delete d->albumHistory; delete d; } void DigikamView::applySettings() { foreach(SidebarWidget* const sidebarWidget, d->leftSideBarWidgets) { sidebarWidget->applySettings(); } d->iconView->imageFilterModel()->setVersionImageFilterSettings(VersionImageFilterSettings(ApplicationSettings::instance()->getVersionManagerSettings())); refreshView(); } void DigikamView::refreshView() { d->rightSideBar->refreshTagsView(); } void DigikamView::setupConnections() { // -- DigikamApp connections ---------------------------------- connect(d->parent, SIGNAL(signalEscapePressed()), this, SLOT(slotEscapePreview())); connect(d->parent, SIGNAL(signalEscapePressed()), d->stackedview, SLOT(slotEscapePreview())); connect(d->parent, SIGNAL(signalNextItem()), this, SLOT(slotNextItem())); connect(d->parent, SIGNAL(signalPrevItem()), this, SLOT(slotPrevItem())); connect(d->parent, SIGNAL(signalFirstItem()), this, SLOT(slotFirstItem())); connect(d->parent, SIGNAL(signalLastItem()), this, SLOT(slotLastItem())); connect(d->parent, SIGNAL(signalCutAlbumItemsSelection()), d->iconView, SLOT(cut())); connect(d->parent, SIGNAL(signalCopyAlbumItemsSelection()), d->iconView, SLOT(copy())); connect(d->parent, SIGNAL(signalPasteAlbumItemsSelection()), this, SLOT(slotImagePaste())); // -- AlbumManager connections -------------------------------- connect(d->albumManager, SIGNAL(signalAlbumCurrentChanged(QList)), this, SLOT(slotAlbumSelected(QList))); connect(d->albumManager, SIGNAL(signalAllAlbumsLoaded()), this, SLOT(slotAllAlbumsLoaded())); connect(d->albumManager, SIGNAL(signalAlbumsCleared()), this, SLOT(slotAlbumsCleared())); // -- IconView Connections ------------------------------------- connect(d->iconView->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(slotImageSelected())); connect(d->iconView->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(slotImageSelected())); connect(d->iconView->model(), SIGNAL(layoutChanged()), this, SLOT(slotImageSelected())); connect(d->iconView, SIGNAL(selectionChanged()), this, SLOT(slotImageSelected())); connect(d->iconView, SIGNAL(previewRequested(ImageInfo)), this, SLOT(slotTogglePreviewMode(ImageInfo))); connect(d->iconView, SIGNAL(fullscreenRequested(ImageInfo)), this, SLOT(slotSlideShowManualFrom(ImageInfo))); connect(d->iconView, SIGNAL(zoomOutStep()), this, SLOT(slotZoomOut())); connect(d->iconView, SIGNAL(zoomInStep()), this, SLOT(slotZoomIn())); connect(d->iconView, SIGNAL(signalAddToExistingQueue(int)), this, SLOT(slotImageAddToExistingQueue(int))); connect(d->iconView, SIGNAL(signalShowContextMenu(QContextMenuEvent*, QList)), this, SLOT(slotShowContextMenu(QContextMenuEvent*,QList))); connect(d->iconView, SIGNAL(signalShowContextMenuOnInfo(QContextMenuEvent*,ImageInfo, QList,ImageFilterModel*)), this, SLOT(slotShowContextMenuOnInfo(QContextMenuEvent*,ImageInfo, QList,ImageFilterModel*))); // -- TableView Connections ----------------------------------- connect(d->tableView, SIGNAL(signalPreviewRequested(ImageInfo)), this, SLOT(slotTogglePreviewMode(ImageInfo))); connect(d->tableView, SIGNAL(signalZoomOutStep()), this, SLOT(slotZoomOut())); connect(d->tableView, SIGNAL(signalZoomInStep()), this, SLOT(slotZoomIn())); connect(d->tableView, SIGNAL(signalShowContextMenu(QContextMenuEvent*, QList)), this, SLOT(slotShowContextMenu(QContextMenuEvent*,QList))); connect(d->tableView, SIGNAL(signalShowContextMenuOnInfo(QContextMenuEvent*,ImageInfo, QList,ImageFilterModel*)), this, SLOT(slotShowContextMenuOnInfo(QContextMenuEvent*,ImageInfo, QList,ImageFilterModel*))); // TableView::signalItemsChanged is emitted when something changes in the model that // DigikamView should care about, not only the selection. connect(d->tableView, SIGNAL(signalItemsChanged()), this, SLOT(slotImageSelected())); // -- Trash View Connections ---------------------------------- connect(d->trashView, SIGNAL(selectionChanged()), this, SLOT(slotImageSelected())); // -- Sidebar Connections ------------------------------------- connect(d->leftSideBar, SIGNAL(signalChangedTab(QWidget*)), this, SLOT(slotLeftSidebarChangedTab(QWidget*))); connect(d->rightSideBar, SIGNAL(signalFirstItem()), this, SLOT(slotFirstItem())); connect(d->rightSideBar, SIGNAL(signalNextItem()), this, SLOT(slotNextItem())); connect(d->rightSideBar, SIGNAL(signalPrevItem()), this, SLOT(slotPrevItem())); connect(d->rightSideBar, SIGNAL(signalLastItem()), this, SLOT(slotLastItem())); connect(this, SIGNAL(signalNoCurrentItem()), d->rightSideBar, SLOT(slotNoCurrentItem())); #ifdef HAVE_MARBLE connect(d->gpsSearchSideBar, SIGNAL(signalMapSoloItems(QList,QString)), d->iconView->imageFilterModel(), SLOT(setIdWhitelist(QList,QString))); #endif // HAVE_MARBLE // -- Filter Bars Connections --------------------------------- ImageAlbumFilterModel* const model = d->iconView->imageAlbumFilterModel(); connect(d->filterWidget, SIGNAL(signalTagFilterChanged(QList,QList,ImageFilterSettings::MatchingCondition,bool,QList,QList)), d->iconView->imageFilterModel(), SLOT(setTagFilter(QList,QList,ImageFilterSettings::MatchingCondition,bool,QList,QList))); connect(d->filterWidget, SIGNAL(signalRatingFilterChanged(int,ImageFilterSettings::RatingCondition,bool)), model, SLOT(setRatingFilter(int,ImageFilterSettings::RatingCondition,bool))); connect(d->filterWidget, SIGNAL(signalSearchTextFilterChanged(SearchTextFilterSettings)), model, SLOT(setTextFilter(SearchTextFilterSettings))); connect(model, SIGNAL(filterMatchesForText(bool)), d->filterWidget, SLOT(slotFilterMatchesForText(bool))); connect(d->filterWidget, SIGNAL(signalMimeTypeFilterChanged(int)), model, SLOT(setMimeTypeFilter(int))); connect(d->filterWidget, SIGNAL(signalGeolocationFilterChanged(ImageFilterSettings::GeolocationCondition)), model, SLOT(setGeolocationFilter(ImageFilterSettings::GeolocationCondition))); // -- Preview image widget Connections ------------------------ connect(d->stackedview, SIGNAL(signalNextItem()), this, SLOT(slotNextItem())); connect(d->stackedview, SIGNAL(signalPrevItem()), this, SLOT(slotPrevItem())); connect(d->stackedview, SIGNAL(signalEditItem()), this, SLOT(slotImageEdit())); connect(d->stackedview, SIGNAL(signalDeleteItem()), this, SLOT(slotImageDelete())); connect(d->stackedview, SIGNAL(signalViewModeChanged()), this, SLOT(slotViewModeChanged())); connect(d->stackedview, SIGNAL(signalEscapePreview()), this, SLOT(slotEscapePreview())); connect(d->stackedview, SIGNAL(signalSlideShow()), this, SLOT(slotSlideShowAll())); connect(d->stackedview, SIGNAL(signalSlideShowCurrent()), this, SLOT(slotSlideShowManualFromCurrent())); connect(d->stackedview, SIGNAL(signalZoomFactorChanged(double)), this, SLOT(slotZoomFactorChanged(double))); connect(d->stackedview, SIGNAL(signalInsert2LightTable()), this, SLOT(slotImageAddToLightTable())); connect(d->stackedview, SIGNAL(signalInsert2QueueMgr()), this, SLOT(slotImageAddToCurrentQueue())); connect(d->stackedview, SIGNAL(signalFindSimilar()), this, SLOT(slotImageFindSimilar())); connect(d->stackedview, SIGNAL(signalAddToExistingQueue(int)), this, SLOT(slotImageAddToExistingQueue(int))); connect(d->stackedview, SIGNAL(signalGotoAlbumAndItem(ImageInfo)), this, SLOT(slotGotoAlbumAndItem(ImageInfo))); connect(d->stackedview, SIGNAL(signalGotoDateAndItem(ImageInfo)), this, SLOT(slotGotoDateAndItem(ImageInfo))); connect(d->stackedview, SIGNAL(signalGotoTagAndItem(int)), this, SLOT(slotGotoTagAndItem(int))); // -- FileActionMngr progress --------------- connect(FileActionMngr::instance(), SIGNAL(signalImageChangeFailed(QString,QStringList)), this, SLOT(slotImageChangeFailed(QString,QStringList))); // -- timers --------------- connect(d->selectionTimer, SIGNAL(timeout()), this, SLOT(slotDispatchImageSelected())); connect(d->thumbSizeTimer, SIGNAL(timeout()), this, SLOT(slotThumbSizeEffect()) ); // -- Album Settings ---------------- connect(ApplicationSettings::instance(), SIGNAL(setupChanged()), this, SLOT(slotSidebarTabTitleStyleChanged())); // -- Album History ----------------- connect(this, SIGNAL(signalAlbumSelected(Album*)), d->albumHistory, SLOT(slotAlbumSelected())); connect(this, SIGNAL(signalImageSelected(ImageInfoList,ImageInfoList)), d->albumHistory, SLOT(slotImageSelected(ImageInfoList))); connect(d->iconView, SIGNAL(currentChanged(ImageInfo)), d->albumHistory, SLOT(slotCurrentChange(ImageInfo))); connect(d->iconView->imageModel(), SIGNAL(imageInfosAdded(QList)), d->albumHistory, SLOT(slotAlbumCurrentChanged())); connect(d->albumHistory, SIGNAL(signalSetCurrent(qlonglong)), this, SLOT(slotSetCurrentWhenAvailable(qlonglong))); connect(d->albumHistory, SIGNAL(signalSetSelectedInfos(QList)), d->iconView, SLOT(setSelectedImageInfos(QList))); connect(d->albumManager, SIGNAL(signalAlbumDeleted(Album*)), d->albumHistory, SLOT(slotAlbumDeleted(Album*))); connect(d->albumManager, SIGNAL(signalAlbumsCleared()), d->albumHistory, SLOT(slotAlbumsCleared())); // -- Image versions ---------------- connect(d->rightSideBar->getFiltersHistoryTab(), SIGNAL(imageSelected(ImageInfo)), d->iconView, SLOT(hintAt(ImageInfo))); connect(d->rightSideBar->getFiltersHistoryTab(), SIGNAL(actionTriggered(ImageInfo)), this, SLOT(slotGotoAlbumAndItem(ImageInfo))); } void DigikamView::connectIconViewFilter(FilterStatusBar* const filterbar) { ImageAlbumFilterModel* const model = d->iconView->imageAlbumFilterModel(); connect(model, SIGNAL(filterMatches(bool)), filterbar, SLOT(slotFilterMatches(bool))); connect(model, SIGNAL(filterSettingsChanged(ImageFilterSettings)), filterbar, SLOT(slotFilterSettingsChanged(ImageFilterSettings))); connect(filterbar, SIGNAL(signalResetFilters()), d->filterWidget, SLOT(slotResetFilters())); connect(filterbar, SIGNAL(signalPopupFiltersView()), this, SLOT(slotPopupFiltersView())); } void DigikamView::slotPopupFiltersView() { d->rightSideBar->setActiveTab(d->filterWidget); d->filterWidget->setFocusToTextFilter(); } void DigikamView::loadViewState() { foreach(SidebarWidget* const widget, d->leftSideBarWidgets) { widget->loadState(); } d->filterWidget->loadState(); KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(QLatin1String("MainWindow")); // Restore the splitter d->splitter->restoreState(group); // Restore the thumbnail bar dock. QByteArray thumbbarState; thumbbarState = group.readEntry(QLatin1String("ThumbbarState"), thumbbarState); d->dockArea->restoreState(QByteArray::fromBase64(thumbbarState)); d->initialAlbumID = group.readEntry(QLatin1String("InitialAlbumID"), 0); #ifdef HAVE_MARBLE d->mapView->loadState(); #endif // HAVE_MARBLE d->tableView->loadState(); d->rightSideBar->loadState(); } void DigikamView::saveViewState() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(QLatin1String("MainWindow")); foreach(SidebarWidget* const widget, d->leftSideBarWidgets) { widget->saveState(); } d->filterWidget->saveState(); // Save the splitter states. d->splitter->saveState(group); // Save the position and size of the thumbnail bar. The thumbnail bar dock // needs to be closed explicitly, because when it is floating and visible // (when the user is in image preview mode) when the layout is saved, it // also reappears when restoring the view, while it should always be hidden. d->stackedview->thumbBarDock()->close(); group.writeEntry(QLatin1String("ThumbbarState"), d->dockArea->saveState().toBase64()); QList albumList = AlbumManager::instance()->currentAlbums(); Album* album = 0; if(!albumList.isEmpty()) { album = albumList.first(); } if (album) { group.writeEntry(QLatin1String("InitialAlbumID"), album->globalID()); } else { group.writeEntry(QLatin1String("InitialAlbumID"), 0); } #ifdef HAVE_MARBLE d->mapView->saveState(); #endif // HAVE_MARBLE d->tableView->saveState(); d->rightSideBar->saveState(); } QList DigikamView::leftSidebarWidgets() const { return d->leftSideBarWidgets; } QList DigikamView::allUrls(bool grouping) const { /// @todo This functions seems not to be used anywhere right now switch (viewMode()) { case StackedView::TableViewMode: return d->tableView->allUrls(grouping); default: return d->iconView->allUrls(grouping); } } QList DigikamView::selectedUrls(bool grouping) const { switch (viewMode()) { case StackedView::TableViewMode: return d->tableView->selectedUrls(grouping); default: return d->iconView->selectedUrls(grouping); } } QList DigikamView::selectedUrls(const ApplicationSettings::OperationType type) const { return selectedUrls(needGroupResolving(type)); } void DigikamView::showSideBars() { d->leftSideBar->restore(); d->rightSideBar->restore(); } void DigikamView::hideSideBars() { d->leftSideBar->backup(); d->rightSideBar->backup(); } void DigikamView::toggleLeftSidebar() { d->leftSideBar->isExpanded() ? d->leftSideBar->shrink() : d->leftSideBar->expand(); } void DigikamView::toggleRightSidebar() { d->rightSideBar->isExpanded() ? d->rightSideBar->shrink() : d->rightSideBar->expand(); } void DigikamView::previousLeftSideBarTab() { d->leftSideBar->activePreviousTab(); } void DigikamView::nextLeftSideBarTab() { d->leftSideBar->activeNextTab(); } void DigikamView::previousRightSideBarTab() { d->rightSideBar->activePreviousTab(); } void DigikamView::nextRightSideBarTab() { d->rightSideBar->activeNextTab(); } void DigikamView::slotFirstItem() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotGoToRow(0, false); break; default: // all other views are tied to IconView's selection model d->iconView->toFirstIndex(); } } void DigikamView::slotPrevItem() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotGoToRow(-1, true); break; default: // all other views are tied to IconView's selection model d->iconView->toPreviousIndex(); } } void DigikamView::slotNextItem() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotGoToRow(1, true); break; default: // all other views are tied to IconView's selection model d->iconView->toNextIndex(); } } void DigikamView::slotLastItem() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotGoToRow(-1, false); break; default: // all other views are tied to IconView's selection model d->iconView->toLastIndex(); } } void DigikamView::slotSelectItemByUrl(const QUrl& url) { /// @todo This functions seems not to be used anywhere right now /// @todo Adapt to TableView d->iconView->toIndex(url); } void DigikamView::slotAllAlbumsLoaded() { disconnect(d->albumManager, SIGNAL(signalAllAlbumsLoaded()), this, SLOT(slotAllAlbumsLoaded())); loadViewState(); d->leftSideBar->loadState(); d->rightSideBar->loadState(); d->rightSideBar->populateTags(); // now that all albums have been loaded, activate the albumHistory d->useAlbumHistory = true; Album* const album = d->albumManager->findAlbum(d->initialAlbumID); d->albumManager->setCurrentAlbums(QList() << album); } void DigikamView::slotSortAlbums(int role) { ApplicationSettings* const settings = ApplicationSettings::instance(); if (!settings) { return; } settings->setAlbumSortRole((ApplicationSettings::AlbumSortRole) role); settings->saveSettings(); //A dummy way to force the tree view to resort if the album sort role changed PAlbum* const albumBeforeSorting = d->albumFolderSideBar->currentAlbum(); settings->setAlbumSortChanged(true); d->albumFolderSideBar->doSaveState(); d->albumFolderSideBar->doLoadState(); d->albumFolderSideBar->doSaveState(); d->albumFolderSideBar->doLoadState(); settings->setAlbumSortChanged(false); if (d->leftSideBar->getActiveTab() == d->albumFolderSideBar) { d->albumFolderSideBar->setCurrentAlbum(albumBeforeSorting); } } void DigikamView::slotNewAlbum() { // TODO use the selection model of the view instead d->albumModificationHelper->slotAlbumNew(d->albumFolderSideBar->currentAlbum()); } void DigikamView::slotDeleteAlbum() { d->albumModificationHelper->slotAlbumDelete(d->albumFolderSideBar->currentAlbum()); } void DigikamView::slotRenameAlbum() { d->albumModificationHelper->slotAlbumRename(d->albumFolderSideBar->currentAlbum()); } void DigikamView::slotNewTag() { QList talbums = AlbumManager::instance()->currentTAlbums(); if (!talbums.isEmpty()) d->tagModificationHelper->slotTagNew(talbums.first()); } void DigikamView::slotDeleteTag() { QList talbums = AlbumManager::instance()->currentTAlbums(); if (!talbums.isEmpty()) d->tagModificationHelper->slotTagDelete(talbums.first()); } void DigikamView::slotEditTag() { QList talbums = AlbumManager::instance()->currentTAlbums(); if (!talbums.isEmpty()) d->tagModificationHelper->slotTagEdit(talbums.first()); } void DigikamView::slotOpenTagsManager() { TagsManager* const tagMngr = TagsManager::instance(); tagMngr->show(); tagMngr->activateWindow(); tagMngr->raise(); } void DigikamView::slotAssignTag() { d->rightSideBar->setActiveTab(d->rightSideBar->imageDescEditTab()); d->rightSideBar->imageDescEditTab()->setFocusToNewTagEdit(); } void DigikamView::slotNewKeywordSearch() { slotLeftSideBarActivate(d->searchSideBar); d->searchSideBar->newKeywordSearch(); } void DigikamView::slotNewAdvancedSearch() { slotLeftSideBarActivate(d->searchSideBar); d->searchSideBar->newAdvancedSearch(); } void DigikamView::slotNewDuplicatesSearch(PAlbum* album) { slotLeftSideBarActivate(d->fuzzySearchSideBar); d->fuzzySearchSideBar->newDuplicatesSearch(album); } void DigikamView::slotNewDuplicatesSearch(QList albums) { slotLeftSideBarActivate(d->fuzzySearchSideBar); d->fuzzySearchSideBar->newDuplicatesSearch(albums); } void DigikamView::slotNewDuplicatesSearch(QList albums) { slotLeftSideBarActivate(d->fuzzySearchSideBar); d->fuzzySearchSideBar->newDuplicatesSearch(albums); } void DigikamView::slotAlbumsCleared() { emit signalAlbumSelected(0); } void DigikamView::slotAlbumHistoryBack(int steps) { QList albums; QWidget* widget = 0; d->albumHistory->back(albums, &widget, steps); changeAlbumFromHistory(albums, widget); } void DigikamView::slotAlbumHistoryForward(int steps) { QList albums; QWidget* widget = 0; d->albumHistory->forward(albums, &widget, steps); changeAlbumFromHistory(albums , widget); } // TODO update, use SideBarWidget instead of QWidget void DigikamView::changeAlbumFromHistory(QList album, QWidget* const widget) { if (!(album.isEmpty()) && widget) { // TODO update, temporary casting until signature is changed SidebarWidget* const sideBarWidget = dynamic_cast(widget); if (sideBarWidget) { sideBarWidget->changeAlbumFromHistory(album); slotLeftSideBarActivate(sideBarWidget); if (sideBarWidget == d->labelsSideBar) { d->labelsSearchHandler->restoreSelectionFromHistory(d->albumHistory->neededLabels()); } } d->parent->enableAlbumBackwardHistory(d->useAlbumHistory && !d->albumHistory->isBackwardEmpty()); d->parent->enableAlbumForwardHistory(d->useAlbumHistory && !d->albumHistory->isForwardEmpty()); } } void DigikamView::clearHistory() { d->albumHistory->clearHistory(); d->parent->enableAlbumBackwardHistory(false); d->parent->enableAlbumForwardHistory(false); } void DigikamView::getBackwardHistory(QStringList& titles) { d->albumHistory->getBackwardHistory(titles); for (int i = 0; i < titles.size(); ++i) { titles[i] = d->userPresentableAlbumTitle(titles.at(i)); } } void DigikamView::getForwardHistory(QStringList& titles) { d->albumHistory->getForwardHistory(titles); for (int i = 0; i < titles.size(); ++i) { titles[i] = d->userPresentableAlbumTitle(titles.at(i)); } } void DigikamView::slotGotoAlbumAndItem(const ImageInfo& imageInfo) { qCDebug(DIGIKAM_GENERAL_LOG) << "going to " << imageInfo; emit signalNoCurrentItem(); PAlbum* const album = AlbumManager::instance()->findPAlbum(imageInfo.albumId()); d->albumFolderSideBar->setCurrentAlbum(album); slotLeftSideBarActivate(d->albumFolderSideBar); // Set the activate item url to find in the Album View after // all items have be reloaded. slotSetCurrentWhenAvailable(imageInfo.id()); // And finally toggle album manager to handle album history and // reload all items. d->albumManager->setCurrentAlbums(QList() << album); } void DigikamView::slotGotoDateAndItem(const ImageInfo& imageInfo) { QDate date = imageInfo.dateTime().date(); emit signalNoCurrentItem(); // Change to Date Album view. // Note, that this also opens the side bar if it is closed; this is // considered as a feature, because it highlights that the view was changed. slotLeftSideBarActivate(d->dateViewSideBar); // Set the activate item url to find in the Album View after // all items have be reloaded. slotSetCurrentWhenAvailable(imageInfo.id()); // Change the year and month of the iconItem (day is unused). d->dateViewSideBar->gotoDate(date); } void DigikamView::slotGotoTagAndItem(int tagID) { // FIXME: Arnd: don't know yet how to get the iconItem passed through ... // then we would know how to use the following ... // KURL url( iconItem->imageInfo()->kurl() ); // url.cleanPath(); emit signalNoCurrentItem(); // Change to Tag Folder view. // Note, that this also opens the side bar if it is closed; this is // considered as a feature, because it highlights that the view was changed. slotLeftSideBarActivate(d->tagViewSideBar); // Set the current tag in the tag folder view. // TODO this slot should use a TAlbum pointer directly TAlbum* const tag = AlbumManager::instance()->findTAlbum(tagID); if (tag) { d->tagViewSideBar->setCurrentAlbum(tag); } else { qCDebug(DIGIKAM_GENERAL_LOG) << "Could not find a tag album for tag id " << tagID; } // Set the activate item url to find in the Tag View after // all items have be reloaded. // FIXME: see above // d->iconView->setAlbumItemToFind(url); } void DigikamView::slotSelectAlbum(const QUrl& url) { PAlbum* const album = d->albumManager->findPAlbum(url); if (!album) { qCWarning(DIGIKAM_GENERAL_LOG) << "Unable to find album for " << url; return; } slotLeftSideBarActivate(d->albumFolderSideBar); d->albumFolderSideBar->setCurrentAlbum(album); } void DigikamView::slotAlbumSelected(QList albums) { emit signalNoCurrentItem(); emit signalAlbumSelected(0); if (albums.isEmpty() || !albums.first()) { d->iconView->openAlbum(QList()); #ifdef HAVE_MARBLE d->mapView->openAlbum(0); #endif // HAVE_MARBLE slotTogglePreviewMode(ImageInfo()); return; } Album* const album = albums.first(); emit signalAlbumSelected(album); if (d->useAlbumHistory && !d->labelsSearchHandler->isRestoringSelectionFromHistory()) { if (!(d->leftSideBar->getActiveTab() == d->labelsSideBar)) { d->albumHistory->addAlbums(albums, d->leftSideBar->getActiveTab()); } else { if (albums.first()->isUsedByLabelsTree()) { d->albumHistory->addAlbums(albums, d->leftSideBar->getActiveTab(), d->labelsSideBar->selectedLabels()); } } } d->parent->enableAlbumBackwardHistory(d->useAlbumHistory && !d->albumHistory->isBackwardEmpty()); d->parent->enableAlbumForwardHistory(d->useAlbumHistory && !d->albumHistory->isForwardEmpty()); d->iconView->openAlbum(albums); if (album->isRoot()) { d->stackedview->setViewMode(StackedView::WelcomePageMode); } else if (album->isTrashAlbum()) { PAlbum* const palbum = d->albumManager->findPAlbum(album->parent()->id()); if (palbum) { QUrl url = palbum->fileUrl(); url = url.adjusted(QUrl::StripTrailingSlash); d->trashView->model()->loadItemsForCollection(url.toLocalFile()); d->filterWidget->setEnabled(false); d->stackedview->setViewMode(StackedView::TrashViewMode); } } else { switch (viewMode()) { case StackedView::PreviewImageMode: case StackedView::MediaPlayerMode: case StackedView::WelcomePageMode: case StackedView::TrashViewMode: slotTogglePreviewMode(ImageInfo()); break; default: break; } d->filterWidget->setEnabled(true); } } void DigikamView::slotAlbumOpenInFileManager() { Album* const album = d->albumManager->currentAlbums().first(); if (!album || album->type() != Album::PHYSICAL) { return; } if (album->isRoot()) { QMessageBox::critical(this, qApp->applicationName(), i18n("Cannot open the root album. It is not a physical location.")); return; } PAlbum* const palbum = dynamic_cast(album); if (palbum) { QDesktopServices::openUrl(QUrl::fromLocalFile(palbum->folderPath())); } } void DigikamView::slotRefresh() { switch (viewMode()) { case StackedView::PreviewImageMode: d->stackedview->imagePreviewView()->reload(); break; #ifdef HAVE_MEDIAPLAYER case StackedView::MediaPlayerMode: d->stackedview->mediaPlayerView()->reload(); break; #endif //HAVE_MEDIAPLAYER default: Album* const album = currentAlbum(); if (!album) return; // force reloading of thumbnails LoadingCacheInterface::cleanThumbnailCache(); ThumbsGenerator* const tool = new ThumbsGenerator(true, album->id()); tool->start(); // if physical album, schedule a collection scan of current album's path if (album->type() == Album::PHYSICAL) { NewItemsFinder* const tool = new NewItemsFinder(NewItemsFinder::ScheduleCollectionScan, QStringList() << static_cast(album)->folderPath()); connect(tool, SIGNAL(signalComplete()), this, SLOT(slotAlbumRefreshComplete())); tool->start(); } break; } } void DigikamView::slotAlbumRefreshComplete() { // force reload. Should normally not be necessary, but we may have bugs qlonglong currentId = currentInfo().id(); d->iconView->imageAlbumModel()->refresh(); if (currentId != -1) { slotSetCurrentWhenAvailable(currentId); } } void DigikamView::slotImageSelected() { // delay to slotDispatchImageSelected d->needDispatchSelection = true; d->selectionTimer->start(); switch (viewMode()) { case StackedView::TableViewMode: emit signalSelectionChanged(d->tableView->numberOfSelectedItems()); break; default: emit signalSelectionChanged(d->iconView->numberOfSelectedIndexes()); } } void DigikamView::slotDispatchImageSelected() { if (viewMode() == StackedView::TrashViewMode) { d->rightSideBar->itemChanged(d->trashView->lastSelectedItemUrl()); return; } if (d->needDispatchSelection) { // the list of ImageInfos of currently selected items, currentItem first const ImageInfoList list = selectedInfoList(true, true); const ImageInfoList allImages = allInfo(true); if (list.isEmpty()) { d->stackedview->setPreviewItem(); emit signalImageSelected(list, allImages); emit signalNoCurrentItem(); } else { d->rightSideBar->itemChanged(list); ImageInfo previousInfo; ImageInfo nextInfo; if (viewMode() == StackedView::TableViewMode) { previousInfo = d->tableView->previousInfo(); nextInfo = d->tableView->nextInfo(); } else { previousInfo = d->iconView->previousInfo(list.first()); nextInfo = d->iconView->nextInfo(list.first()); } if ((viewMode() != StackedView::IconViewMode) && (viewMode() != StackedView::MapWidgetMode) && (viewMode() != StackedView::TableViewMode) ) { d->stackedview->setPreviewItem(list.first(), previousInfo, nextInfo); } emit signalImageSelected(list, allImages); } d->needDispatchSelection = false; } } double DigikamView::zoomMin() const { return d->stackedview->zoomMin(); } double DigikamView::zoomMax() const { return d->stackedview->zoomMax(); } void DigikamView::setZoomFactor(double zoom) { d->stackedview->setZoomFactorSnapped(zoom); } void DigikamView::slotZoomFactorChanged(double zoom) { toggleZoomActions(); emit signalZoomChanged(zoom); } void DigikamView::setThumbSize(int size) { if (viewMode() == StackedView::PreviewImageMode) { double z = DZoomBar::zoomFromSize(size, zoomMin(), zoomMax()); setZoomFactor(z); } else if ( (viewMode() == StackedView::IconViewMode) || (viewMode() == StackedView::TableViewMode) || (viewMode() == StackedView::TrashViewMode)) { if (size > ThumbnailSize::maxThumbsSize()) { d->thumbSize = ThumbnailSize::maxThumbsSize(); } else if (size < ThumbnailSize::Small) { d->thumbSize = ThumbnailSize::Small; } else { d->thumbSize = size; } emit signalThumbSizeChanged(d->thumbSize); d->thumbSizeTimer->start(); } } void DigikamView::slotThumbSizeEffect() { d->iconView->setThumbnailSize(d->thumbSize); d->tableView->setThumbnailSize(d->thumbSize); d->trashView->setThumbnailSize(d->thumbSize); toggleZoomActions(); ApplicationSettings::instance()->setDefaultIconSize(d->thumbSize); } void DigikamView::toggleZoomActions() { if (viewMode() == StackedView::PreviewImageMode) { d->parent->enableZoomMinusAction(true); d->parent->enableZoomPlusAction(true); if (d->stackedview->maxZoom()) { d->parent->enableZoomPlusAction(false); } if (d->stackedview->minZoom()) { d->parent->enableZoomMinusAction(false); } } else if ( (viewMode() == StackedView::IconViewMode) || (viewMode() == StackedView::TableViewMode) ) { d->parent->enableZoomMinusAction(true); d->parent->enableZoomPlusAction(true); if (d->thumbSize >= ThumbnailSize::maxThumbsSize()) { d->parent->enableZoomPlusAction(false); } if (d->thumbSize <= ThumbnailSize::Small) { d->parent->enableZoomMinusAction(false); } } else { d->parent->enableZoomMinusAction(false); d->parent->enableZoomPlusAction(false); } } void DigikamView::slotZoomIn() { if ( (viewMode() == StackedView::IconViewMode) || (viewMode() == StackedView::TableViewMode) ) { setThumbSize(d->thumbSize + ThumbnailSize::Step); toggleZoomActions(); emit signalThumbSizeChanged(d->thumbSize); } else if (viewMode() == StackedView::PreviewImageMode) { d->stackedview->increaseZoom(); } } void DigikamView::slotZoomOut() { if ( (viewMode() == StackedView::IconViewMode) || (viewMode() == StackedView::TableViewMode) ) { setThumbSize(d->thumbSize - ThumbnailSize::Step); toggleZoomActions(); emit signalThumbSizeChanged(d->thumbSize); } else if (viewMode() == StackedView::PreviewImageMode) { d->stackedview->decreaseZoom(); } } void DigikamView::slotZoomTo100Percents() { if (viewMode() == StackedView::PreviewImageMode) { d->stackedview->toggleFitToWindowOr100(); } } void DigikamView::slotFitToWindow() { if (viewMode() == StackedView::TableViewMode) { /// @todo We should choose an appropriate thumbnail size here } else if (viewMode() == StackedView::IconViewMode) { int nts = d->iconView->fitToWidthIcons(); qCDebug(DIGIKAM_GENERAL_LOG) << "new thumb size = " << nts; setThumbSize(nts); toggleZoomActions(); emit signalThumbSizeChanged(d->thumbSize); } else if (viewMode() == StackedView::PreviewImageMode) { d->stackedview->fitToWindow(); } } void DigikamView::slotAlbumPropsEdit() { d->albumModificationHelper->slotAlbumEdit(d->albumManager->currentPAlbum()); } void DigikamView::slotAlbumWriteMetadata() { Album* const album = d->albumManager->currentAlbums().first(); if (!album) { return; } MetadataSynchronizer* const tool = new MetadataSynchronizer(AlbumList() << album, MetadataSynchronizer::WriteFromDatabaseToFile); tool->start(); } void DigikamView::slotAlbumReadMetadata() { Album* const album = d->albumManager->currentAlbums().first(); if (!album) { return; } MetadataSynchronizer* const tool = new MetadataSynchronizer(AlbumList() << album, MetadataSynchronizer::ReadFromFileToDatabase); tool->start(); } void DigikamView::slotImageWriteMetadata() { const ImageInfoList selected = selectedInfoList(ApplicationSettings::Metadata); MetadataSynchronizer* const tool = new MetadataSynchronizer(selected, MetadataSynchronizer::WriteFromDatabaseToFile); tool->start(); } void DigikamView::slotImageReadMetadata() { const ImageInfoList selected = selectedInfoList(ApplicationSettings::Metadata); MetadataSynchronizer* const tool = new MetadataSynchronizer(selected, MetadataSynchronizer::ReadFromFileToDatabase); tool->start(); } // ---------------------------------------------------------------- void DigikamView::slotEscapePreview() { if (viewMode() == StackedView::IconViewMode || viewMode() == StackedView::MapWidgetMode || viewMode() == StackedView::TableViewMode || viewMode() == StackedView::WelcomePageMode) { return; } // pass a null image info, because we want to fall back to the old // view mode slotTogglePreviewMode(ImageInfo()); } void DigikamView::slotMapWidgetView() { d->stackedview->setViewMode(StackedView::MapWidgetMode); } void DigikamView::slotTableView() { d->stackedview->setViewMode(StackedView::TableViewMode); } void DigikamView::slotIconView() { if (viewMode() == StackedView::PreviewImageMode) { emit signalThumbSizeChanged(d->iconView->thumbnailSize().size()); } // and switch to icon view d->stackedview->setViewMode(StackedView::IconViewMode); // make sure the next/previous buttons are updated slotImageSelected(); } void DigikamView::slotImagePreview() { slotTogglePreviewMode(currentInfo()); } /** * @brief This method toggles between AlbumView/MapWidgetView and ImagePreview modes, depending on the context. */ void DigikamView::slotTogglePreviewMode(const ImageInfo& info) { if ( (viewMode() == StackedView::IconViewMode || viewMode() == StackedView::TableViewMode || viewMode() == StackedView::MapWidgetMode) && !info.isNull() ) { if (info.isLocationAvailable()) { d->lastViewMode = viewMode(); if (viewMode() == StackedView::IconViewMode) { d->stackedview->setPreviewItem(info, d->iconView->previousInfo(info), d->iconView->nextInfo(info)); } else { d->stackedview->setPreviewItem(info, ImageInfo(), ImageInfo()); } } else { QModelIndex index = d->iconView->indexForInfo(info); d->iconView->showIndexNotification(index, i18nc("@info", "The storage location of this image
is currently not available
")); } } else { // go back to either AlbumViewMode or MapWidgetMode d->stackedview->setViewMode( d->lastViewMode ); } // make sure the next/previous buttons are updated slotImageSelected(); } void DigikamView::slotViewModeChanged() { toggleZoomActions(); switch (viewMode()) { case StackedView::IconViewMode: emit signalSwitchedToIconView(); emit signalThumbSizeChanged(d->iconView->thumbnailSize().size()); break; case StackedView::PreviewImageMode: emit signalSwitchedToPreview(); slotZoomFactorChanged(d->stackedview->zoomFactor()); break; case StackedView::WelcomePageMode: emit signalSwitchedToIconView(); break; case StackedView::MediaPlayerMode: emit signalSwitchedToPreview(); break; case StackedView::MapWidgetMode: emit signalSwitchedToMapView(); //TODO: connect map view's zoom buttons to main status bar zoom buttons break; case StackedView::TableViewMode: emit signalSwitchedToTableView(); emit signalThumbSizeChanged(d->trashView->getThumbnailSize().size()); break; case StackedView::TrashViewMode: emit signalSwitchedToTrashView(); break; } } void DigikamView::slotImageFindSimilar() { const ImageInfo current = currentInfo(); if (!current.isNull()) { d->fuzzySearchSideBar->newSimilarSearch(current); slotLeftSideBarActivate(d->fuzzySearchSideBar); } } void DigikamView::slotEditor() { const ImageInfoList imageInfoList = selectedInfoList(ApplicationSettings::Tools); ImageInfo singleInfo = currentInfo(); if (singleInfo.isNull() && !imageInfoList.isEmpty()) { singleInfo = imageInfoList.first(); } Album* const current = currentAlbum(); d->utilities->openInfos(singleInfo, imageInfoList, current); } void DigikamView::slotFileWithDefaultApplication() { d->utilities->openInfosWithDefaultApplication(selectedInfoList(ApplicationSettings::Tools)); } void DigikamView::slotLightTable() { bool grouping = needGroupResolving(ApplicationSettings::LightTable); const ImageInfoList selectedList = selectedInfoList(false, grouping); if (selectedList.isEmpty()) { grouping = needGroupResolving(ApplicationSettings::LightTable, true); } const ImageInfoList allInfoList = allInfo(grouping); const ImageInfo currentImageInfo = currentInfo(); d->utilities->insertToLightTableAuto(allInfoList, selectedList, currentImageInfo); } void DigikamView::slotQueueMgr() { bool grouping = needGroupResolving(ApplicationSettings::BQM); ImageInfoList imageInfoList = selectedInfoList(false, grouping); ImageInfo singleInfo = currentInfo(); if (singleInfo.isNull() && !imageInfoList.isEmpty()) { singleInfo = imageInfoList.first(); } if (singleInfo.isNull()) { grouping = needGroupResolving(ApplicationSettings::BQM, true); const ImageInfoList allItems = allInfo(grouping); if (!allItems.isEmpty()) { singleInfo = allItems.first(); } } d->utilities->insertToQueueManager(imageInfoList, singleInfo, true); } void DigikamView::slotImageEdit() { // Where is the difference to slotEditor? slotEditor(); } void DigikamView::slotImageLightTable() { const ImageInfoList selectedList = selectedInfoList(ApplicationSettings::LightTable); const ImageInfo currentImageInfo = currentInfo(); // replace images in light table d->utilities->insertToLightTable(selectedList, currentImageInfo, false); } void DigikamView::slotImageAddToLightTable() { const ImageInfoList selectedList = selectedInfoList(ApplicationSettings::LightTable); const ImageInfo currentImageInfo = currentInfo(); // add to images in light table d->utilities->insertToLightTable(selectedList, currentImageInfo, true); } void DigikamView::slotImageAddToCurrentQueue() { const ImageInfoList selectedList = selectedInfoList(ApplicationSettings::BQM); const ImageInfo currentImageInfo = currentInfo(); d->utilities->insertToQueueManager(selectedList, currentImageInfo, false); } void DigikamView::slotImageAddToNewQueue() { const bool newQueue = QueueMgrWindow::queueManagerWindowCreated() && !QueueMgrWindow::queueManagerWindow()->queuesMap().isEmpty(); const ImageInfoList selectedList = selectedInfoList(ApplicationSettings::BQM); const ImageInfo currentImageInfo = currentInfo(); d->utilities->insertToQueueManager(selectedList, currentImageInfo, newQueue); } void DigikamView::slotImageAddToExistingQueue(int queueid) { const ImageInfoList selectedList = selectedInfoList(ApplicationSettings::BQM); const ImageInfo currentImageInfo = currentInfo(); if (!selectedList.isEmpty()) { d->utilities->insertSilentToQueueManager(selectedList, currentImageInfo, queueid); } } void DigikamView::slotImageRename() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->rename(); break; default: d->iconView->rename(); } } void DigikamView::slotImageDelete() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotDeleteSelected(ImageViewUtilities::DeleteUseTrash); break; default: d->iconView->deleteSelected(ImageViewUtilities::DeleteUseTrash); } } void DigikamView::slotImageDeletePermanently() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotDeleteSelected(ImageViewUtilities::DeletePermanently); break; default: d->iconView->deleteSelected(ImageViewUtilities::DeletePermanently); } } void DigikamView::slotImageDeletePermanentlyDirectly() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotDeleteSelectedWithoutConfirmation(ImageViewUtilities::DeletePermanently); break; default: d->iconView->deleteSelectedDirectly(ImageViewUtilities::DeletePermanently); } } void DigikamView::slotImageTrashDirectly() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotDeleteSelectedWithoutConfirmation(ImageViewUtilities::DeleteUseTrash); break; default: d->iconView->deleteSelectedDirectly(ImageViewUtilities::DeleteUseTrash); } } void DigikamView::slotSelectAll() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->selectAll(); break; default: d->iconView->selectAll(); } } void DigikamView::slotSelectNone() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->clearSelection(); break; default: d->iconView->clearSelection(); } } void DigikamView::slotSelectInvert() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->invertSelection(); break; default: d->iconView->invertSelection(); } } void DigikamView::slotSortImages(int sortRole) { ApplicationSettings* const settings = ApplicationSettings::instance(); if (!settings) { return; } settings->setImageSortOrder(sortRole); d->iconView->imageFilterModel()->setSortRole((ImageSortSettings::SortRole) sortRole); settings->emitSetupChanged(); } void DigikamView::slotSortImagesOrder(int order) { ApplicationSettings* const settings = ApplicationSettings::instance(); if (!settings) { return; } settings->setImageSorting(order); d->iconView->imageFilterModel()->setSortOrder((ImageSortSettings::SortOrder) order); settings->emitSetupChanged(); } void DigikamView::slotSeparateImages(int categoryMode) { ApplicationSettings* const settings = ApplicationSettings::instance(); if (!settings) { return; } settings->setImageSeparationMode(categoryMode); d->iconView->imageFilterModel()->setCategorizationMode((ImageSortSettings::CategorizationMode) categoryMode); } void DigikamView::slotImageSeparationSortOrder(int order) { ApplicationSettings* const settings = ApplicationSettings::instance(); if (!settings) { return; } settings->setImageSeparationSortOrder(order); d->iconView->imageFilterModel()->setCategorizationSortOrder((ImageSortSettings::SortOrder) order); } void DigikamView::slotMoveSelectionToAlbum() { d->utilities->createNewAlbumForInfos(selectedInfoList(false, true), currentAlbum()); } void DigikamView::slotImagePaste() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotPaste(); break; default: d->iconView->paste(); } } void DigikamView::slotLeftSidebarChangedTab(QWidget* w) { // TODO update, temporary cast SidebarWidget* const widget = dynamic_cast(w); foreach(SidebarWidget* const sideBarWidget, d->leftSideBarWidgets) { bool active = (widget && (widget == sideBarWidget)); sideBarWidget->setActive(active); } } void DigikamView::toggleTag(int tagID) { ImageInfoList tagToRemove, tagToAssign; const ImageInfoList selectedList = selectedInfoList(ApplicationSettings::Metadata); foreach(const ImageInfo& info, selectedList) { if (info.tagIds().contains(tagID)) tagToRemove.append(info); else tagToAssign.append(info); } FileActionMngr::instance()->assignTag(tagToAssign, tagID); FileActionMngr::instance()->removeTag(tagToRemove, tagID); } void DigikamView::slotAssignPickLabel(int pickId) { FileActionMngr::instance()->assignPickLabel(selectedInfoList(ApplicationSettings::Metadata), pickId); } void DigikamView::slotAssignColorLabel(int colorId) { FileActionMngr::instance()->assignColorLabel(selectedInfoList(ApplicationSettings::Metadata), colorId); } void DigikamView::slotAssignRating(int rating) { FileActionMngr::instance()->assignRating(selectedInfoList(ApplicationSettings::Metadata), rating); } void DigikamView::slotAssignTag(int tagID) { FileActionMngr::instance()->assignTags(selectedInfoList(ApplicationSettings::Metadata), QList() << tagID); } void DigikamView::slotRemoveTag(int tagID) { FileActionMngr::instance()->removeTags(selectedInfoList(ApplicationSettings::Metadata), QList() << tagID); } void DigikamView::slotSlideShowAll() { slideShow(allInfo(ApplicationSettings::Slideshow)); } void DigikamView::slotSlideShowSelection() { slideShow(selectedInfoList(ApplicationSettings::Slideshow)); } void DigikamView::slotSlideShowRecursive() { QList albumList = AlbumManager::instance()->currentAlbums(); Album* album = 0; if (!albumList.isEmpty()) { album = albumList.first(); } if (album) { SlideShowBuilder* const builder = new SlideShowBuilder(album); connect(builder, SIGNAL(signalComplete(SlideShowSettings)), this, SLOT(slotSlideShowBuilderComplete(SlideShowSettings))); builder->run(); } } void DigikamView::slotSlideShowManualFromCurrent() { slotSlideShowManualFrom(currentInfo()); } void DigikamView::slotSlideShowManualFrom(const ImageInfo& info) { SlideShowBuilder* const builder = new SlideShowBuilder(allInfo(ApplicationSettings::Slideshow)); builder->setOverrideStartFrom(info); builder->setAutoPlayEnabled(false); connect(builder, SIGNAL(signalComplete(SlideShowSettings)), this, SLOT(slotSlideShowBuilderComplete(SlideShowSettings))); builder->run(); } void DigikamView::presentation() { PresentationMngr* const mngr = new PresentationMngr(this); foreach(const ImageInfo& info, selectedInfoList(ApplicationSettings::Slideshow)) { mngr->addFile(info.fileUrl(), info.comment()); qApp->processEvents(); } mngr->showConfigDialog(); } void DigikamView::slideShow(const ImageInfoList& infoList) { SlideShowBuilder* const builder = new SlideShowBuilder(infoList); connect(builder, SIGNAL(signalComplete(SlideShowSettings)), this, SLOT(slotSlideShowBuilderComplete(SlideShowSettings))); builder->run(); } void DigikamView::slotSlideShowBuilderComplete(const SlideShowSettings& settings) { SlideShow* const slide = new SlideShow(settings); TagsActionMngr::defaultManager()->registerActionsToWidget(slide); if (settings.imageUrl.isValid()) { slide->setCurrentItem(settings.imageUrl); } else if (settings.startWithCurrent) { slide->setCurrentItem(currentInfo().fileUrl()); } connect(slide, SIGNAL(signalRatingChanged(QUrl,int)), this, SLOT(slotRatingChanged(QUrl,int))); connect(slide, SIGNAL(signalColorLabelChanged(QUrl,int)), this, SLOT(slotColorLabelChanged(QUrl,int))); connect(slide, SIGNAL(signalPickLabelChanged(QUrl,int)), this, SLOT(slotPickLabelChanged(QUrl,int))); connect(slide, SIGNAL(signalToggleTag(QUrl,int)), this, SLOT(slotToggleTag(QUrl,int))); connect(slide, SIGNAL(signalLastItemUrl(QUrl)), d->iconView, SLOT(setCurrentUrl(QUrl))); slide->show(); } void DigikamView::toggleShowBar(bool b) { d->stackedview->thumbBarDock()->showThumbBar(b); // See bug #319876 : force to reload current view mode to set thumbbar visibility properly. d->stackedview->setViewMode(viewMode()); } void DigikamView::setRecurseAlbums(bool recursive) { d->iconView->imageAlbumModel()->setRecurseAlbums(recursive); } void DigikamView::setRecurseTags(bool recursive) { d->iconView->imageAlbumModel()->setRecurseTags(recursive); } void DigikamView::slotSidebarTabTitleStyleChanged() { d->leftSideBar->setStyle(ApplicationSettings::instance()->getSidebarTitleStyle()); d->rightSideBar->setStyle(ApplicationSettings::instance()->getSidebarTitleStyle()); /// @todo Which settings actually have to be reloaded? // d->rightSideBar->applySettings(); } void DigikamView::slotImageChangeFailed(const QString& message, const QStringList& fileNames) { if (fileNames.isEmpty()) { return; } DMessageBox::showInformationList(QMessageBox::Critical, qApp->activeWindow(), qApp->applicationName(), message, fileNames); } void DigikamView::slotLeftSideBarActivateAlbums() { d->leftSideBar->setActiveTab(d->albumFolderSideBar); } void DigikamView::slotLeftSideBarActivateTags() { d->leftSideBar->setActiveTab(d->tagViewSideBar); } void DigikamView::slotLeftSideBarActivate(SidebarWidget* widget) { d->leftSideBar->setActiveTab(widget); } void DigikamView::slotLeftSideBarActivate(QWidget* widget) { slotLeftSideBarActivate(static_cast(widget)); } void DigikamView::slotRightSideBarActivateTitles() { d->rightSideBar->setActiveTab(d->rightSideBar->imageDescEditTab()); d->rightSideBar->imageDescEditTab()->setFocusToTitlesEdit(); } void DigikamView::slotRightSideBarActivateComments() { d->rightSideBar->setActiveTab(d->rightSideBar->imageDescEditTab()); d->rightSideBar->imageDescEditTab()->setFocusToCommentsEdit(); } void DigikamView::slotRightSideBarActivateAssignedTags() { d->rightSideBar->setActiveTab(d->rightSideBar->imageDescEditTab()); d->rightSideBar->imageDescEditTab()->activateAssignedTagsButton(); } void DigikamView::slotRatingChanged(const QUrl& url, int rating) { rating = qMin(RatingMax, qMax(RatingMin, rating)); ImageInfo info = ImageInfo::fromUrl(url); if (!info.isNull()) { FileActionMngr::instance()->assignRating(info, rating); } } void DigikamView::slotColorLabelChanged(const QUrl& url, int color) { ImageInfo info = ImageInfo::fromUrl(url); if (!info.isNull()) { FileActionMngr::instance()->assignColorLabel(info, color); } } void DigikamView::slotPickLabelChanged(const QUrl& url, int pick) { ImageInfo info = ImageInfo::fromUrl(url); if (!info.isNull()) { FileActionMngr::instance()->assignPickLabel(info, pick); } } void DigikamView::slotToggleTag(const QUrl& url, int tagID) { ImageInfo info = ImageInfo::fromUrl(url); if (!info.isNull()) { if (info.tagIds().contains(tagID)) FileActionMngr::instance()->removeTag(info, tagID); else FileActionMngr::instance()->assignTag(info, tagID); } } bool DigikamView::hasCurrentItem() const { return !currentInfo().isNull(); } void DigikamView::slotFocusAndNextImage() { //slot is called on pressing "return" a second time after assigning a tag d->stackedview->currentWidget()->setFocus(); //select next image, since the user is probably done tagging the current image slotNextItem(); } void DigikamView::slotImageExifOrientation(int orientation) { FileActionMngr::instance()->setExifOrientation( selectedInfoList(ApplicationSettings::Metadata), orientation); } void DigikamView::imageTransform(MetaEngineRotation::TransformationAction transform) { FileActionMngr::instance()->transform( selectedInfoList(ApplicationSettings::Metadata), transform); } ImageInfo DigikamView::currentInfo() const { switch (viewMode()) { case StackedView::TableViewMode: return d->tableView->currentInfo(); #ifdef HAVE_MARBLE case StackedView::MapWidgetMode: return d->mapView->currentImageInfo(); #endif // HAVE_MARBLE case StackedView::MediaPlayerMode: case StackedView::PreviewImageMode: case StackedView::IconViewMode: // all of these modes use the same selection model and data as the IconViewMode return d->iconView->currentInfo(); default: return ImageInfo(); } } Album* DigikamView::currentAlbum() const { switch (viewMode()) { case StackedView::TableViewMode: return d->tableView->currentAlbum(); case StackedView::PreviewImageMode: case StackedView::MediaPlayerMode: case StackedView::MapWidgetMode: case StackedView::IconViewMode: // all of these modes use the same selection model and data as the IconViewMode return d->iconView->currentAlbum(); default: return 0; } } ImageInfoList DigikamView::selectedInfoList(const bool currentFirst, const bool grouping) const { switch (viewMode()) { case StackedView::TableViewMode: if (currentFirst) { return d->tableView->selectedImageInfosCurrentFirst(grouping); } return d->tableView->selectedImageInfos(grouping); case StackedView::PreviewImageMode: case StackedView::MediaPlayerMode: case StackedView::MapWidgetMode: case StackedView::IconViewMode: // all of these modes use the same selection model and data as the IconViewMode if (currentFirst) { return d->iconView->selectedImageInfosCurrentFirst(grouping); } return d->iconView->selectedImageInfos(grouping); default: return QList(); } } ImageInfoList DigikamView::selectedInfoList(const ApplicationSettings::OperationType type, const bool currentFirst) const { return selectedInfoList(currentFirst, needGroupResolving(type)); } ImageInfoList DigikamView::allInfo(const bool grouping) const { switch (viewMode()) { case StackedView::TableViewMode: return d->tableView->allInfo(grouping); case StackedView::MapWidgetMode: case StackedView::PreviewImageMode: case StackedView::MediaPlayerMode: case StackedView::IconViewMode: // all of these modes use the same selection model and data as the IconViewMode return d->iconView->allImageInfos(grouping); default: return QList(); } } ImageInfoList DigikamView::allInfo(const ApplicationSettings::OperationType type) const { return allInfo(needGroupResolving(type, true)); } bool DigikamView::needGroupResolving(const ApplicationSettings::OperationType type, const bool all) const { switch (viewMode()) { case StackedView::TableViewMode: return d->tableView->needGroupResolving(type, all); case StackedView::MapWidgetMode: case StackedView::PreviewImageMode: case StackedView::MediaPlayerMode: case StackedView::IconViewMode: // all of these modes use the same selection model and data as the IconViewMode return d->iconView->needGroupResolving(type, all); default: return false; } } QUrl DigikamView::currentUrl() const { const ImageInfo cInfo = currentInfo(); return cInfo.fileUrl(); } void DigikamView::slotSetCurrentWhenAvailable(const qlonglong id) { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotSetCurrentWhenAvailable(id); break; default: d->iconView->setCurrentWhenAvailable(id); } } void DigikamView::slotAwayFromSelection() { switch (viewMode()) { case StackedView::TableViewMode: d->tableView->slotAwayFromSelection(); break; default: d->iconView->awayFromSelection(); } } StackedView::StackedViewMode DigikamView::viewMode() const { return d->stackedview->viewMode(); } void DigikamView::slotSetupMetadataFilters(int tab) { Setup::execMetadataFilters(this, tab); } void DigikamView::toggleFullScreen(bool set) { d->stackedview->imagePreviewView()->toggleFullScreen(set); } void DigikamView::setToolsIconView(DCategorizedView* const view) { d->rightSideBar->appendTab(view, QIcon::fromTheme(QLatin1String("document-edit")), i18n("Tools")); } void DigikamView::slotShowContextMenu(QContextMenuEvent* event, const QList& extraGroupingActions) { Album* const album = currentAlbum(); if (!album || album->isRoot() || (album->type() != Album::PHYSICAL && album->type() != Album::TAG) ) { return; } QMenu menu(this); ContextMenuHelper cmHelper(&menu); cmHelper.addAction(QLatin1String("full_screen")); cmHelper.addAction(QLatin1String("options_show_menubar")); cmHelper.addSeparator(); // -------------------------------------------------------- cmHelper.addStandardActionPaste(this, SLOT(slotImagePaste())); // -------------------------------------------------------- if (!extraGroupingActions.isEmpty()) { cmHelper.addSeparator(); cmHelper.addGroupMenu(QList(), extraGroupingActions); } cmHelper.exec(event->globalPos()); } void DigikamView::slotShowContextMenuOnInfo(QContextMenuEvent* event, const ImageInfo& info, const QList& extraGroupingActions, ImageFilterModel* imageFilterModel) { QList selectedImageIds = selectedInfoList(true, true).toImageIdList(); // -------------------------------------------------------- QMenu menu(this); ContextMenuHelper cmHelper(&menu); cmHelper.setImageFilterModel(imageFilterModel); cmHelper.addAction(QLatin1String("full_screen")); cmHelper.addAction(QLatin1String("options_show_menubar")); - cmHelper.addSeparator(); - cmHelper.addAction(QLatin1String("move_selection_to_album")); + // -------------------------------------------------------- + QAction* const viewAction = new QAction(i18nc("View the selected image", "Preview"), this); viewAction->setIcon(QIcon::fromTheme(QLatin1String("view-preview"))); viewAction->setEnabled(selectedImageIds.count() == 1); cmHelper.addAction(viewAction); - cmHelper.addAction(QLatin1String("image_edit")); - cmHelper.addServicesMenu(selectedUrls()); - cmHelper.addGotoMenu(selectedImageIds); - cmHelper.addAction(QLatin1String("image_rotate")); - + cmHelper.addOpenAndNavigateActions(selectedImageIds); cmHelper.addSeparator(); + // -------------------------------------------------------- + cmHelper.addAction(QLatin1String("image_find_similar")); cmHelper.addStandardActionLightTable(); cmHelper.addQueueManagerMenu(); - cmHelper.addSeparator(); - cmHelper.addAction(QLatin1String("image_rename")); + // -------------------------------------------------------- + + cmHelper.addAction(QLatin1String("image_rotate")); cmHelper.addAction(QLatin1String("cut_album_selection")); cmHelper.addAction(QLatin1String("copy_album_selection")); cmHelper.addAction(QLatin1String("paste_album_selection")); + cmHelper.addAction(QLatin1String("image_rename")); cmHelper.addStandardActionItemDelete(this, SLOT(slotImageDelete()), selectedImageIds.count()); - cmHelper.addSeparator(); + // -------------------------------------------------------- + cmHelper.addStandardActionThumbnail(selectedImageIds, currentAlbum()); cmHelper.addAssignTagsMenu(selectedImageIds); cmHelper.addRemoveTagsMenu(selectedImageIds); - - cmHelper.addSeparator(); - cmHelper.addLabelsAction(); if (d->leftSideBar->getActiveTab() != d->peopleSideBar) { + cmHelper.addSeparator(); + cmHelper.addGroupMenu(selectedImageIds, extraGroupingActions); } // special action handling -------------------------------- connect(&cmHelper, SIGNAL(signalAssignColorLabel(int)), this, SLOT(slotAssignColorLabel(int))); connect(&cmHelper, SIGNAL(signalAssignPickLabel(int)), this, SLOT(slotAssignPickLabel(int))); connect(&cmHelper, SIGNAL(signalAssignRating(int)), this, SLOT(slotAssignRating(int))); connect(&cmHelper, SIGNAL(signalAssignTag(int)), this, SLOT(slotAssignTag(int))); connect(&cmHelper, SIGNAL(signalRemoveTag(int)), this, SLOT(slotRemoveTag(int))); connect(&cmHelper, SIGNAL(signalPopupTagsView()), d->rightSideBar, SLOT(slotPopupTagsView())); connect(&cmHelper, SIGNAL(signalGotoTag(int)), this, SLOT(slotGotoTagAndItem(int))); connect(&cmHelper, SIGNAL(signalGotoTag(int)), d->albumHistory, SLOT(slotClearSelectTAlbum(int))); connect(&cmHelper, SIGNAL(signalGotoAlbum(ImageInfo)), this, SLOT(slotGotoAlbumAndItem(ImageInfo))); connect(&cmHelper, SIGNAL(signalGotoAlbum(ImageInfo)), d->albumHistory, SLOT(slotClearSelectPAlbum(ImageInfo))); connect(&cmHelper, SIGNAL(signalGotoDate(ImageInfo)), this, SLOT(slotGotoDateAndItem(ImageInfo))); connect(&cmHelper, SIGNAL(signalSetThumbnail(ImageInfo)), this, SLOT(slotSetAsAlbumThumbnail(ImageInfo))); connect(&cmHelper, SIGNAL(signalAddToExistingQueue(int)), this, SLOT(slotImageAddToExistingQueue(int))); connect(&cmHelper, SIGNAL(signalCreateGroup()), this, SLOT(slotCreateGroupFromSelection())); connect(&cmHelper, SIGNAL(signalCreateGroupByTime()), this, SLOT(slotCreateGroupByTimeFromSelection())); connect(&cmHelper, SIGNAL(signalCreateGroupByFilename()), this, SLOT(slotCreateGroupByFilenameFromSelection())); connect(&cmHelper, SIGNAL(signalRemoveFromGroup()), this, SLOT(slotRemoveSelectedFromGroup())); connect(&cmHelper, SIGNAL(signalUngroup()), this, SLOT(slotUngroupSelected())); // -------------------------------------------------------- QAction* const choice = cmHelper.exec(event->globalPos()); if (choice && (choice == viewAction)) { slotTogglePreviewMode(info); } } void DigikamView::slotSetAsAlbumThumbnail(const ImageInfo& info) { d->utilities->setAsAlbumThumbnail(currentAlbum(), info); } void DigikamView::slotCreateGroupFromSelection() { FileActionMngr::instance()->addToGroup(currentInfo(), selectedInfoList(false, true)); } void DigikamView::slotCreateGroupByTimeFromSelection() { d->utilities->createGroupByTimeFromInfoList(selectedInfoList(false, true)); } void DigikamView::slotCreateGroupByFilenameFromSelection() { d->utilities->createGroupByFilenameFromInfoList(selectedInfoList(false, true)); } void DigikamView::slotRemoveSelectedFromGroup() { FileActionMngr::instance()->removeFromGroup(selectedInfoList(false, true)); } void DigikamView::slotUngroupSelected() { FileActionMngr::instance()->ungroup(selectedInfoList(false, true)); } } // namespace Digikam diff --git a/app/views/imagepreviewview.cpp b/app/views/imagepreviewview.cpp index 7cafbc5da9..14c6383ff8 100644 --- a/app/views/imagepreviewview.cpp +++ b/app/views/imagepreviewview.cpp @@ -1,576 +1,576 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2006-21-12 * Description : a embedded view to show the image preview widget. * * Copyright (C) 2006-2017 by Gilles Caulier * Copyright (C) 2009-2012 by Andi Clemens * Copyright (C) 2010-2011 by Aditya Bhatt * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "imagepreviewview.h" // Qt includes #include #include #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "digikam_config.h" #include "imagepreviewviewitem.h" #include "applicationsettings.h" #include "contextmenuhelper.h" #include "ddragobjects.h" #include "digikamapp.h" #include "dimg.h" #include "dimgpreviewitem.h" #include "imageinfo.h" #include "fileactionmngr.h" #include "metadatasettings.h" #include "regionframeitem.h" #include "tagspopupmenu.h" #include "thememanager.h" #include "previewlayout.h" #include "previewsettings.h" #include "tagscache.h" #include "imagetagpair.h" #include "albummanager.h" #include "facegroup.h" namespace Digikam { class ImagePreviewView::Private { public: Private() { fullSize = 0; scale = 1.0; item = 0; isValid = false; rotationLock = false; toolBar = 0; prevAction = 0; nextAction = 0; rotLeftAction = 0; rotRightAction = 0; mode = ImagePreviewView::IconViewPreview; faceGroup = 0; peopleToggleAction = 0; addPersonAction = 0; forgetFacesAction = 0; fullscreenAction = 0; + currAlbum = 0; } bool fullSize; double scale; bool isValid; bool rotationLock; ImagePreviewView::Mode mode; ImagePreviewViewItem* item; QAction* prevAction; QAction* nextAction; QAction* rotLeftAction; QAction* rotRightAction; QToolBar* toolBar; FaceGroup* faceGroup; QAction* peopleToggleAction; QAction* addPersonAction; QAction* forgetFacesAction; QAction* fullscreenAction; + + Album* currAlbum; }; -ImagePreviewView::ImagePreviewView(QWidget* const parent, Mode mode) +ImagePreviewView::ImagePreviewView(QWidget* const parent, Mode mode, Album* currAlbum) : GraphicsDImgView(parent), - d(new Private) + d(new Private()) { d->mode = mode; d->item = new ImagePreviewViewItem(); + d->currAlbum = currAlbum; setItem(d->item); d->faceGroup = new FaceGroup(this); d->faceGroup->setShowOnHover(true); d->item->setFaceGroup(d->faceGroup); connect(d->item, SIGNAL(loaded()), this, SLOT(imageLoaded())); connect(d->item, SIGNAL(loadingFailed()), this, SLOT(imageLoadingFailed())); connect(d->item, SIGNAL(imageChanged()), this, SLOT(slotUpdateFaces())); connect(d->item, SIGNAL(showContextMenu(QGraphicsSceneContextMenuEvent*)), this, SLOT(slotShowContextMenu(QGraphicsSceneContextMenuEvent*))); // set default zoom layout()->fitToWindow(); // ------------------------------------------------------------ installPanIcon(); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // ------------------------------------------------------------ d->prevAction = new QAction(QIcon::fromTheme(QLatin1String("go-previous")), i18nc("go to previous image", "Back"), this); d->nextAction = new QAction(QIcon::fromTheme(QLatin1String("go-next")), i18nc("go to next image", "Forward"), this); d->rotLeftAction = new QAction(QIcon::fromTheme(QLatin1String("object-rotate-left")), i18nc("@info:tooltip", "Rotate Left"), this); d->rotRightAction = new QAction(QIcon::fromTheme(QLatin1String("object-rotate-right")), i18nc("@info:tooltip", "Rotate Right"), this); d->addPersonAction = new QAction(QIcon::fromTheme(QLatin1String("list-add-user")), i18n("Add a Face Tag"), this); d->forgetFacesAction = new QAction(QIcon::fromTheme(QLatin1String("list-remove-user")), i18n("Clear all faces on this image"), this); d->peopleToggleAction = new QAction(QIcon::fromTheme(QLatin1String("im-user")), i18n("Show Face Tags"), this); d->peopleToggleAction->setCheckable(true); d->fullscreenAction = new QAction(QIcon::fromTheme(QLatin1String("media-playback-start")), i18n("Show Fullscreen"), this); d->toolBar = new QToolBar(this); if (mode == IconViewPreview) { d->toolBar->addAction(d->prevAction); d->toolBar->addAction(d->nextAction); } d->toolBar->addAction(d->rotLeftAction); d->toolBar->addAction(d->rotRightAction); d->toolBar->addAction(d->peopleToggleAction); d->toolBar->addAction(d->addPersonAction); d->toolBar->addAction(d->fullscreenAction); connect(d->prevAction, SIGNAL(triggered()), this, SIGNAL(toPreviousImage())); connect(d->nextAction, SIGNAL(triggered()), this, SIGNAL(toNextImage())); connect(d->rotLeftAction, SIGNAL(triggered()), this, SLOT(slotRotateLeft())); connect(d->rotRightAction, SIGNAL(triggered()), this, SLOT(slotRotateRight())); connect(d->peopleToggleAction, SIGNAL(toggled(bool)), d->faceGroup, SLOT(setVisible(bool))); connect(d->addPersonAction, SIGNAL(triggered()), d->faceGroup, SLOT(addFace())); connect(d->forgetFacesAction, SIGNAL(triggered()), d->faceGroup, SLOT(rejectAll())); connect(d->fullscreenAction, SIGNAL(triggered()), this, SIGNAL(signalSlideShowCurrent())); // ------------------------------------------------------------ connect(this, SIGNAL(toNextImage()), this, SIGNAL(signalNextItem())); connect(this, SIGNAL(toPreviousImage()), this, SIGNAL(signalPrevItem())); connect(this, SIGNAL(activated()), this, SIGNAL(signalEscapePreview())); connect(ThemeManager::instance(), SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); connect(ApplicationSettings::instance(), SIGNAL(setupChanged()), this, SLOT(slotSetupChanged())); slotSetupChanged(); } ImagePreviewView::~ImagePreviewView() { delete d->item; delete d; } void ImagePreviewView::reload() { previewItem()->reload(); } void ImagePreviewView::imageLoaded() { emit signalPreviewLoaded(true); d->rotLeftAction->setEnabled(true); d->rotRightAction->setEnabled(true); d->faceGroup->setInfo(d->item->imageInfo()); } void ImagePreviewView::imageLoadingFailed() { emit signalPreviewLoaded(false); d->rotLeftAction->setEnabled(false); d->rotRightAction->setEnabled(false); d->faceGroup->setInfo(ImageInfo()); } void ImagePreviewView::setImageInfo(const ImageInfo& info, const ImageInfo& previous, const ImageInfo& next) { d->faceGroup->aboutToSetInfo(info); d->item->setImageInfo(info); d->prevAction->setEnabled(!previous.isNull()); d->nextAction->setEnabled(!next.isNull()); QStringList previewPaths; if (next.category() == DatabaseItem::Image) { previewPaths << next.filePath(); } if (previous.category() == DatabaseItem::Image) { previewPaths << previous.filePath(); } d->item->setPreloadPaths(previewPaths); } ImageInfo ImagePreviewView::getImageInfo() const { return d->item->imageInfo(); } bool ImagePreviewView::acceptsMouseClick(QMouseEvent* e) { if (!GraphicsDImgView::acceptsMouseClick(e)) { return false; } return d->faceGroup->acceptsMouseClick(mapToScene(e->pos())); } void ImagePreviewView::enterEvent(QEvent* e) { d->faceGroup->enterEvent(e); } void ImagePreviewView::leaveEvent(QEvent* e) { d->faceGroup->leaveEvent(e); } void ImagePreviewView::showEvent(QShowEvent* e) { GraphicsDImgView::showEvent(e); d->faceGroup->setVisible(d->peopleToggleAction->isChecked()); } void ImagePreviewView::slotShowContextMenu(QGraphicsSceneContextMenuEvent* event) { ImageInfo info = d->item->imageInfo(); if (info.isNull()) { return; } event->accept(); QList idList; idList << info.id(); - QList selectedItems; - selectedItems << info.fileUrl(); // -------------------------------------------------------- QMenu popmenu(this); - ContextMenuHelper cmhelper(&popmenu); + ContextMenuHelper cmHelper(&popmenu); - cmhelper.addAction(QLatin1String("full_screen")); - cmhelper.addAction(QLatin1String("options_show_menubar")); - cmhelper.addSeparator(); + cmHelper.addAction(QLatin1String("full_screen")); + cmHelper.addAction(QLatin1String("options_show_menubar")); + cmHelper.addSeparator(); // -------------------------------------------------------- if (d->mode == IconViewPreview) { - cmhelper.addAction(d->prevAction, true); - cmhelper.addAction(d->nextAction, true); - cmhelper.addGotoMenu(idList); - cmhelper.addSeparator(); + cmHelper.addAction(d->prevAction, true); + cmHelper.addAction(d->nextAction, true); + cmHelper.addSeparator(); } // -------------------------------------------------------- - cmhelper.addAction(d->peopleToggleAction, true); - cmhelper.addAction(d->addPersonAction, true); - cmhelper.addAction(d->forgetFacesAction, true); - cmhelper.addSeparator(); + cmHelper.addAction(d->peopleToggleAction, true); + cmHelper.addAction(d->addPersonAction, true); + cmHelper.addAction(d->forgetFacesAction, true); + cmHelper.addSeparator(); // -------------------------------------------------------- - cmhelper.addAction(QLatin1String("image_edit")); - cmhelper.addServicesMenu(selectedItems); - cmhelper.addAction(QLatin1String("image_rotate")); - cmhelper.addSeparator(); + cmHelper.addOpenAndNavigateActions(idList); + cmHelper.addSeparator(); // -------------------------------------------------------- - cmhelper.addAction(QLatin1String("image_find_similar")); + cmHelper.addAction(QLatin1String("image_find_similar")); if (d->mode == IconViewPreview) { - cmhelper.addStandardActionLightTable(); + cmHelper.addStandardActionLightTable(); } - cmhelper.addQueueManagerMenu(); - cmhelper.addSeparator(); - - // -------------------------------------------------------- - - cmhelper.addStandardActionItemDelete(this, SLOT(slotDeleteItem())); - cmhelper.addSeparator(); + cmHelper.addQueueManagerMenu(); + cmHelper.addSeparator(); // -------------------------------------------------------- - cmhelper.addAssignTagsMenu(idList); - cmhelper.addRemoveTagsMenu(idList); - cmhelper.addSeparator(); + cmHelper.addAction(QLatin1String("image_rotate")); + cmHelper.addStandardActionItemDelete(this, SLOT(slotDeleteItem())); + cmHelper.addSeparator(); // -------------------------------------------------------- - cmhelper.addLabelsAction(); + if (d->mode == IconViewPreview && d->currAlbum) + { + cmHelper.addStandardActionThumbnail(idList, d->currAlbum); + } + cmHelper.addAssignTagsMenu(idList); + cmHelper.addRemoveTagsMenu(idList); + cmHelper.addLabelsAction(); // special action handling -------------------------------- - connect(&cmhelper, SIGNAL(signalAssignTag(int)), + connect(&cmHelper, SIGNAL(signalAssignTag(int)), this, SLOT(slotAssignTag(int))); - connect(&cmhelper, SIGNAL(signalPopupTagsView()), + connect(&cmHelper, SIGNAL(signalPopupTagsView()), this, SIGNAL(signalPopupTagsView())); - connect(&cmhelper, SIGNAL(signalRemoveTag(int)), + connect(&cmHelper, SIGNAL(signalRemoveTag(int)), this, SLOT(slotRemoveTag(int))); - connect(&cmhelper, SIGNAL(signalAssignPickLabel(int)), + connect(&cmHelper, SIGNAL(signalAssignPickLabel(int)), this, SLOT(slotAssignPickLabel(int))); - connect(&cmhelper, SIGNAL(signalAssignColorLabel(int)), + connect(&cmHelper, SIGNAL(signalAssignColorLabel(int)), this, SLOT(slotAssignColorLabel(int))); - connect(&cmhelper, SIGNAL(signalAssignRating(int)), + connect(&cmHelper, SIGNAL(signalAssignRating(int)), this, SLOT(slotAssignRating(int))); - connect(&cmhelper, SIGNAL(signalAddToExistingQueue(int)), + connect(&cmHelper, SIGNAL(signalAddToExistingQueue(int)), this, SIGNAL(signalAddToExistingQueue(int))); - connect(&cmhelper, SIGNAL(signalGotoTag(int)), + connect(&cmHelper, SIGNAL(signalGotoTag(int)), this, SIGNAL(signalGotoTagAndItem(int))); - connect(&cmhelper, SIGNAL(signalGotoAlbum(ImageInfo)), + connect(&cmHelper, SIGNAL(signalGotoAlbum(ImageInfo)), this, SIGNAL(signalGotoAlbumAndItem(ImageInfo))); - connect(&cmhelper, SIGNAL(signalGotoDate(ImageInfo)), + connect(&cmHelper, SIGNAL(signalGotoDate(ImageInfo)), this, SIGNAL(signalGotoDateAndItem(ImageInfo))); - cmhelper.exec(event->screenPos()); + cmHelper.exec(event->screenPos()); } void ImagePreviewView::slotAssignTag(int tagID) { FileActionMngr::instance()->assignTag(d->item->imageInfo(), tagID); } void ImagePreviewView::slotRemoveTag(int tagID) { FileActionMngr::instance()->removeTag(d->item->imageInfo(), tagID); } void ImagePreviewView::slotAssignPickLabel(int pickId) { FileActionMngr::instance()->assignPickLabel(d->item->imageInfo(), pickId); } void ImagePreviewView::slotAssignColorLabel(int colorId) { FileActionMngr::instance()->assignColorLabel(d->item->imageInfo(), colorId); } void ImagePreviewView::slotAssignRating(int rating) { FileActionMngr::instance()->assignRating(d->item->imageInfo(), rating); } void ImagePreviewView::slotThemeChanged() { QPalette plt(palette()); plt.setColor(backgroundRole(), qApp->palette().color(QPalette::Base)); setPalette(plt); } void ImagePreviewView::slotSetupChanged() { previewItem()->setPreviewSettings(ApplicationSettings::instance()->getPreviewSettings()); d->toolBar->setVisible(ApplicationSettings::instance()->getPreviewShowIcons()); setShowText(ApplicationSettings::instance()->getPreviewShowIcons()); // pass auto-suggest? } void ImagePreviewView::slotRotateLeft() { if(d->rotationLock) return; d->rotationLock = true; /** * Setting lock won't allow mouse hover events in ImagePreviewViewItem class */ d->item->setAcceptHoverEvents(false); /** * aboutToSetInfo will delete all face tags from FaceGroup storage */ d->faceGroup->aboutToSetInfo(ImageInfo()); FileActionMngr::instance()->transform(QList() << d->item->imageInfo(), MetaEngineRotation::Rotate270); } void ImagePreviewView::slotRotateRight() { if(d->rotationLock) return; d->rotationLock = true; /** * Setting lock won't allow mouse hover events in ImagePreviewViewItem class */ d->item->setAcceptHoverEvents(false); /** * aboutToSetInfo will delete all face tags from FaceGroup storage */ d->faceGroup->aboutToSetInfo(ImageInfo()); FileActionMngr::instance()->transform(QList() << d->item->imageInfo(), MetaEngineRotation::Rotate90); } void ImagePreviewView::slotDeleteItem() { emit signalDeleteItem(); } void Digikam::ImagePreviewView::slotUpdateFaces() { //d->faceGroup->aboutToSetInfo(ImageInfo()); d->faceGroup->aboutToSetInfoAfterRotate(ImageInfo()); d->item->setAcceptHoverEvents(true); /** * Release rotation lock after rotation */ d->rotationLock = false; } void ImagePreviewView::dragMoveEvent(QDragMoveEvent* e) { if (DTagListDrag::canDecode(e->mimeData())) { e->accept(); return; } e->ignore(); } void ImagePreviewView::dragEnterEvent(QDragEnterEvent* e) { if (DTagListDrag::canDecode(e->mimeData())) { e->accept(); return; } e->ignore(); } void ImagePreviewView::dropEvent(QDropEvent* e) { if (DTagListDrag::canDecode(e->mimeData())) { QList tagIDs; if (!DTagListDrag::decode(e->mimeData(), tagIDs)) { return; } QMenu popMenu(this); QAction* const assignToThisAction = popMenu.addAction(QIcon::fromTheme(QLatin1String("tag")), i18n("Assign Tags to &This Item")); popMenu.addSeparator(); popMenu.addAction(QIcon::fromTheme(QLatin1String("dialog-cancel")), i18n("&Cancel")); popMenu.setMouseTracking(true); QAction* const choice = popMenu.exec(this->mapToGlobal(e->pos())); if(choice == assignToThisAction) { FileActionMngr::instance()->assignTags(d->item->imageInfo(),tagIDs); } } e->accept(); return; } void ImagePreviewView::mousePressEvent(QMouseEvent* e) { if (e->button() == Qt::LeftButton && QApplication::keyboardModifiers() == Qt::ControlModifier) { d->faceGroup->addFace(); } GraphicsDImgView::mousePressEvent(e); } } // namespace Digikam diff --git a/app/views/imagepreviewview.h b/app/views/imagepreviewview.h index 822bcec7f6..04f4a27c8d 100644 --- a/app/views/imagepreviewview.h +++ b/app/views/imagepreviewview.h @@ -1,136 +1,137 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2006-21-12 * Description : a embedded view to show the image preview widget. * * Copyright (C) 2006-2017 by Gilles Caulier * Copyright (C) 2009-2012 by Andi Clemens * Copyright (C) 2010-2011 by Aditya Bhatt * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef IMAGE_PREVIEW_VIEW_H #define IMAGE_PREVIEW_VIEW_H // Local includes #include "graphicsdimgview.h" #include "imageinfo.h" class QPixmap; class QDragMoveEvent; class QDropEvent; class QDragEnterEvent; namespace Digikam { +class Album; class LoadingDescription; class ImagePreviewView : public GraphicsDImgView { Q_OBJECT public: enum Mode { IconViewPreview, LightTablePreview }; public: - explicit ImagePreviewView(QWidget* const parent, Mode mode=IconViewPreview); + explicit ImagePreviewView(QWidget* const parent, Mode mode=IconViewPreview, Album* currAlbum = 0); ~ImagePreviewView(); void setImageInfo(const ImageInfo& info = ImageInfo(), const ImageInfo& previous = ImageInfo(), const ImageInfo& next = ImageInfo()); ImageInfo getImageInfo() const; void reload(); void setImagePath(const QString& path=QString()); void setPreviousNextPaths(const QString& previous, const QString& next); Q_SIGNALS: void signalNextItem(); void signalPrevItem(); void signalDeleteItem(); void signalEditItem(); void signalPreviewLoaded(bool success); void signalEscapePreview(); void signalSlideShowCurrent(); void signalSlideShow(); void signalInsert2LightTable(); void signalInsert2QueueMgr(); void signalFindSimilar(); void signalAddToExistingQueue(int); void signalGotoAlbumAndItem(const ImageInfo&); void signalGotoDateAndItem(const ImageInfo&); void signalGotoTagAndItem(int); void signalPopupTagsView(); protected: bool acceptsMouseClick(QMouseEvent* e); void mousePressEvent(QMouseEvent* e); void enterEvent(QEvent* e); void leaveEvent(QEvent* e); void showEvent(QShowEvent* e); void dropEvent(QDropEvent* e); void dragMoveEvent(QDragMoveEvent* e); void dragEnterEvent(QDragEnterEvent* e); private Q_SLOTS: void imageLoaded(); void imageLoadingFailed(); void slotAssignTag(int tagID); void slotRemoveTag(int tagID); void slotAssignRating(int rating); void slotAssignPickLabel(int pickId); void slotAssignColorLabel(int colorId); void slotThemeChanged(); void slotSetupChanged(); void slotRotateLeft(); void slotRotateRight(); void slotDeleteItem(); /** * @brief slotUpdateFaces - after applying some transformation on * image, update face tags position */ void slotUpdateFaces(); void slotShowContextMenu(QGraphicsSceneContextMenuEvent* event); private: class Private; Private* const d; }; } // namespace Digikam #endif // IMAGE_PREVIEW_VIEW_H