diff --git a/core/app/items/utils/contextmenuhelper.cpp b/core/app/items/utils/contextmenuhelper.cpp index 7b261554dc..df0a928feb 100644 --- a/core/app/items/utils/contextmenuhelper.cpp +++ b/core/app/items/utils/contextmenuhelper.cpp @@ -1,1288 +1,1319 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-02-15 * Description : contextmenu helper class * * Copyright (C) 2009-2011 by Andi Clemens * Copyright (C) 2010-2020 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 #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 "albumpointer.h" #include "albummodificationhelper.h" #include "abstractalbummodel.h" #include "coredbaccess.h" #include "digikamapp.h" #include "dfileoperations.h" #include "iteminfo.h" #include "itemfiltermodel.h" #include "itemviewutilities.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" #ifdef HAVE_AKONADICONTACT # include "akonadiiface.h" #endif #ifdef Q_OS_WIN # include # include #endif namespace Digikam { class Q_DECL_HIDDEN ContextMenuHelper::Private { public: explicit Private(ContextMenuHelper* const q) : gotoAlbumAction(nullptr), gotoDateAction(nullptr), setThumbnailAction(nullptr), imageFilterModel(nullptr), albumModel(nullptr), parent(nullptr), stdActionCollection(nullptr), q(q) { } QAction* gotoAlbumAction; QAction* gotoDateAction; QAction* setThumbnailAction; QList selectedIds; QList selectedItems; QMap queueActions; QMap servicesMap; ItemFilterModel* imageFilterModel; AbstractCheckableAlbumModel* albumModel; QMenu* parent; KActionCollection* stdActionCollection; ContextMenuHelper* q; public: QModelIndex indexForAlbumFromAction(QObject* sender) const { QAction* action = nullptr; 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 nullptr; } 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 = nullptr; QStringList ltActionNames; ltActionNames << QLatin1String("image_add_to_lighttable") << QLatin1String("image_lighttable"); - if (LightTableWindow::lightTableWindowCreated() && !LightTableWindow::lightTableWindow()->isEmpty()) + 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 && (ids.count() == 1)) { - if (album->type() == Album::PHYSICAL) + 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, bool lightTable) { if (lightTable) { setSelectedIds(ids); QAction* const openImageFile = new QAction(QIcon::fromTheme(QLatin1String("quickopen-file")), i18n("Open..."), this); addAction(openImageFile); connect(openImageFile, SIGNAL(triggered()), this, SLOT(slotOpenImageFile())); } else { addAction(QLatin1String("image_edit")); addAction(QLatin1String("move_selection_to_album")); } addServicesMenu(ItemInfoList(ids).toImageUrlList()); // addServicesMenu() has stored d->selectedItems + if (!d->selectedItems.isEmpty()) { QAction* const openFileMngr = new QAction(QIcon::fromTheme(QLatin1String("folder-open")), i18n("Open in File Manager"), this); addAction(openFileMngr); connect(openFileMngr, SIGNAL(triggered()), this, SLOT(slotOpenInFileManager())); } if (!lightTable) { addGotoMenu(ids); } } void ContextMenuHelper::addServicesMenu(const QList& selectedItems) { setSelectedItems(selectedItems); #ifdef Q_OS_WIN if (selectedItems.length() == 1) { QAction* const openWith = new QAction(i18n("Open With"), this); addAction(openWith); connect(openWith, SIGNAL(triggered()), this, SLOT(slotOpenWith())); } #else // Q_OS_WIN KService::List offers = DFileOperations::servicesForOpenWith(selectedItems); if (!offers.isEmpty()) { - QMenu* const servicesMenu = new QMenu(d->parent); + 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 } #endif // Q_OS_WIN } void ContextMenuHelper::slotOpenWith() { // call the slot with an "empty" action + slotOpenWith(nullptr); } void ContextMenuHelper::slotOpenWith(QAction* action) { #ifdef Q_OS_WIN Q_UNUSED(action); // See Bug #380065 for details. if (d->selectedItems.length() == 1) { SHELLEXECUTEINFO sei = {}; sei.cbSize = sizeof(sei); sei.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_NOASYNC; sei.nShow = SW_SHOWNORMAL; sei.lpVerb = (LPCWSTR)QString::fromLatin1("openas").utf16(); sei.lpFile = (LPCWSTR)d->selectedItems.first().toLocalFile().utf16(); ShellExecuteEx(&sei); qCDebug(DIGIKAM_GENERAL_LOG) << "ShellExecuteEx::openas called"; } #else // Q_OS_WIN 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.data(), list); #endif // Q_OS_WIN + } void ContextMenuHelper::slotOpenInFileManager() { DFileOperations::openInFileManager(d->selectedItems); } void ContextMenuHelper::slotOpenImageFile() { if (d->selectedIds.isEmpty()) { return; } ItemInfoList infos = ItemInfoList(d->selectedIds); ItemViewUtilities(d->parent).openInfos(infos.first(), infos, nullptr); } 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("edit-delete")), 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("edit-delete")), 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); + // This is only for the user to give a hint for the shortcut key + editTagAction->setShortcut(Qt::ALT + Qt::Key_Return); 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) + 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() { + #ifdef HAVE_AKONADICONTACT + AkonadiIface* const abc = new AkonadiIface(d->parent); connect(abc, SIGNAL(signalContactTriggered(QString)), this, SIGNAL(signalAddNewTagFromABCMenu(QString))); // AkonadiIface instance will be deleted with d->parent. + #endif + } 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); KXMLGUIClient* const client = const_cast(d->stdActionCollection->parentGUIClient()); QList actions = DPluginLoader::instance()->pluginsActions(DPluginAction::GenericImport, dynamic_cast(client)); if (!actions.isEmpty()) { foreach (DPluginAction* const ac, actions) { menuImport->addActions(QList() << ac); } } else { QAction* const notools = new QAction(i18n("No import tool available"), this); notools->setEnabled(false); menuImport->addAction(notools); } d->parent->addMenu(menuImport); } void ContextMenuHelper::addExportMenu() { QMenu* const menuExport = new QMenu(i18n("Export"), d->parent); KXMLGUIClient* const client = const_cast(d->stdActionCollection->parentGUIClient()); QList actions = DPluginLoader::instance()->pluginsActions(DPluginAction::GenericExport, dynamic_cast(client)); #if 0 + QAction* selectAllAction = 0; selectAllAction = d->stdActionCollection->action("selectAll"); + #endif if (!actions.isEmpty()) { foreach (DPluginAction* const ac, actions) { menuExport->addActions(QList() << ac); } } else { QAction* const notools = new QAction(i18n("No export tool available"), this); notools->setEnabled(false); menuExport->addAction(notools); } d->parent->addMenu(menuExport); } void ContextMenuHelper::addAlbumActions() { QList albumActions; 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 + ItemInfo item; if (!d->selectedIds.isEmpty()) { item = ItemInfo(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 = nullptr; if (!albumList.isEmpty()) { currentAlbum = albumList.first(); } else { return; } - if (currentAlbum->type() == Album::PHYSICAL) + 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")); + 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")); + 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::setItemFilterModel(ItemFilterModel* 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; } ItemInfo 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 closeActionTimelapse = new QAction(i18nc("@action:inmenu", "Group Selected By Timelapse / Burst"), this); connect(closeActionTimelapse, SIGNAL(triggered()), this, SIGNAL(signalCreateGroupByTimelapse())); actions << closeActionTimelapse; 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 hideAction = new QAction(i18nc("@action:inmenu", "Hide Grouped Images"), this); connect(hideAction, SIGNAL(triggered()), this, SLOT(slotCloseGroups())); actions << hideAction; 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; } GroupItemFilterSettings settings = d->imageFilterModel->groupItemFilterSettings(); foreach (const qlonglong& id, d->selectedIds) { ItemInfo info(id); if (info.hasGroupedImages()) { settings.setOpen(id, open); } } d->imageFilterModel->setGroupItemFilterSettings(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-full")), 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) { ItemInfo selectedItem(d->selectedIds.first()); - if (choice == d->gotoAlbumAction) + 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/core/app/items/utils/groupingviewimplementation.cpp b/core/app/items/utils/groupingviewimplementation.cpp index 4e79d66be3..57b0e7e8bf 100644 --- a/core/app/items/utils/groupingviewimplementation.cpp +++ b/core/app/items/utils/groupingviewimplementation.cpp @@ -1,95 +1,94 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2017-11-02 * Description : Implementation of grouping specific functions for views * - * Copyright (C) 2017 by Simon Frei + * 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 "groupingviewimplementation.h" // Qt includes #include // Local includes #include "iteminfolist.h" namespace Digikam { bool GroupingViewImplementation::needGroupResolving(ApplicationSettings::OperationType type, const ItemInfoList& infos) const { - ApplicationSettings::ApplyToEntireGroup applyAll = - ApplicationSettings::instance()->getGroupingOperateOnAll(type); + ApplicationSettings::ApplyToEntireGroup applyAll = ApplicationSettings::instance()->getGroupingOperateOnAll(type); if (applyAll == ApplicationSettings::No) { return false; } foreach (const ItemInfo& info, infos) { if (hasHiddenGroupedImages(info)) { if (applyAll == ApplicationSettings::Yes) { return true; } return ApplicationSettings::instance()->askGroupingOperateOnAll(type); } } return false; } ItemInfoList GroupingViewImplementation::resolveGrouping(const ItemInfoList& infos) const { ItemInfoList outInfos; foreach (const ItemInfo& info, infos) { outInfos << info; if (hasHiddenGroupedImages(info)) { outInfos << info.groupedImages(); } } return outInfos; } ItemInfoList GroupingViewImplementation::getHiddenGroupedInfos(const ItemInfoList& infos) const { ItemInfoList outInfos; foreach (const ItemInfo& info, infos) { if (hasHiddenGroupedImages(info)) { outInfos << info.groupedImages(); } } return outInfos; } } // namespace Digikam diff --git a/core/app/items/utils/groupingviewimplementation.h b/core/app/items/utils/groupingviewimplementation.h index a1efe5be97..93559da940 100644 --- a/core/app/items/utils/groupingviewimplementation.h +++ b/core/app/items/utils/groupingviewimplementation.h @@ -1,62 +1,63 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2017-11-02 * Description : Implementation of grouping specific functions for views * - * Copyright (C) 2017 by Simon Frei + * 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. * * ============================================================ */ #ifndef DIGIKAM_GROUPING_VIEW_IMPLEMENTATION_H #define DIGIKAM_GROUPING_VIEW_IMPLEMENTATION_H // Local includes #include "applicationsettings.h" #include "digikam_export.h" namespace Digikam { class ItemInfo; class ItemInfoList; class DIGIKAM_GUI_EXPORT GroupingViewImplementation { public: virtual ~GroupingViewImplementation() { } - // must be implemented by parent view - virtual bool hasHiddenGroupedImages(const ItemInfo&) const + /// must be implemented by parent view + + virtual bool hasHiddenGroupedImages(const ItemInfo&) const { return false; } bool needGroupResolving(ApplicationSettings::OperationType type, - const ItemInfoList& infos) const; + const ItemInfoList& infos) const; - ItemInfoList resolveGrouping(const ItemInfoList& infos) const; - ItemInfoList getHiddenGroupedInfos(const ItemInfoList& infos) const; + ItemInfoList resolveGrouping(const ItemInfoList& infos) const; + ItemInfoList getHiddenGroupedInfos(const ItemInfoList& infos) const; }; } // namespace Digikam #endif // DIGIKAM_GROUPING_VIEW_IMPLEMENTATION_H diff --git a/core/app/items/utils/itemcategorydrawer.cpp b/core/app/items/utils/itemcategorydrawer.cpp index ed62c48de2..f4023f5f01 100644 --- a/core/app/items/utils/itemcategorydrawer.cpp +++ b/core/app/items/utils/itemcategorydrawer.cpp @@ -1,415 +1,427 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-04-20 * Description : Qt model-view for items - category drawer * * Copyright (C) 2009-2011 by Marcel Wiesweg * Copyright (C) 2011 by Andi Clemens * * 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 "itemcategorydrawer.h" // Qt includes #include #include #include // KDE includes #include // Local includes #include "album.h" #include "albummanager.h" #include "itemalbummodel.h" #include "itemcategorizedview.h" #include "itemdelegate.h" #include "itemfiltermodel.h" #include "itemmodel.h" #include "itemscanner.h" #include "searchfolderview.h" namespace Digikam { class Q_DECL_HIDDEN ItemCategoryDrawer::Private { public: explicit Private() + : lowerSpacing(0), + view(nullptr) { - lowerSpacing = 0; - view = nullptr; } - QFont font; - QRect rect; - QPixmap pixmap; - int lowerSpacing; + QFont font; + QRect rect; + QPixmap pixmap; + int lowerSpacing; ItemCategorizedView* view; }; ItemCategoryDrawer::ItemCategoryDrawer(ItemCategorizedView* const parent) : DCategoryDrawer(nullptr), d(new Private) { d->view = parent; } ItemCategoryDrawer::~ItemCategoryDrawer() { delete d; } int ItemCategoryDrawer::categoryHeight(const QModelIndex& /*index*/, const QStyleOption& /*option*/) const { - return d->rect.height() + d->lowerSpacing; + return (d->rect.height() + d->lowerSpacing); } int ItemCategoryDrawer::maximumHeight() const { - return d->rect.height() + d->lowerSpacing; + return (d->rect.height() + d->lowerSpacing); } void ItemCategoryDrawer::setLowerSpacing(int spacing) { d->lowerSpacing = spacing; } void ItemCategoryDrawer::setDefaultViewOptions(const QStyleOptionViewItem& option) { d->font = option.font; if (option.rect.width() != d->rect.width()) { updateRectsAndPixmaps(option.rect.width()); } } void ItemCategoryDrawer::invalidatePaintingCache() { if (d->rect.isNull()) { return; } updateRectsAndPixmaps(d->rect.width()); } void ItemCategoryDrawer::drawCategory(const QModelIndex& index, int /*sortRole*/, - const QStyleOption& option, QPainter* p) const + const QStyleOption& option, QPainter* p) const { if (option.rect.width() != d->rect.width()) { const_cast(this)->updateRectsAndPixmaps(option.rect.width()); } p->save(); p->translate(option.rect.topLeft()); ItemSortSettings::CategorizationMode mode = (ItemSortSettings::CategorizationMode)index.data(ItemFilterModel::CategorizationModeRole).toInt(); p->drawPixmap(0, 0, d->pixmap); QFont fontBold(d->font); QFont fontNormal(d->font); fontBold.setBold(true); int fnSize = fontBold.pointSize(); if (fnSize > 0) { fontBold.setPointSize(fnSize+2); } else { fnSize = fontBold.pixelSize(); fontBold.setPixelSize(fnSize+2); } QString header; QString subLine; switch (mode) { case ItemSortSettings::NoCategories: break; + case ItemSortSettings::OneCategory: viewHeaderText(index, &header, &subLine); break; + case ItemSortSettings::CategoryByAlbum: textForAlbum(index, &header, &subLine); break; + case ItemSortSettings::CategoryByFormat: textForFormat(index, &header, &subLine); break; + case ItemSortSettings::CategoryByMonth: textForMonth(index, &header, &subLine); break; } p->setPen(qApp->palette().color(QPalette::HighlightedText)); p->setFont(fontBold); QRect tr; p->drawText(5, 5, d->rect.width(), d->rect.height(), Qt::AlignLeft | Qt::AlignTop, p->fontMetrics().elidedText(header, Qt::ElideRight, d->rect.width() - 10), &tr); int y = tr.height() + 2; p->setFont(fontNormal); p->drawText(5, y, d->rect.width(), d->rect.height() - y, Qt::AlignLeft | Qt::AlignVCenter, p->fontMetrics().elidedText(subLine, Qt::ElideRight, d->rect.width() - 10)); p->restore(); } void ItemCategoryDrawer::viewHeaderText(const QModelIndex& index, QString* header, QString* subLine) const { ItemModel* const sourceModel = index.data(ItemModel::ItemModelPointerRole).value(); if (!sourceModel) { return; } int count = d->view->categoryRange(index).height(); // Add here further model subclasses in use with ItemCategoryDrawer. // Note you need a Q_OBJECT in the class's header for this to work. + ItemAlbumModel* const albumModel = qobject_cast(sourceModel); if (albumModel) { QList albums = albumModel->currentAlbums(); Album* album = nullptr; if (albums.isEmpty()) { return; } album = albums.first(); if (!album) { return; } switch (album->type()) { case Album::PHYSICAL: textForPAlbum(static_cast(album), albumModel->isRecursingAlbums(), count, header, subLine); break; + case Album::TAG: textForTAlbum(static_cast(album), albumModel->isRecursingTags(), count, header, subLine); break; + case Album::DATE: textForDAlbum(static_cast(album), count, header, subLine); break; + case Album::SEARCH: textForSAlbum(static_cast(album), count, header, subLine); break; + case Album::FACE: default: break; } } } void ItemCategoryDrawer::textForAlbum(const QModelIndex& index, QString* header, QString* subLine) const { int albumId = index.data(ItemFilterModel::CategoryAlbumIdRole).toInt(); PAlbum* const album = AlbumManager::instance()->findPAlbum(albumId); int count = d->view->categoryRange(index).height(); textForPAlbum(album, false, count, header, subLine); } void ItemCategoryDrawer::textForFormat(const QModelIndex& index, QString* header, QString* subLine) const { QString format = index.data(ItemFilterModel::CategoryFormatRole).toString(); format = ItemScanner::formatToString(format); *header = format; int count = d->view->categoryRange(index).height(); *subLine = i18np("1 Item", "%1 Items", count); } void ItemCategoryDrawer::textForMonth(const QModelIndex& index, QString* header, QString* subLine) const { QDate date = index.data(ItemFilterModel::CategoryDateRole).toDate(); *header = date.toString(QLatin1String("MMM yyyy")); int count = d->view->categoryRange(index).height(); *subLine = i18np("1 Item", "%1 Items", count); } void ItemCategoryDrawer::textForPAlbum(PAlbum* album, bool recursive, int count, QString* header, QString* subLine) const { Q_UNUSED(recursive); if (!album) { return; } QDate date = album->date(); QLocale tmpLocale; // day of month with two digits + QString day = tmpLocale.toString(date, QLatin1String("dd")); // short form of the month + QString month = tmpLocale.toString(date, QLatin1String("MMM")); // long form of the year + QString year = tmpLocale.toString(date, QLatin1String("yyyy")); *subLine = i18ncp("%1: day of month with two digits, %2: short month name, %3: year", "Album Date: %2 %3 %4 - 1 Item", "Album Date: %2 %3 %4 - %1 Items", count, day, month, year); if (!album->caption().isEmpty()) { QString caption = album->caption(); *subLine += QLatin1String(" - ") + caption.replace(QLatin1Char('\n'), QLatin1Char(' ')); } *header = album->prettyUrl(); } void ItemCategoryDrawer::textForTAlbum(TAlbum* talbum, bool recursive, int count, QString* header, QString* subLine) const { *header = talbum->title(); if (recursive && talbum->firstChild()) { int n=0; - for (AlbumIterator it(talbum); it.current(); ++it) + for (AlbumIterator it(talbum) ; it.current() ; ++it) { n++; } QString firstPart = i18ncp("%2: a tag title; %3: number of subtags", "%2 including 1 subtag", "%2 including %1 subtags", n, talbum->tagPath(false)); *subLine = i18ncp("%2: the previous string (e.g. 'Foo including 7 subtags'); %1: number of items in tag", "%2 - 1 Item", "%2 - %1 Items", count, firstPart); } else { *subLine = i18np("%2 - 1 Item", "%2 - %1 Items", count, talbum->tagPath(false)); } } void ItemCategoryDrawer::textForSAlbum(SAlbum* salbum, int count, QString* header, QString* subLine) const { QString title = salbum->displayTitle(); *header = title; - if (salbum->isNormalSearch()) + if (salbum->isNormalSearch()) { *subLine = i18np("Keyword Search - 1 Item", "Keyword Search - %1 Items", count); } else if (salbum->isAdvancedSearch()) { *subLine = i18np("Advanced Search - 1 Item", "Advanced Search - %1 Items", count); } else { *subLine = i18np("1 Item", "%1 Items", count); } } void ItemCategoryDrawer::textForDAlbum(DAlbum* album, int count, QString* header, QString* subLine) const { if (album->range() == DAlbum::Month) { *header = i18nc("Month String - Year String", "%1 %2", QLocale().standaloneMonthName(album->date().month(), QLocale::LongFormat), album->date().year()); } else { *header = QString::fromUtf8("%1").arg(album->date().year()); } *subLine = i18np("1 Item", "%1 Items", count); } void ItemCategoryDrawer::updateRectsAndPixmaps(int width) { d->rect = QRect(0, 0, 0, 0); // Title -------------------------------------------------------- QFont fn(d->font); int fnSize = fn.pointSize(); bool usePointSize; if (fnSize > 0) { fn.setPointSize(fnSize+2); usePointSize = true; } else { - fnSize = fn.pixelSize(); + fnSize = fn.pixelSize(); fn.setPixelSize(fnSize+2); usePointSize = false; } fn.setBold(true); QFontMetrics fm(fn); QRect tr = fm.boundingRect(0, 0, width, 0xFFFFFFFF, Qt::AlignLeft | Qt::AlignVCenter, QLatin1String("XXX")); d->rect.setHeight(tr.height()); if (usePointSize) { fn.setPointSize(d->font.pointSize()); } else { fn.setPixelSize(d->font.pixelSize()); } fn.setBold(false); fm = QFontMetrics(fn); tr = fm.boundingRect(0, 0, width, 0xFFFFFFFF, Qt::AlignLeft | Qt::AlignVCenter, QLatin1String("XXX")); d->rect.setHeight(d->rect.height() + tr.height() + 10); d->rect.setWidth(width); d->pixmap = QPixmap(d->rect.width(), d->rect.height()); d->pixmap.fill(qApp->palette().color(QPalette::Highlight)); } } // namespace Digikam diff --git a/core/app/items/utils/itemcategorydrawer.h b/core/app/items/utils/itemcategorydrawer.h index c3581e154f..a4db78b82e 100644 --- a/core/app/items/utils/itemcategorydrawer.h +++ b/core/app/items/utils/itemcategorydrawer.h @@ -1,78 +1,78 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-04-20 * Description : Qt model-view for items - category drawer * * Copyright (C) 2009-2011 by Marcel Wiesweg * * 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 DIGIKAM_ITEM_CATEGORY_DRAWER_H #define DIGIKAM_ITEM_CATEGORY_DRAWER_H // Local includes #include "dcategorydrawer.h" class QStyleOptionViewItem; namespace Digikam { class ItemCategorizedView; class Album; class PAlbum; class TAlbum; class SAlbum; class DAlbum; class ItemCategoryDrawer : public DCategoryDrawer { public: explicit ItemCategoryDrawer(ItemCategorizedView* const parent); ~ItemCategoryDrawer(); - virtual int categoryHeight(const QModelIndex& index, const QStyleOption& option) const; + virtual int categoryHeight(const QModelIndex& index, const QStyleOption& option) const; virtual void drawCategory(const QModelIndex& index, int sortRole, const QStyleOption& option, QPainter* painter) const; - virtual int maximumHeight() const; + virtual int maximumHeight() const; void setLowerSpacing(int spacing); void setDefaultViewOptions(const QStyleOptionViewItem& option); void invalidatePaintingCache(); private: void updateRectsAndPixmaps(int width); - void viewHeaderText(const QModelIndex& index, QString* header, QString* subLine) const; - void textForAlbum(const QModelIndex& index, QString* header, QString* subLine) const; - void textForPAlbum(PAlbum* a, bool recursive, int count, QString* header, QString* subLine) const; - void textForTAlbum(TAlbum* a, bool recursive, int count, QString* header, QString* subLine) const; - void textForSAlbum(SAlbum* a, int count, QString* header, QString* subLine) const; - void textForDAlbum(DAlbum* a, int count, QString* header, QString* subLine) const; - void textForFormat(const QModelIndex& index, QString* header, QString* subLine) const; - void textForMonth(const QModelIndex& index, QString* header, QString* subLine) const; + void viewHeaderText(const QModelIndex& index, QString* header, QString* subLine) const; + void textForAlbum(const QModelIndex& index, QString* header, QString* subLine) const; + void textForPAlbum(PAlbum* a, bool recursive, int count, QString* header, QString* subLine) const; + void textForTAlbum(TAlbum* a, bool recursive, int count, QString* header, QString* subLine) const; + void textForSAlbum(SAlbum* a, int count, QString* header, QString* subLine) const; + void textForDAlbum(DAlbum* a, int count, QString* header, QString* subLine) const; + void textForFormat(const QModelIndex& index, QString* header, QString* subLine) const; + void textForMonth(const QModelIndex& index, QString* header, QString* subLine) const; private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_ITEM_CATEGORY_DRAWER_H diff --git a/core/app/items/utils/itemviewutilities.cpp b/core/app/items/utils/itemviewutilities.cpp index 8298cb16b1..5af714f2c0 100644 --- a/core/app/items/utils/itemviewutilities.cpp +++ b/core/app/items/utils/itemviewutilities.cpp @@ -1,594 +1,612 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-05-04 * Description : Various operation on items * * Copyright (C) 2002-2005 by Renchi Raju * Copyright (C) 2002-2020 by Gilles Caulier * Copyright (C) 2006-2010 by Marcel Wiesweg * Copyright (C) 2009-2010 by Andi Clemens * * 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 "itemviewutilities.h" // Qt includes #include #include #include // KDE includes #include #include // Local includes #include "digikam_debug.h" #include "album.h" #include "albummanager.h" #include "albumselectdialog.h" #include "applicationsettings.h" #include "deletedialog.h" #include "dio.h" #include "iteminfo.h" #include "imagewindow.h" #include "lighttablewindow.h" #include "loadingcacheinterface.h" #include "queuemgrwindow.h" #include "thumbnailloadthread.h" #include "fileactionmngr.h" #include "dfileoperations.h" #include "coredb.h" #include "coredbaccess.h" namespace Digikam { ItemViewUtilities::ItemViewUtilities(QWidget* const parentWidget) : QObject(parentWidget) { m_widget = parentWidget; connect(this, SIGNAL(signalImagesDeleted(QList)), AlbumManager::instance(), SLOT(slotImagesDeleted(QList))); } void ItemViewUtilities::setAsAlbumThumbnail(Album* album, - const ItemInfo& itemInfo) + const ItemInfo& itemInfo) { if (!album) { return; } - if (album->type() == Album::PHYSICAL) + if (album->type() == Album::PHYSICAL) { PAlbum* const palbum = static_cast(album); QString err; AlbumManager::instance()->updatePAlbumIcon(palbum, itemInfo.id(), err); } else if (album->type() == Album::TAG) { TAlbum* const talbum = static_cast(album); QString err; AlbumManager::instance()->updateTAlbumIcon(talbum, QString(), itemInfo.id(), err); } } void ItemViewUtilities::rename(const QUrl& imageUrl, - const QString& newName, - bool overwrite) + const QString& newName, + bool overwrite) { if (imageUrl.isEmpty() || !imageUrl.isLocalFile() || newName.isEmpty()) { return; } DIO::rename(imageUrl, newName, overwrite); } bool ItemViewUtilities::deleteImages(const QList& infos, - const DeleteMode deleteMode) + const DeleteMode deleteMode) { if (infos.isEmpty()) { return false; } QList deleteInfos = infos; QList urlList; QList imageIds; // Buffer the urls for deletion and imageids for notification of the AlbumManager + foreach (const ItemInfo& info, deleteInfos) { urlList << info.fileUrl(); imageIds << info.id(); } DeleteDialog dialog(m_widget); DeleteDialogMode::DeleteMode deleteDialogMode = DeleteDialogMode::NoChoiceTrash; if (deleteMode == ItemViewUtilities::DeletePermanently) { deleteDialogMode = DeleteDialogMode::NoChoiceDeletePermanently; } if (!dialog.confirmDeleteList(urlList, DeleteDialogMode::Files, deleteDialogMode)) { return false; } const bool useTrash = !dialog.shouldDelete(); DIO::del(deleteInfos, useTrash); // Signal the Albummanager about the ids of the deleted images. + emit signalImagesDeleted(imageIds); return true; } void ItemViewUtilities::deleteImagesDirectly(const QList& infos, - const DeleteMode deleteMode) + const DeleteMode deleteMode) { // This method deletes the selected items directly, without confirmation. // It is not used in the default setup. if (infos.isEmpty()) { return; } QList imageIds; foreach (const ItemInfo& info, infos) { imageIds << info.id(); } const bool useTrash = (deleteMode == ItemViewUtilities::DeleteUseTrash); DIO::del(infos, useTrash); // Signal the Albummanager about the ids of the deleted images. + emit signalImagesDeleted(imageIds); } void ItemViewUtilities::notifyFileContentChanged(const QList& urls) { foreach (const QUrl& url, urls) { QString path = url.toLocalFile(); ThumbnailLoadThread::deleteThumbnail(path); + // clean LoadingCache as well - be pragmatic, do it here. + LoadingCacheInterface::fileChanged(path); } } void ItemViewUtilities::createNewAlbumForInfos(const QList& infos, - Album* currentAlbum) + Album* currentAlbum) { if (infos.isEmpty()) { return; } - if (currentAlbum && currentAlbum->type() != Album::PHYSICAL) + if (currentAlbum && (currentAlbum->type() != Album::PHYSICAL)) { currentAlbum = nullptr; } QString header(i18n("

Please select the destination album from the digiKam library to " "move the selected images into.

")); Album* const album = AlbumSelectDialog::selectAlbum(m_widget, static_cast(currentAlbum), header); if (!album) { return; } DIO::move(infos, (PAlbum*)album); } void ItemViewUtilities::insertToLightTableAuto(const QList& all, - const QList& selected, - const ItemInfo& current) + const QList& selected, + const ItemInfo& current) { ItemInfoList list = ItemInfoList(selected); ItemInfo singleInfo = current; - if (list.isEmpty() || (list.size() == 1 && LightTableWindow::lightTableWindow()->isEmpty())) + if (list.isEmpty() || ((list.size() == 1) && LightTableWindow::lightTableWindow()->isEmpty())) { list = ItemInfoList(all); } if (singleInfo.isNull() && !list.isEmpty()) { singleInfo = list.first(); } - insertToLightTable(list, current, list.size() <= 1); + insertToLightTable(list, current, (list.size() <= 1)); } void ItemViewUtilities::insertToLightTable(const QList& list, const ItemInfo& current, bool addTo) { LightTableWindow* const ltview = LightTableWindow::lightTableWindow(); // If addTo is false, the light table will be emptied before adding // the images. + ltview->loadItemInfos(ItemInfoList(list), current, addTo); ltview->setLeftRightItems(ItemInfoList(list), addTo); if (ltview->isHidden()) { ltview->show(); } if (ltview->isMinimized()) { KWindowSystem::unminimizeWindow(ltview->winId()); } KWindowSystem::activateWindow(ltview->winId()); } void ItemViewUtilities::insertToQueueManager(const QList& list, const ItemInfo& current, bool newQueue) { Q_UNUSED(current); QueueMgrWindow* const bqmview = QueueMgrWindow::queueManagerWindow(); if (bqmview->isHidden()) { bqmview->show(); } if (bqmview->isMinimized()) { KWindowSystem::unminimizeWindow(bqmview->winId()); } KWindowSystem::activateWindow(bqmview->winId()); if (newQueue) { bqmview->loadItemInfosToNewQueue(ItemInfoList(list)); } else { bqmview->loadItemInfosToCurrentQueue(ItemInfoList(list)); } } void ItemViewUtilities::insertSilentToQueueManager(const QList& list, - const ItemInfo& /*current*/, - int queueid) + const ItemInfo& /*current*/, + int queueid) { QueueMgrWindow* const bqmview = QueueMgrWindow::queueManagerWindow(); bqmview->loadItemInfos(ItemInfoList(list), queueid); } void ItemViewUtilities::openInfos(const ItemInfo& info, const QList& allInfosToOpen, Album* currentAlbum) { if (info.isNull()) { return; } QFileInfo fi(info.filePath()); QString imagefilter = ApplicationSettings::instance()->getImageFileFilter(); imagefilter += ApplicationSettings::instance()->getRawFileFilter(); // If the current item is not an image file. + if (!imagefilter.contains(fi.suffix().toLower())) { // Openonly the first one from the list. + openInfosWithDefaultApplication(QList() << info); return; } // Run digiKam ImageEditor with all image from current Album. ImageWindow* const imview = ImageWindow::imageWindow(); imview->disconnect(this); connect(imview, SIGNAL(signalURLChanged(QUrl)), this, SIGNAL(editorCurrentUrlChanged(QUrl))); imview->loadItemInfos(ItemInfoList(allInfosToOpen), info, currentAlbum ? i18n("Album \"%1\"", currentAlbum->title()) : QString()); if (imview->isHidden()) { imview->show(); } if (imview->isMinimized()) { KWindowSystem::unminimizeWindow(imview->winId()); } KWindowSystem::activateWindow(imview->winId()); } void ItemViewUtilities::openInfosWithDefaultApplication(const QList& infos) { if (infos.isEmpty()) { return; } QList urls; foreach (const ItemInfo& inf, infos) { urls << inf.fileUrl(); } DFileOperations::openFilesWithDefaultApplication(urls); } namespace { bool lessThanByTimeForItemInfo(const ItemInfo& a, const ItemInfo& b) { - return a.dateTime() < b.dateTime(); + return (a.dateTime() < b.dateTime()); } bool lowerThanByNameForItemInfo(const ItemInfo& a, const ItemInfo& b) { - return a.name() < b.name(); + return (a.name() < b.name()); } bool lowerThanBySizeForItemInfo(const ItemInfo& a, const ItemInfo& b) { - return a.fileSize() < b.fileSize(); + return (a.fileSize() < b.fileSize()); } } // namespace void ItemViewUtilities::createGroupByTimeFromInfoList(const ItemInfoList& itemInfoList) { QList groupingList = itemInfoList; + // sort by time + std::stable_sort(groupingList.begin(), groupingList.end(), lessThanByTimeForItemInfo); QList::iterator it, it2; for (it = groupingList.begin() ; it != groupingList.end() ; ) { const ItemInfo& leader = *it; QList group; QDateTime time = it->dateTime(); if (time.isValid()) { for (it2 = it + 1 ; it2 != groupingList.end() ; ++it2) { if (qAbs(time.secsTo(it2->dateTime())) < 2) { group << *it2; } else { break; } } } else { ++it; continue; } // increment to next item not put in the group + it = it2; if (!group.isEmpty()) { FileActionMngr::instance()->addToGroup(leader, group); } } } void ItemViewUtilities::createGroupByFilenameFromInfoList(const ItemInfoList& itemInfoList) { QList groupingList = itemInfoList; + // sort by Name + std::stable_sort(groupingList.begin(), groupingList.end(), lowerThanByNameForItemInfo); QList::iterator it, it2; for (it = groupingList.begin() ; it != groupingList.end() ; ) { QList group; QString fname = it->name().left(it->name().lastIndexOf(QLatin1Char('.'))); + // don't know the leader yet so put first element also in group + group << *it; for (it2 = it + 1 ; it2 != groupingList.end() ; ++it2) { QString fname2 = it2->name().left(it2->name().lastIndexOf(QLatin1Char('.'))); if (fname == fname2) { group << *it2; } else { break; } } // increment to next item not put in the group + it = it2; if (group.count() > 1) { // sort by filesize and take smallest as leader + std::stable_sort(group.begin(), group.end(), lowerThanBySizeForItemInfo); const ItemInfo& leader = group.takeFirst(); FileActionMngr::instance()->addToGroup(leader, group); } } } namespace { struct Q_DECL_HIDDEN NumberInFilenameMatch { NumberInFilenameMatch() : value(0), containsValue(false) { } explicit NumberInFilenameMatch(const QString& filename) : NumberInFilenameMatch() { if (filename.isEmpty()) { return; } auto firstDigit = std::find_if(filename.begin(), filename.end(), [](const QChar& c) { return c.isDigit(); }); prefix = filename.leftRef(std::distance(filename.begin(), firstDigit)); if (firstDigit == filename.end()) { return; } auto lastDigit = std::find_if(firstDigit, filename.end(), [](const QChar& c) { return !c.isDigit(); }); value = filename.midRef(prefix.size(), std::distance(firstDigit, lastDigit)).toULongLong(&containsValue); suffix = filename.midRef(std::distance(lastDigit, filename.end())); } bool directlyPreceeds(NumberInFilenameMatch const& other) const { if (!containsValue || !other.containsValue) { return false; } if (prefix != other.prefix) { return false; } if (suffix != other.suffix) { return false; } - return (value+1 == other.value); + return ((value + 1) == other.value); } qulonglong value; QStringRef prefix; QStringRef suffix; bool containsValue; }; bool imageMatchesTimelapseGroup(const ItemInfoList& group, const ItemInfo& itemInfo) { if (group.size() < 2) { return true; } auto const timeBetweenPhotos = qAbs(group.first().dateTime() .secsTo(group.last() .dateTime())) / (group.size()-1); auto const predictedNextTimestamp = group.last().dateTime() .addSecs(timeBetweenPhotos); return (qAbs(itemInfo.dateTime().secsTo(predictedNextTimestamp)) <= 1); } } // namespace void ItemViewUtilities::createGroupByTimelapseFromInfoList(const ItemInfoList& itemInfoList) { if (itemInfoList.size() < 3) { return; } ItemInfoList groupingList = itemInfoList; std::stable_sort(groupingList.begin(), groupingList.end(), lowerThanByNameForItemInfo); NumberInFilenameMatch previousNumberMatch; ItemInfoList group; for (const auto& itemInfo : groupingList) { NumberInFilenameMatch numberMatch(itemInfo.name()); // if this is an end of currently processed group + if (!previousNumberMatch.directlyPreceeds(numberMatch) || !imageMatchesTimelapseGroup(group, itemInfo)) { if (group.size() > 2) { FileActionMngr::instance()->addToGroup(group.takeFirst(), group); } group.clear(); } group.append(itemInfo); previousNumberMatch = std::move(numberMatch); } if (group.size() > 2) { FileActionMngr::instance()->addToGroup(group.takeFirst(), group); } } } // namespace Digikam diff --git a/core/app/items/utils/tooltipfiller.cpp b/core/app/items/utils/tooltipfiller.cpp index 006c31c090..1873df2354 100644 --- a/core/app/items/utils/tooltipfiller.cpp +++ b/core/app/items/utils/tooltipfiller.cpp @@ -1,753 +1,761 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-12-10 * Description : album icon view tool tip * * Copyright (C) 2008-2020 by Gilles Caulier * Copyright (C) 2013 by Michael G. Hansen * * 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 "tooltipfiller.h" // Qt includes #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "albummanager.h" #include "collectionlocation.h" #include "collectionmanager.h" #include "applicationsettings.h" #include "album.h" #include "coredbinfocontainers.h" #include "dimgfiltermanager.h" #include "ditemtooltip.h" #include "filteraction.h" #include "iteminfo.h" #include "itempropertiestab.h" #include "colorlabelwidget.h" #include "picklabelwidget.h" #include "albumthumbnailloader.h" #include "thumbnailsize.h" namespace Digikam { QString ToolTipFiller::imageInfoTipContents(const ItemInfo& info) { QString str; ApplicationSettings* const settings = ApplicationSettings::instance(); DToolTipStyleSheet cnt(settings->getToolTipsFont()); ImageCommonContainer commonInfo = info.imageCommonContainer(); ImageMetadataContainer photoInfo = info.imageMetadataContainer(); VideoMetadataContainer videoInfo = info.videoMetadataContainer(); QString tip = cnt.tipHeader; // -- File properties ---------------------------------------------- if (settings->getToolTipsShowFileName() || settings->getToolTipsShowFileDate() || settings->getToolTipsShowFileSize() || settings->getToolTipsShowImageType() || settings->getToolTipsShowImageDim() || settings->getToolTipsShowImageAR()) { tip += cnt.headBeg + i18n("File Properties") + cnt.headEnd; if (settings->getToolTipsShowFileName()) { tip += cnt.cellBeg + i18nc("filename", "Name:") + cnt.cellMid; tip += commonInfo.fileName + cnt.cellEnd; } if (settings->getToolTipsShowFileDate()) { QDateTime modifiedDate = commonInfo.fileModificationDate; str = QLocale().toString(modifiedDate, QLocale::ShortFormat); tip += cnt.cellBeg + i18n("Date:") + cnt.cellMid + str + cnt.cellEnd; } if (settings->getToolTipsShowFileSize()) { tip += cnt.cellBeg + i18n("Size:") + cnt.cellMid; QString localeFileSize = QLocale().toString(commonInfo.fileSize); str = i18n("%1 (%2)", ItemPropertiesTab::humanReadableBytesCount(commonInfo.fileSize), localeFileSize); tip += str + cnt.cellEnd; } if (settings->getToolTipsShowImageType()) { tip += cnt.cellBeg + i18n("Type:") + cnt.cellMid + commonInfo.format + cnt.cellEnd; } if (settings->getToolTipsShowImageDim()) { - if (commonInfo.width == 0 || commonInfo.height == 0) + if ((commonInfo.width == 0) || (commonInfo.height == 0)) { str = i18nc("unknown / invalid image dimension", "Unknown"); } else { QString mpixels; mpixels.setNum(commonInfo.width*commonInfo.height/1000000.0, 'f', 2); str = i18nc("width x height (megapixels Mpx)", "%1x%2 (%3Mpx)", commonInfo.width, commonInfo.height, mpixels); } tip += cnt.cellBeg + i18n("Dimensions:") + cnt.cellMid + str + cnt.cellEnd; } if (settings->getToolTipsShowImageAR()) { if (!ItemPropertiesTab::aspectRatioToString(commonInfo.width, commonInfo.height, str)) { str = i18nc("unknown / invalid image aspect ratio", "Unknown"); } tip += cnt.cellBeg + i18n("Aspect Ratio:") + cnt.cellMid + str + cnt.cellEnd; } } // -- Photograph Info ---------------------------------------------------- if (settings->getToolTipsShowPhotoMake() || settings->getToolTipsShowPhotoLens() || settings->getToolTipsShowPhotoDate() || settings->getToolTipsShowPhotoFocal() || settings->getToolTipsShowPhotoExpo() || settings->getToolTipsShowPhotoMode() || settings->getToolTipsShowPhotoFlash() || settings->getToolTipsShowPhotoWB()) { if (!photoInfo.allFieldsNull || commonInfo.creationDate.isValid()) { QString metaStr; tip += cnt.headBeg + i18n("Photograph Properties") + cnt.headEnd; if (settings->getToolTipsShowPhotoMake()) { ItemPropertiesTab::shortenedMakeInfo(photoInfo.make); ItemPropertiesTab::shortenedModelInfo(photoInfo.model); str = QString::fromUtf8("%1 / %2").arg(photoInfo.make.isEmpty() ? cnt.unavailable : photoInfo.make) .arg(photoInfo.model.isEmpty() ? cnt.unavailable : photoInfo.model); if (str.length() > cnt.maxStringLength) { str = str.left(cnt.maxStringLength-3) + QLatin1String("..."); } metaStr += cnt.cellBeg + i18n("Make/Model:") + cnt.cellMid + str.toHtmlEscaped() + cnt.cellEnd; } if (settings->getToolTipsShowPhotoLens()) { str = photoInfo.lens.isEmpty() ? cnt.unavailable : photoInfo.lens; QString lens = i18nc("camera lens", "Lens:"); if (str.length() > cnt.maxStringLength) { int space = str.lastIndexOf(QLatin1Char(' '), cnt.maxStringLength); if (space == -1) + { space = cnt.maxStringLength; + } metaStr += cnt.cellBeg + lens + cnt.cellMid + str.left(space).toHtmlEscaped() + cnt.cellEnd; str = str.mid(space+1); lens = QString(); } if (str.length() > cnt.maxStringLength) { str = str.left(cnt.maxStringLength-3) + QLatin1String("..."); } metaStr += cnt.cellBeg + lens + cnt.cellMid + str.toHtmlEscaped() + cnt.cellEnd; } if (settings->getToolTipsShowPhotoDate()) { if (commonInfo.creationDate.isValid()) { str = QLocale().toString(commonInfo.creationDate, QLocale::ShortFormat); if (str.length() > cnt.maxStringLength) { str = str.left(cnt.maxStringLength-3) + QLatin1String("..."); } metaStr += cnt.cellBeg + i18nc("creation date of the image", "Created:") + cnt.cellMid + str.toHtmlEscaped() + cnt.cellEnd; } else { metaStr += cnt.cellBeg + i18nc("creation date of the image", "Created:") + cnt.cellMid + cnt.unavailable.toHtmlEscaped() + cnt.cellEnd; } } if (settings->getToolTipsShowPhotoFocal()) { str = photoInfo.aperture.isEmpty() ? cnt.unavailable : photoInfo.aperture; if (photoInfo.focalLength35.isEmpty()) { str += QString::fromUtf8(" / %1").arg(photoInfo.focalLength.isEmpty() ? cnt.unavailable : photoInfo.focalLength); } else { str += QString::fromUtf8(" / %1").arg(i18n("%1 (%2)",photoInfo.focalLength,photoInfo.focalLength35)); } if (str.length() > cnt.maxStringLength) { str = str.left(cnt.maxStringLength-3) + QLatin1String("..."); } metaStr += cnt.cellBeg + i18n("Aperture/Focal:") + cnt.cellMid + str.toHtmlEscaped() + cnt.cellEnd; } if (settings->getToolTipsShowPhotoExpo()) { str = QString::fromUtf8("%1 / %2").arg(photoInfo.exposureTime.isEmpty() ? cnt.unavailable : photoInfo.exposureTime) .arg(photoInfo.sensitivity.isEmpty() ? cnt.unavailable : i18n("%1 ISO",photoInfo.sensitivity)); if (str.length() > cnt.maxStringLength) { str = str.left(cnt.maxStringLength-3) + QLatin1String("..."); } metaStr += cnt.cellBeg + i18n("Exposure/Sensitivity:") + cnt.cellMid + str.toHtmlEscaped() + cnt.cellEnd; } if (settings->getToolTipsShowPhotoMode()) { - if (photoInfo.exposureMode.isEmpty() && photoInfo.exposureProgram.isEmpty()) + if (photoInfo.exposureMode.isEmpty() && photoInfo.exposureProgram.isEmpty()) { str = cnt.unavailable; } else if (!photoInfo.exposureMode.isEmpty() && photoInfo.exposureProgram.isEmpty()) { str = photoInfo.exposureMode; } else if (photoInfo.exposureMode.isEmpty() && !photoInfo.exposureProgram.isEmpty()) { str = photoInfo.exposureProgram; } else { str = QString::fromUtf8("%1 / %2").arg(photoInfo.exposureMode).arg(photoInfo.exposureProgram); } if (str.length() > cnt.maxStringLength) { str = str.left(cnt.maxStringLength-3) + QLatin1String("..."); } metaStr += cnt.cellBeg + i18n("Mode/Program:") + cnt.cellMid + str.toHtmlEscaped() + cnt.cellEnd; } if (settings->getToolTipsShowPhotoFlash()) { str = photoInfo.flashMode.isEmpty() ? cnt.unavailable : photoInfo.flashMode; if (str.length() > cnt.maxStringLength) { str = str.left(cnt.maxStringLength-3) + QLatin1String("..."); } metaStr += cnt.cellBeg + i18nc("camera flash settings", "Flash:") + cnt.cellMid + str.toHtmlEscaped() + cnt.cellEnd; } if (settings->getToolTipsShowPhotoWB()) { str = photoInfo.whiteBalance.isEmpty() ? cnt.unavailable : photoInfo.whiteBalance; if (str.length() > cnt.maxStringLength) { str = str.left(cnt.maxStringLength-3) + QLatin1String("..."); } metaStr += cnt.cellBeg + i18n("White Balance:") + cnt.cellMid + str.toHtmlEscaped() + cnt.cellEnd; } tip += metaStr; } } // -- Video Metadata Info ---------------------------------------------------- if (settings->getToolTipsShowVideoAspectRatio() || settings->getToolTipsShowVideoDuration() || settings->getToolTipsShowVideoFrameRate() || settings->getToolTipsShowVideoVideoCodec() || settings->getToolTipsShowVideoAudioBitRate() || settings->getToolTipsShowVideoAudioChannelType() || settings->getToolTipsShowVideoAudioCodec()) { if (!videoInfo.allFieldsNull) { QString metaStr; tip += cnt.headBeg + i18n("Audio/Video Properties") + cnt.headEnd; if (settings->getToolTipsShowVideoAspectRatio()) { str = videoInfo.aspectRatio.isEmpty() ? cnt.unavailable : videoInfo.aspectRatio; if (str.length() > cnt.maxStringLength) { str = str.left(cnt.maxStringLength-3) + QLatin1String("..."); } metaStr += cnt.cellBeg + i18n("Aspect Ratio:") + cnt.cellMid + str.toHtmlEscaped() + cnt.cellEnd; } if (settings->getToolTipsShowVideoDuration()) { QString durationString; bool ok = false; const int durationVal = videoInfo.duration.toInt(&ok); if (ok) { unsigned int r, d, h, m, s, f; r = qAbs(durationVal); d = r / 86400000; r = r % 86400000; h = r / 3600000; r = r % 3600000; m = r / 60000; r = r % 60000; s = r / 1000; f = r % 1000; durationString = QString().asprintf("%d.%02d:%02d:%02d.%03d", d, h, m, s, f); } str = videoInfo.duration.isEmpty() ? cnt.unavailable : durationString; if (str.length() > cnt.maxStringLength) { str = str.left(cnt.maxStringLength-3) + QLatin1String("..."); } metaStr += cnt.cellBeg + i18n("Duration:") + cnt.cellMid + str.toHtmlEscaped() + cnt.cellEnd; } if (settings->getToolTipsShowVideoFrameRate()) { QString frameRateString; bool ok; const double frameRateDouble = videoInfo.frameRate.toDouble(&ok); if (ok) { frameRateString = QLocale().toString(frameRateDouble); } str = videoInfo.frameRate.isEmpty() ? cnt.unavailable : frameRateString; if (str.length() > cnt.maxStringLength) { str = str.left(cnt.maxStringLength-3) + QLatin1String("..."); } metaStr += cnt.cellBeg + i18n("Frame Rate:") + cnt.cellMid + str.toHtmlEscaped() + i18n(" fps") + cnt.cellEnd; } if (settings->getToolTipsShowVideoVideoCodec()) { str = videoInfo.videoCodec.isEmpty() ? cnt.unavailable : videoInfo.videoCodec; if (str.length() > cnt.maxStringLength) { str = str.left(cnt.maxStringLength-3) + QLatin1String("..."); } metaStr += cnt.cellBeg + i18n("Video Codec:") + cnt.cellMid + str.toHtmlEscaped() + cnt.cellEnd; } if (settings->getToolTipsShowVideoAudioBitRate()) { QString audioBitRateString = str; bool ok; const int audioBitRateInt = videoInfo.audioBitRate.toInt(&ok); if (ok) { audioBitRateString = QLocale().toString(audioBitRateInt); } str = videoInfo.audioBitRate.isEmpty() ? cnt.unavailable : audioBitRateString; if (str.length() > cnt.maxStringLength) { str = str.left(cnt.maxStringLength-3) + QLatin1String("..."); } metaStr += cnt.cellBeg + i18n("Audio Bit Rate:") + cnt.cellMid + str.toHtmlEscaped() + cnt.cellEnd; } if (settings->getToolTipsShowVideoAudioChannelType()) { str = videoInfo.audioChannelType.isEmpty() ? cnt.unavailable : videoInfo.audioChannelType; if (str.length() > cnt.maxStringLength) { str = str.left(cnt.maxStringLength-3) + QLatin1String("..."); } metaStr += cnt.cellBeg + i18n("Audio Channel Type:") + cnt.cellMid + str.toHtmlEscaped() + cnt.cellEnd; } if (settings->getToolTipsShowVideoAudioCodec()) { str = videoInfo.audioCodec.isEmpty() ? cnt.unavailable : videoInfo.audioCodec; if (str.length() > cnt.maxStringLength) { str = str.left(cnt.maxStringLength-3) + QLatin1String("..."); } metaStr += cnt.cellBeg + i18n("Audio Codec:") + cnt.cellMid + str.toHtmlEscaped() + cnt.cellEnd; } tip += metaStr; } } // -- digiKam properties ------------------------------------------ if (settings->getToolTipsShowAlbumName() || settings->getToolTipsShowTitles() || settings->getToolTipsShowComments() || settings->getToolTipsShowTags() || settings->getToolTipsShowLabelRating()) { tip += cnt.headBeg + i18n("digiKam Properties") + cnt.headEnd; if (settings->getToolTipsShowAlbumName()) { PAlbum* const album = AlbumManager::instance()->findPAlbum(info.albumId()); if (album) { tip += cnt.cellSpecBeg + i18n("Album:") + cnt.cellSpecMid + album->albumPath().remove(0, 1) + cnt.cellSpecEnd; } } if (settings->getToolTipsShowTitles()) { str = info.title(); if (str.isEmpty()) { str = QLatin1String("---"); } tip += cnt.cellSpecBeg + i18nc("title of the file", "Title:") + cnt.cellSpecMid + cnt.breakString(str) + cnt.cellSpecEnd; } if (settings->getToolTipsShowComments()) { str = info.comment(); if (str.isEmpty()) { str = QLatin1String("---"); } tip += cnt.cellSpecBeg + i18nc("caption of the file", "Caption:") + cnt.cellSpecMid + cnt.breakString(str) + cnt.cellSpecEnd; } if (settings->getToolTipsShowTags()) { QStringList tagPaths = AlbumManager::instance()->tagPaths(info.tagIds(), false); tagPaths.sort(); QString tags(i18n("Tags:")); if (tagPaths.isEmpty()) { tip += cnt.cellSpecBeg + tags + cnt.cellSpecMid + QLatin1String("---") + cnt.cellSpecEnd; } else { QString title = tags; QString tagText; for (int i = 0 ; i < tagPaths.size() ; ++i) { tagText = tagPaths.at(i); if (tagText.size() > cnt.maxStringLength) { tagText = cnt.elidedText(tagPaths.at(i), Qt::ElideLeft); } tip += cnt.cellSpecBeg + title + cnt.cellSpecMid + tagText + cnt.cellSpecEnd; title.clear(); } } } if (settings->getToolTipsShowLabelRating()) { str = PickLabelWidget::labelPickName((PickLabel)info.pickLabel()); str += QLatin1String(" / "); str += ColorLabelWidget::labelColorName((ColorLabel)info.colorLabel()); str += QLatin1String(" / "); int rating = info.rating(); - if (rating > RatingMin && rating <= RatingMax) + if ((rating > RatingMin) && (rating <= RatingMax)) { for (int i = 0 ; i < rating ; ++i) { str += QChar(0x2730); str += QLatin1Char(' '); } } else { str += QLatin1String("---"); } tip += cnt.cellSpecBeg + i18n("Labels:") + cnt.cellSpecMid + str + cnt.cellSpecEnd; } } tip += cnt.tipFooter; return tip; } QString ToolTipFiller::albumTipContents(PAlbum* const album, int count) { if (!album || album->isTrashAlbum()) { return QString(); } QString str; ApplicationSettings* const settings = ApplicationSettings::instance(); DToolTipStyleSheet cnt(settings->getToolTipsFont()); QString tip = cnt.tipHeader; if (settings->getToolTipsShowAlbumTitle() || settings->getToolTipsShowAlbumDate() || settings->getToolTipsShowAlbumCollection() || settings->getToolTipsShowAlbumCategory() || settings->getToolTipsShowAlbumCaption()) { tip += cnt.headBeg + i18n("Album Properties") + cnt.headEnd; if (settings->getToolTipsShowAlbumTitle()) { tip += cnt.cellBeg + i18n("Name:") + cnt.cellMid; tip += album->title() + cnt.cellEnd; } if (settings->getShowFolderTreeViewItemsCount()) { tip += cnt.cellBeg + i18n("Items:") + cnt.cellMid; tip += QString::number(count) + cnt.cellEnd; } if (settings->getToolTipsShowAlbumCollection()) { tip += cnt.cellBeg + i18n("Collection:") + cnt.cellMid; CollectionLocation col = CollectionManager::instance()->locationForAlbumRootId(album->albumRootId()); tip += !col.isNull() ? col.label() : QString() + cnt.cellEnd; } if (settings->getToolTipsShowAlbumDate()) { QDate date = album->date(); str = QLocale().toString(date, QLocale::ShortFormat); tip += cnt.cellBeg + i18n("Date:") + cnt.cellMid + str + cnt.cellEnd; } if (settings->getToolTipsShowAlbumCategory()) { str = album->category(); if (str.isEmpty()) { str = QLatin1String("---"); } tip += cnt.cellSpecBeg + i18n("Category:") + cnt.cellSpecMid + cnt.breakString(str) + cnt.cellSpecEnd; } if (settings->getToolTipsShowAlbumCaption()) { str = album->caption(); if (str.isEmpty()) { str = QLatin1String("---"); } tip += cnt.cellSpecBeg + i18n("Caption:") + cnt.cellSpecMid + cnt.breakString(str) + cnt.cellSpecEnd; } if (settings->getToolTipsShowAlbumPreview()) { tip += cnt.cellSpecBeg + i18n("Preview:") + cnt.cellSpecMid + cnt.imageAsBase64(AlbumThumbnailLoader::instance()->getAlbumThumbnailDirectly(album).toImage()) + //cnt.imageAsBase64(AlbumThumbnailLoader::instance()->getAlbumPreviewDirectly(album, ThumbnailSize::Medium)) + cnt.cellSpecEnd; } } tip += cnt.tipFooter; return tip; } QString ToolTipFiller::filterActionTipContents(const FilterAction& action) { if (action.isNull()) { return QString(); } QString str; DToolTipStyleSheet cnt(ApplicationSettings::instance()->getToolTipsFont()); QString tip = cnt.tipHeader; tip += cnt.headBeg + i18n("Filter") + cnt.headEnd; // Displayable name + tip += cnt.cellBeg + i18n("Name:") + cnt.cellMid + DImgFilterManager::instance()->i18nDisplayableName(action) + cnt.cellEnd; // Category + QString reproducible = QLatin1String("---"); switch (action.category()) { case FilterAction::ReproducibleFilter: reproducible = i18nc("Image filter reproducible: Yes", "Yes"); break; + case FilterAction::ComplexFilter: reproducible = i18nc("Image filter reproducible: Partially", "Partially"); break; + case FilterAction::DocumentedHistory: reproducible = i18nc("Image filter reproducible: No", "No"); break; + default: break; }; tip += cnt.cellBeg + i18n("Reproducible:") + cnt.cellMid + reproducible + cnt.cellEnd; // Description str = action.description(); if (str.isEmpty()) { str = QLatin1String("---"); } tip += cnt.cellSpecBeg + i18nc("Image filter description", "Description:") + cnt.cellSpecMid + cnt.breakString(str) + cnt.cellSpecEnd; // Identifier + version + tip += cnt.cellBeg + i18n("Identifier:") + cnt.cellMid + action.identifier() + QLatin1String(" (v") + QString::number(action.version()) + QLatin1String(") ") + cnt.cellEnd; if (action.hasParameters()) { tip += cnt.headBeg + i18n("Technical Parameters") + cnt.headEnd; const QHash& params = action.parameters(); QList keys = params.keys(); std::sort(keys.begin(), keys.end()); foreach (const QString& key, keys) { QHash::const_iterator it; - for (it = params.find(key) ; it != params.end() && it.key() == key ; ++it) + for (it = params.find(key) ; ((it != params.end()) && (it.key() == key)) ; ++it) { if (it.key().isEmpty() || !it.value().isValid()) { continue; } if (it.key().startsWith(QLatin1String("curveData"))) { str = i18n("Binary Data"); } else { str = it.value().toString(); } if (str.length() > cnt.maxStringLength) { str = cnt.elidedText(str, Qt::ElideRight); } QString key = it.key(); QChar first = key.at(0); if (first.isLower()) { key.replace(0, 1, first.toUpper()); } tip += cnt.cellBeg + key + cnt.cellMid + str + cnt.cellEnd; } } } tip += cnt.tipFooter; return tip; } } // namespace Digikam diff --git a/core/app/views/tableview/tableview_column_digikam.cpp b/core/app/views/tableview/tableview_column_digikam.cpp index 557099bd09..44b8eec580 100644 --- a/core/app/views/tableview/tableview_column_digikam.cpp +++ b/core/app/views/tableview/tableview_column_digikam.cpp @@ -1,563 +1,563 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2013-02-28 * Description : Table view column helpers: Digikam properties * * Copyright (C) 2017-2020 by Gilles Caulier * Copyright (C) 2013 by Michael G. Hansen * * 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 "tableview_column_digikam.h" // Qt includes #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "coredbfields.h" #include "albummanager.h" #include "digikam_globals.h" #include "iteminfo.h" namespace Digikam { namespace TableViewColumns { ColumnDigikamProperties::ColumnDigikamProperties(TableViewShared* const tableViewShared, const TableViewColumnConfiguration& pConfiguration, const SubColumn pSubColumn, QObject* const parent) : TableViewColumn(tableViewShared, pConfiguration, parent), subColumn(pSubColumn) { } ColumnDigikamProperties::~ColumnDigikamProperties() { } QStringList ColumnDigikamProperties::getSubColumns() { QStringList columns; columns << QLatin1String("digikam-rating") << QLatin1String("digikam-picklabel") << QLatin1String("digikam-colorlabel") << QLatin1String("digikam-title") << QLatin1String("digikam-caption") << QLatin1String("digikam-tags"); return columns; } TableViewColumnDescription ColumnDigikamProperties::getDescription() { TableViewColumnDescription description(QLatin1String("digikam-properties"), i18n("digiKam properties")); description.setIcon(QLatin1String("edit-text-frame-update")); description.addSubColumn(TableViewColumnDescription(QLatin1String("digikam-rating"), i18n("Rating")).setIcon(QLatin1String("draw-star"))); description.addSubColumn(TableViewColumnDescription(QLatin1String("digikam-picklabel"), i18n("Pick label")).setIcon(QLatin1String("flag"))); description.addSubColumn(TableViewColumnDescription(QLatin1String("digikam-colorlabel"), i18n("Color label"))); /// @todo This column will show the 'default' title. Add a configuration dialog to choose different languages. description.addSubColumn(TableViewColumnDescription(QLatin1String("digikam-title"), i18n("Title"))); /// @todo This column will show the 'default' caption. Add a configuration dialog to choose different languages. description.addSubColumn(TableViewColumnDescription(QLatin1String("digikam-caption"), i18n("Caption"))); - description.addSubColumn(TableViewColumnDescription(QLatin1String("digikam-tags"), i18n("Tags")).setIcon(QLatin1String("tags"))); + description.addSubColumn(TableViewColumnDescription(QLatin1String("digikam-tags"), i18n("Tags")).setIcon(QLatin1String("tag"))); return description; } QString ColumnDigikamProperties::getTitle() const { switch (subColumn) { case SubColumnRating: { return i18n("Rating"); } case SubColumnPickLabel: { return i18n("Pick label"); } case SubColumnColorLabel: { return i18n("Color label"); } case SubColumnTitle: { return i18n("Title"); } case SubColumnCaption: { return i18n("Caption"); } case SubColumnTags: { return i18n("Tags"); } } return QString(); } TableViewColumn::ColumnFlags ColumnDigikamProperties::getColumnFlags() const { ColumnFlags flags(ColumnNoFlags); if ( (subColumn == SubColumnRating) || (subColumn == SubColumnPickLabel) || (subColumn == SubColumnColorLabel) ) { flags |= ColumnCustomSorting; } return flags; } QVariant ColumnDigikamProperties::data(TableViewModel::Item* const item, const int role) const { if ( (role != Qt::DisplayRole) && (role != Qt::TextAlignmentRole) && (role != Qt::ForegroundRole) ) { return QVariant(); } if (role == Qt::TextAlignmentRole) { switch (subColumn) { case SubColumnRating: { return QVariant(Qt::Alignment(Qt::AlignCenter)); } default: { return QVariant(); } } } if (role == Qt::ForegroundRole) { switch (subColumn) { case SubColumnPickLabel: { const ItemInfo info = s->tableViewModel->infoFromItem(item); const PickLabel pickLabel = PickLabel(info.pickLabel()); QColor labelColor; switch (pickLabel) { case NoPickLabel: { labelColor = Qt::darkGray; break; } case RejectedLabel: { labelColor = Qt::red; break; } case PendingLabel: { // yellow is too hard to read labelColor = Qt::darkYellow; break; } case AcceptedLabel: { // green is too hard to read labelColor = Qt::darkGreen; break; } default: { break; } } QBrush labelBrush(labelColor); return QVariant::fromValue(labelBrush); } case SubColumnColorLabel: { const ItemInfo info = s->tableViewModel->infoFromItem(item); const ColorLabel colorLabel = ColorLabel(info.colorLabel()); QColor labelColor; switch (colorLabel) { case NoColorLabel: { labelColor = Qt::lightGray; break; } case RedLabel: { labelColor = Qt::red; break; } case OrangeLabel: { labelColor = QColor(0xff, 0x80, 0x00); break; } case YellowLabel: { labelColor = Qt::darkYellow; break; } case GreenLabel: { labelColor = Qt::darkGreen; break; } case BlueLabel: { labelColor = Qt::darkBlue; break; } case MagentaLabel: { labelColor = Qt::magenta; break; } case GrayLabel: { labelColor = Qt::darkGray; break; } case BlackLabel: { labelColor = Qt::black; break; } case WhiteLabel: { labelColor = Qt::white; break; } default: { break; } } QBrush labelBrush(labelColor); return QVariant::fromValue(labelBrush); } default: { return QVariant(); } } } const ItemInfo info = s->tableViewModel->infoFromItem(item); /** * @todo Also display the pick label icon? * Make display of text/icon configurable. */ switch (subColumn) { case SubColumnRating: { const int itemRating = info.rating(); if (itemRating <= 0) { // no rating return QString(); } return QLocale().toString(itemRating); } case SubColumnPickLabel: { const PickLabel pickLabel = PickLabel(info.pickLabel()); QString labelString; switch (pickLabel) { case NoPickLabel: { labelString = i18n("None"); break; } case RejectedLabel: { labelString = i18n("Rejected"); break; } case PendingLabel: { labelString = i18n("Pending"); break; } case AcceptedLabel: { labelString = i18n("Accepted"); break; } default: { break; } } return labelString; } case SubColumnColorLabel: { const ColorLabel colorLabel = ColorLabel(info.colorLabel()); QString labelString; switch (colorLabel) { case NoColorLabel: { labelString = i18n("None"); break; } case RedLabel: { labelString = i18n("Red"); break; } case OrangeLabel: { labelString = i18n("Orange"); break; } case YellowLabel: { labelString = i18n("Yellow"); break; } case GreenLabel: { labelString = i18n("Green"); break; } case BlueLabel: { labelString = i18n("Blue"); break; } case MagentaLabel: { labelString = i18n("Magenta"); break; } case GrayLabel: { labelString = i18n("Gray"); break; } case BlackLabel: { labelString = i18n("Black"); break; } case WhiteLabel: { labelString = i18n("White"); break; } default: { break; } } return labelString; } case SubColumnTitle: { const QString title = info.title(); return title; } case SubColumnCaption: { const QString caption = info.comment(); return caption; } case SubColumnTags: { QStringList tagPaths = AlbumManager::instance()->tagPaths(info.tagIds(), false); tagPaths.sort(); return tagPaths.join(QLatin1Char('\n')); } } return QVariant(); } TableViewColumn::ColumnCompareResult ColumnDigikamProperties::compare(TableViewModel::Item* const itemA, TableViewModel::Item* const itemB) const { const ItemInfo infoA = s->tableViewModel->infoFromItem(itemA); const ItemInfo infoB = s->tableViewModel->infoFromItem(itemB); switch (subColumn) { case SubColumnRating: { /// @todo Handle un-rated vs rated items differently? const int ratingA = infoA.rating(); const int ratingB = infoB.rating(); return compareHelper(ratingA, ratingB); } case SubColumnPickLabel: { /// @todo Handle un-rated vs rated items differently? const int pickLabelA = infoA.pickLabel(); const int pickLabelB = infoB.pickLabel(); return compareHelper(pickLabelA, pickLabelB); } case SubColumnColorLabel: { /// @todo Handle un-rated vs rated items differently? const int colorLabelA = infoA.colorLabel(); const int colorLabelB = infoB.colorLabel(); return compareHelper(colorLabelA, colorLabelB); } default: { qCWarning(DIGIKAM_GENERAL_LOG) << "item: unimplemented comparison, subColumn=" << subColumn; return CmpEqual; } } } bool Digikam::TableViewColumns::ColumnDigikamProperties::columnAffectedByChangeset(const Digikam::ImageChangeset& imageChangeset) const { switch (subColumn) { case SubColumnTitle: case SubColumnCaption: case SubColumnTags: { return true; /// @todo These are not the right flags for these columns /* return imageChangeset.changes() & DatabaseFields::ItemCommentsAll; */ } case SubColumnRating: { return (imageChangeset.changes() & DatabaseFields::Rating); } case SubColumnPickLabel: { return (imageChangeset.changes() & DatabaseFields::PickLabel); } case SubColumnColorLabel: { return (imageChangeset.changes() & DatabaseFields::ColorLabel); } } return false; } } // namespace TableViewColumns } // namespace Digikam