diff --git a/core/app/items/views/digikamitemview.cpp b/core/app/items/views/digikamitemview.cpp index ecd5aeddfb..8d2fe29110 100644 --- a/core/app/items/views/digikamitemview.cpp +++ b/core/app/items/views/digikamitemview.cpp @@ -1,629 +1,629 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-04-24 * Description : Qt model-view for items * * Copyright (C) 2009-2011 by Marcel Wiesweg * Copyright (C) 2009-2020 by Gilles Caulier * Copyright (C) 2011 by Andi Clemens * Copyright (C) 2013 by Michael G. Hansen * Copyright (C) 2014 by Mohamed_Anwer * Copyright (C) 2017 by Simon Frei * * This program is free software you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "digikamitemview_p.h" // Qt includes #include #include #include #include #include #include // Local includes #include "digikam_debug.h" #include "albummanager.h" #include "coredb.h" #include "coredboperationgroup.h" #include "advancedrenamedialog.h" #include "advancedrenameprocessdialog.h" #include "applicationsettings.h" #include "assignnameoverlay.h" #include "contextmenuhelper.h" #include "coredbaccess.h" #include "ddragobjects.h" #include "digikamapp.h" #include "digikamitemdelegate.h" #include "itemfacedelegate.h" #include "dio.h" #include "groupindicatoroverlay.h" #include "itemalbumfiltermodel.h" #include "itemalbummodel.h" #include "itemdragdrop.h" #include "itemratingoverlay.h" #include "itemfullscreenoverlay.h" #include "itemcoordinatesoverlay.h" #include "tagslineeditoverlay.h" #include "itemviewutilities.h" #include "imagewindow.h" #include "fileactionmngr.h" #include "fileactionprogress.h" #include "thumbnailloadthread.h" #include "tagregion.h" #include "addtagslineedit.h" #include "facerejectionoverlay.h" #include "facetagsiface.h" namespace Digikam { DigikamItemView::DigikamItemView(QWidget* const parent) : ItemCategorizedView(parent), d(new Private(this)) { installDefaultModels(); d->editPipeline.plugDatabaseEditor(); d->editPipeline.plugTrainer(); d->editPipeline.construct(); connect(&d->editPipeline, SIGNAL(scheduled()), this, SLOT(slotInitProgressIndicator())); d->normalDelegate = new DigikamItemDelegate(this); d->faceDelegate = new ItemFaceDelegate(this); setItemDelegate(d->normalDelegate); setSpacing(10); ApplicationSettings* const settings = ApplicationSettings::instance(); imageFilterModel()->setCategorizationMode(ItemSortSettings::CategoryByAlbum); imageAlbumModel()->setThumbnailLoadThread(ThumbnailLoadThread::defaultIconViewThread()); setThumbnailSize(ThumbnailSize(settings->getDefaultIconSize())); imageAlbumModel()->setPreloadThumbnails(true); imageModel()->setDragDropHandler(new ItemDragDropHandler(imageModel())); setDragEnabled(true); setAcceptDrops(true); setDropIndicatorShown(false); setToolTipEnabled(settings->showToolTipsIsValid()); imageFilterModel()->setStringTypeNatural(settings->isStringTypeNatural()); imageFilterModel()->setSortRole((ItemSortSettings::SortRole)settings->getImageSortOrder()); imageFilterModel()->setSortOrder((ItemSortSettings::SortOrder)settings->getImageSorting()); imageFilterModel()->setCategorizationMode((ItemSortSettings::CategorizationMode)settings->getImageSeparationMode()); imageFilterModel()->setCategorizationSortOrder((ItemSortSettings::SortOrder) settings->getImageSeparationSortOrder()); // selection overlay addSelectionOverlay(d->normalDelegate); addSelectionOverlay(d->faceDelegate); // rotation overlays d->rotateLeftOverlay = ItemRotateOverlay::left(this); d->rotateRightOverlay = ItemRotateOverlay::right(this); d->fullscreenOverlay = ItemFullScreenOverlay::instance(this); d->updateOverlays(); // rating overlay ItemRatingOverlay* const ratingOverlay = new ItemRatingOverlay(this); addOverlay(ratingOverlay); // face overlays // NOTE: order to plug this overlay is important, else rejection cant be suitable (see bug #324759). addAssignNameOverlay(d->faceDelegate); addRejectionOverlay(d->faceDelegate); GroupIndicatorOverlay* const groupOverlay = new GroupIndicatorOverlay(this); addOverlay(groupOverlay); addOverlay(new ItemCoordinatesOverlay(this)); connect(ratingOverlay, SIGNAL(ratingEdited(QList,int)), this, SLOT(assignRating(QList,int))); connect(groupOverlay, SIGNAL(toggleGroupOpen(QModelIndex)), this, SLOT(groupIndicatorClicked(QModelIndex))); connect(groupOverlay, SIGNAL(showButtonContextMenu(QModelIndex,QContextMenuEvent*)), this, SLOT(showGroupContextMenu(QModelIndex,QContextMenuEvent*))); d->utilities = new ItemViewUtilities(this); connect(imageModel()->dragDropHandler(), SIGNAL(assignTags(QList,QList)), FileActionMngr::instance(), SLOT(assignTags(QList,QList))); connect(imageModel()->dragDropHandler(), SIGNAL(addToGroup(ItemInfo,QList)), FileActionMngr::instance(), SLOT(addToGroup(ItemInfo,QList))); connect(imageModel()->dragDropHandler(), SIGNAL(dragDropSort(ItemInfo,QList)), this, SLOT(dragDropSort(ItemInfo,QList))); connect(d->utilities, SIGNAL(editorCurrentUrlChanged(QUrl)), this, SLOT(setCurrentUrlWhenAvailable(QUrl))); // --- NOTE: use dynamic binding as slotSetupChanged() is a virtual method which can be re-implemented in derived classes. connect(settings, &ApplicationSettings::setupChanged, this, &DigikamItemView::slotSetupChanged); this->slotSetupChanged(); // --- } DigikamItemView::~DigikamItemView() { delete d; } ItemViewUtilities* DigikamItemView::utilities() const { return d->utilities; } void DigikamItemView::setThumbnailSize(const ThumbnailSize& size) { imageThumbnailModel()->setPreloadThumbnailSize(size); ItemCategorizedView::setThumbnailSize(size); } ItemInfoList DigikamItemView::allItemInfos(bool grouping) const { if (grouping) { return resolveGrouping(ItemCategorizedView::allItemInfos()); } return ItemCategorizedView::allItemInfos(); } ItemInfoList DigikamItemView::selectedItemInfos(bool grouping) const { if (grouping) { return resolveGrouping(ItemCategorizedView::selectedItemInfos()); } return ItemCategorizedView::selectedItemInfos(); } ItemInfoList DigikamItemView::selectedItemInfosCurrentFirst(bool grouping) const { if (grouping) { return resolveGrouping(ItemCategorizedView::selectedItemInfosCurrentFirst()); } return ItemCategorizedView::selectedItemInfosCurrentFirst(); } void DigikamItemView::dragDropSort(const ItemInfo& pick, const QList& infos) { if (pick.isNull() || infos.isEmpty()) { return; } ItemInfoList infoList = allItemInfos(false); qlonglong counter = pick.manualOrder(); bool order = (ApplicationSettings::instance()-> getImageSorting() == Qt::AscendingOrder); bool found = false; QApplication::setOverrideCursor(Qt::WaitCursor); CoreDbOperationGroup group; group.setMaximumTime(200); foreach (ItemInfo info, infoList) { if (!found && info.id() == pick.id()) { foreach (ItemInfo dropInfo, infos) { dropInfo.setManualOrder(counter); counter += (order ? 1 : -1); } info.setManualOrder(counter); found = true; } else if (found && !infos.contains(info)) { if (( order && info.manualOrder() > counter) || (!order && info.manualOrder() < counter)) { break; } counter += (order ? 100 : -100); info.setManualOrder(counter); } group.allowLift(); } QApplication::restoreOverrideCursor(); imageFilterModel()->invalidate(); } bool DigikamItemView::allNeedGroupResolving(const ApplicationSettings::OperationType type) const { return needGroupResolving(type, allItemInfos()); } bool DigikamItemView::selectedNeedGroupResolving(const ApplicationSettings::OperationType type) const { return needGroupResolving(type, selectedItemInfos()); } int DigikamItemView::fitToWidthIcons() { return delegate()->calculatethumbSizeToFit(viewport()->size().width()); } void DigikamItemView::slotSetupChanged() { imageFilterModel()->setStringTypeNatural(ApplicationSettings::instance()->isStringTypeNatural()); setToolTipEnabled(ApplicationSettings::instance()->showToolTipsIsValid()); setFont(ApplicationSettings::instance()->getIconViewFont()); d->updateOverlays(); ItemCategorizedView::slotSetupChanged(); } bool DigikamItemView::hasHiddenGroupedImages(const ItemInfo& info) const { return (info.hasGroupedImages() && !imageFilterModel()->isAllGroupsOpen() && !imageFilterModel()->isGroupOpen(info.id())); } ItemInfoList DigikamItemView::imageInfos(const QList& indexes, ApplicationSettings::OperationType type) const { ItemInfoList infos = ItemCategorizedView::imageInfos(indexes); if (needGroupResolving(type, infos)) { return resolveGrouping(infos); } return infos; } bool DigikamItemView::getFaceMode() const { return d->faceMode; } void DigikamItemView::setFaceMode(bool on) { d->faceMode = on; if (on) { // See ItemLister, which creates a search the implements listing tag in the ioslave imageAlbumModel()->setSpecialTagListing(QLatin1String("faces")); setItemDelegate(d->faceDelegate); // grouping is not very much compatible with faces imageFilterModel()->setAllGroupsOpen(true); } else { imageAlbumModel()->setSpecialTagListing(QString()); setItemDelegate(d->normalDelegate); bool open = ApplicationSettings::instance()->getAllGroupsOpen(); imageFilterModel()->setAllGroupsOpen(open); } } void DigikamItemView::addRejectionOverlay(ItemDelegate* delegate) { FaceRejectionOverlay* const rejectionOverlay = new FaceRejectionOverlay(this); connect(rejectionOverlay, SIGNAL(rejectFaces(QList)), this, SLOT(removeFaces(QList))); addOverlay(rejectionOverlay, delegate); } /* void DigikamItemView::addTagEditOverlay(ItemDelegate* delegate) { TagsLineEditOverlay* tagOverlay = new TagsLineEditOverlay(this); connect(tagOverlay, SIGNAL(tagEdited(QModelIndex,QString)), this, SLOT(assignTag(QModelIndex,QString))); addOverlay(tagOverlay, delegate); } */ void DigikamItemView::addAssignNameOverlay(ItemDelegate* delegate) { AssignNameOverlay* const nameOverlay = new AssignNameOverlay(this); addOverlay(nameOverlay, delegate); connect(nameOverlay, SIGNAL(confirmFaces(QList,int)), this, SLOT(confirmFaces(QList,int))); connect(nameOverlay, SIGNAL(removeFaces(QList)), this, SLOT(removeFaces(QList))); } void DigikamItemView::confirmFaces(const QList& indexes, int tagId) { QList infos; QList faces; QList sourceIndexes; // fast-remove in the "unknown person" view Album* const album = currentAlbum(); bool needFastRemove = false; if (album) { needFastRemove = (d->faceMode && (tagId != album->id())); } foreach (const QModelIndex& index, indexes) { infos << ItemModel::retrieveItemInfo(index); faces << d->faceDelegate->face(index); if (needFastRemove) { sourceIndexes << imageSortFilterModel()->mapToSourceItemModel(index); } } imageAlbumModel()->removeIndexes(sourceIndexes); for (int i = 0 ; i < infos.size() ; ++i) { d->editPipeline.confirm(infos[i], faces[i], tagId); } } void DigikamItemView::removeFaces(const QList& indexes) { QList infos; QList faces; QList sourceIndexes; foreach (const QModelIndex& index, indexes) { infos << ItemModel::retrieveItemInfo(index); faces << d->faceDelegate->face(index); sourceIndexes << imageSortFilterModel()->mapToSourceItemModel(index); } imageAlbumModel()->removeIndexes(sourceIndexes); for (int i = 0 ; i < infos.size() ; ++i) { d->editPipeline.remove(infos[i], faces[i]); } } void DigikamItemView::activated(const ItemInfo& info, Qt::KeyboardModifiers modifiers) { if (info.isNull()) { return; } - if (modifiers != Qt::MetaModifier) + if (modifiers != Qt::AltModifier) { int leftClickAction = ApplicationSettings::instance()->getItemLeftClickAction(); if (leftClickAction == ApplicationSettings::ShowPreview) { emit previewRequested(info); } else if (leftClickAction == ApplicationSettings::StartEditor) { openFile(info); } else { d->utilities->openInfosWithDefaultApplication(QList() << info); } } else { d->utilities->openInfosWithDefaultApplication(QList() << info); } } void DigikamItemView::showContextMenuOnInfo(QContextMenuEvent* event, const ItemInfo& info) { emit signalShowContextMenuOnInfo(event, info, QList(), imageFilterModel()); } void DigikamItemView::showGroupContextMenu(const QModelIndex& index, QContextMenuEvent* event) { Q_UNUSED(index); emit signalShowGroupContextMenu(event, selectedItemInfosCurrentFirst(), imageFilterModel()); } void DigikamItemView::showContextMenu(QContextMenuEvent* event) { emit signalShowContextMenu(event); } void DigikamItemView::openFile(const ItemInfo& info) { d->utilities->openInfos(info, allItemInfos(), currentAlbum()); } void DigikamItemView::deleteSelected(const ItemViewUtilities::DeleteMode deleteMode) { ItemInfoList imageInfoList = selectedItemInfos(true); if (d->utilities->deleteImages(imageInfoList, deleteMode)) { awayFromSelection(); } } void DigikamItemView::deleteSelectedDirectly(const ItemViewUtilities::DeleteMode deleteMode) { ItemInfoList imageInfoList = selectedItemInfos(true); d->utilities->deleteImagesDirectly(imageInfoList, deleteMode); awayFromSelection(); } void DigikamItemView::assignRating(const QList& indexes, int rating) { ItemInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata); FileActionMngr::instance()->assignRating(infos, rating); } void DigikamItemView::groupIndicatorClicked(const QModelIndex& index) { ItemInfo info = imageFilterModel()->imageInfo(index); if (info.isNull()) { return; } setCurrentIndex(index); imageFilterModel()->toggleGroupOpen(info.id()); imageAlbumModel()->ensureHasGroupedImages(info); } void DigikamItemView::rename() { ItemInfoList infos = selectedItemInfos(); if (needGroupResolving(ApplicationSettings::Rename, infos)) { infos = resolveGrouping(infos); } QList urls = infos.toImageUrlList(); bool loop = false; NewNamesList newNamesList; do { qCDebug(DIGIKAM_GENERAL_LOG) << "Selected URLs to rename: " << urls; QPointer dlg = new AdvancedRenameDialog(this); dlg->slotAddImages(urls); if (dlg->exec() != QDialog::Accepted) { delete dlg; break; } if (!loop) { QUrl nextUrl = nextInOrder(infos.last(), 1).fileUrl(); setCurrentUrl(nextUrl); loop = true; } newNamesList = dlg->newNames(); delete dlg; setFocus(); qApp->processEvents(); if (!newNamesList.isEmpty()) { QPointer dlg2 = new AdvancedRenameProcessDialog(newNamesList, this); dlg2->exec(); imageFilterModel()->invalidate(); urls = dlg2->failedUrls(); delete dlg2; } } while (!urls.isEmpty() && !newNamesList.isEmpty()); } void DigikamItemView::slotRotateLeft(const QList& indexes) { ItemInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata); FileActionMngr::instance()->transform(infos, MetaEngineRotation::Rotate270); } void DigikamItemView::slotRotateRight(const QList& indexes) { ItemInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata); FileActionMngr::instance()->transform(infos, MetaEngineRotation::Rotate90); } void DigikamItemView::slotFullscreen(const QList& indexes) { QList infos = imageInfos(indexes, ApplicationSettings::Slideshow); if (infos.isEmpty()) { return; } // Just fullscreen the first. const ItemInfo& info = infos.at(0); QList actions = DPluginLoader::instance()-> pluginActions(QLatin1String("org.kde.digikam.plugin.generic.SlideShow"), DigikamApp::instance()); if (actions.isEmpty()) { return; } //Trigger SlideShow manual actions[0]->setData(info.fileUrl()); actions[0]->trigger(); } void DigikamItemView::slotInitProgressIndicator() { if (!ProgressManager::instance()->findItembyId(QLatin1String("FaceActionProgress"))) { FileActionProgress* const item = new FileActionProgress(QLatin1String("FaceActionProgress")); connect(&d->editPipeline, SIGNAL(started(QString)), item, SLOT(slotProgressStatus(QString))); connect(&d->editPipeline, SIGNAL(progressValueChanged(float)), item, SLOT(slotProgressValue(float))); connect(&d->editPipeline, SIGNAL(finished()), item, SLOT(slotCompleted())); } } } // namespace Digikam diff --git a/core/app/views/tableview/tableview.cpp b/core/app/views/tableview/tableview.cpp index 42f1f8c976..a98602a0a9 100644 --- a/core/app/views/tableview/tableview.cpp +++ b/core/app/views/tableview/tableview.cpp @@ -1,689 +1,689 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2013-02-11 * Description : Table view * * Copyright (C) 2013 by Michael G. Hansen * Copyright (C) 2017 by Simon Frei * Copyright (C) 2017-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 "tableview.h" // Qt includes #include #include #include #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "advancedrenamedialog.h" #include "advancedrenameprocessdialog.h" #include "applicationsettings.h" #include "contextmenuhelper.h" #include "digikam_debug.h" #include "fileactionmngr.h" #include "album.h" #include "itemviewutilities.h" #include "tableview_columnfactory.h" #include "tableview_model.h" #include "tableview_selection_model_syncer.h" #include "tableview_shared.h" #include "tableview_treeview.h" namespace Digikam { class ItemAlbumModel; class ItemFilterModel; class Q_DECL_HIDDEN TableView::Private { public: explicit Private() : columnProfiles(), thumbnailSize(), imageViewUtilities(nullptr) { } QList columnProfiles; ThumbnailSize thumbnailSize; ItemViewUtilities* imageViewUtilities; }; TableView::TableView(QItemSelectionModel* const selectionModel, DCategorizedSortFilterProxyModel* const imageFilterModel, QWidget* const parent) : QWidget(parent), StateSavingObject(this), d(new Private()), s(new TableViewShared()) { s->isActive = false; s->tableView = this; s->thumbnailLoadThread = new ThumbnailLoadThread(this); s->imageFilterModel = dynamic_cast(imageFilterModel); s->imageModel = dynamic_cast(imageFilterModel->sourceModel()); s->imageFilterSelectionModel = selectionModel; s->columnFactory = new TableViewColumnFactory(s.data(), this); QVBoxLayout* const vbox1 = new QVBoxLayout(); s->tableViewModel = new TableViewModel(s.data(), this); s->tableViewSelectionModel = new QItemSelectionModel(s->tableViewModel); s->tableViewSelectionModelSyncer = new TableViewSelectionModelSyncer(s.data(), this); s->treeView = new TableViewTreeView(s.data(), this); s->treeView->installEventFilter(this); d->imageViewUtilities = new ItemViewUtilities(this); connect(s->treeView, SIGNAL(activated(QModelIndex)), this, SLOT(slotItemActivated(QModelIndex))); connect(s->treeView, SIGNAL(signalZoomInStep()), this, SIGNAL(signalZoomInStep())); connect(s->treeView, SIGNAL(signalZoomOutStep()), this, SIGNAL(signalZoomOutStep())); connect(s->tableViewSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SIGNAL(signalItemsChanged())); connect(s->treeView, SIGNAL(collapsed(QModelIndex)), this, SIGNAL(signalItemsChanged())); connect(s->treeView, SIGNAL(expanded(QModelIndex)), this, SIGNAL(signalItemsChanged())); connect(s->tableViewModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(signalItemsChanged())); connect(s->tableViewModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(signalItemsChanged())); connect(s->tableViewModel, SIGNAL(layoutChanged()), this, SIGNAL(signalItemsChanged())); connect(s->tableViewModel, SIGNAL(modelReset()), this, SIGNAL(signalItemsChanged())); vbox1->addWidget(s->treeView); vbox1->setContentsMargins(QMargins()); setLayout(vbox1); } TableView::~TableView() { } void TableView::doLoadState() { const KConfigGroup group = getConfigGroup(); TableViewColumnProfile profile; const KConfigGroup groupCurrentProfile = group.group("Current Profile"); profile.loadSettings(groupCurrentProfile); s->tableViewModel->loadColumnProfile(profile); const TableViewModel::GroupingMode groupingMode = TableViewModel::GroupingMode(group.readEntry("Grouping mode", int(TableViewModel::GroupingShowSubItems))); s->tableViewModel->setGroupingMode(groupingMode); if (!profile.headerState.isEmpty()) { s->treeView->header()->restoreState(profile.headerState); } } void TableView::doSaveState() { KConfigGroup group = getConfigGroup(); TableViewColumnProfile profile = s->tableViewModel->getColumnProfile(); profile.headerState = s->treeView->header()->saveState(); KConfigGroup groupCurrentProfile = group.group("Current Profile"); profile.saveSettings(groupCurrentProfile); group.writeEntry("Grouping mode", int(s->tableViewModel->groupingMode())); } void TableView::slotItemActivated(const QModelIndex& tableViewIndex) { const ItemInfo info = s->tableViewModel->imageInfo(tableViewIndex); if (info.isNull()) { return; } - if (qApp->queryKeyboardModifiers() != Qt::MetaModifier) + if (qApp->queryKeyboardModifiers() != Qt::AltModifier) { int leftClickAction = ApplicationSettings::instance()->getItemLeftClickAction(); if (leftClickAction == ApplicationSettings::ShowPreview) { emit signalPreviewRequested(info); } else if (leftClickAction == ApplicationSettings::StartEditor) { d->imageViewUtilities->openInfos(info, allItemInfos(), currentAlbum()); } else { d->imageViewUtilities->openInfosWithDefaultApplication(QList() << info); } } else { d->imageViewUtilities->openInfosWithDefaultApplication(QList() << info); } } bool TableView::eventFilter(QObject* watched, QEvent* event) { // we are looking for context menu events for the table view if ((watched == s->treeView) && (event->type() == QEvent::ContextMenu)) { QContextMenuEvent* const e = static_cast(event); e->accept(); const QModelIndex contextMenuIndex = s->treeView->indexAt(e->pos()); if (contextMenuIndex.isValid()) { emit signalShowContextMenuOnInfo(e, s->tableViewModel->imageInfo(contextMenuIndex), getExtraGroupingActions()); } else { emit signalShowContextMenu(e, getExtraGroupingActions()); } // event has been filtered by us return true; } return QObject::eventFilter(watched, event); } void TableView::setThumbnailSize(const ThumbnailSize& size) { d->thumbnailSize = size; const QList columnObjects = s->tableViewModel->getColumnObjects(); foreach (TableViewColumn* const iColumn, columnObjects) { iColumn->updateThumbnailSize(); } } ThumbnailSize TableView::getThumbnailSize() const { return d->thumbnailSize; } Album* TableView::currentAlbum() const { ItemAlbumModel* const albumModel = qobject_cast(s->imageModel); if (!albumModel) { return nullptr; } if (albumModel->currentAlbums().isEmpty()) { return nullptr; } return albumModel->currentAlbums().first(); } void TableView::slotPaste() { DragDropViewImplementation* const dragDropViewImplementation = s->treeView; dragDropViewImplementation->paste(); } ItemInfo TableView::currentInfo() const { return s->tableViewModel->imageInfo(s->tableViewSelectionModel->currentIndex()); } ItemInfoList TableView::allItemInfos(bool grouping) const { if (grouping) { return s->treeView->resolveGrouping(ItemInfoList(s->tableViewModel->allItemInfo())); } return ItemInfoList(s->tableViewModel->allItemInfo()); } bool TableView::allNeedGroupResolving(const ApplicationSettings::OperationType type) const { return s->treeView->needGroupResolving(type, allItemInfos()); } bool TableView::selectedNeedGroupResolving(const ApplicationSettings::OperationType type) const { return s->treeView->needGroupResolving(type, selectedItemInfos()); } void TableView::slotDeleteSelected(const ItemViewUtilities::DeleteMode deleteMode) { const ItemInfoList infoList = selectedItemInfos(true); /// @todo Update parameter naming for deleteImages if (d->imageViewUtilities->deleteImages(infoList, deleteMode)) { slotAwayFromSelection(); } } void TableView::slotDeleteSelectedWithoutConfirmation(const ItemViewUtilities::DeleteMode deleteMode) { const ItemInfoList infoList = selectedItemInfos(true); d->imageViewUtilities->deleteImagesDirectly(infoList, deleteMode); slotAwayFromSelection(); } QList TableView::getExtraGroupingActions() { QList actionList; const TableViewModel::GroupingMode currentGroupingMode = s->tableViewModel->groupingMode(); QAction* const actionHideGrouped = new QAction(i18n("Hide grouped items"), this); actionHideGrouped->setCheckable(true); actionHideGrouped->setChecked(currentGroupingMode == TableViewModel::GroupingHideGrouped); actionHideGrouped->setData(QVariant::fromValue(TableViewModel::GroupingHideGrouped)); connect(actionHideGrouped, SIGNAL(triggered(bool)), this, SLOT(slotGroupingModeActionTriggered())); actionList << actionHideGrouped; QAction* const actionIgnoreGrouping = new QAction(i18n("Ignore grouping"), this); actionIgnoreGrouping->setCheckable(true); actionIgnoreGrouping->setChecked(currentGroupingMode == TableViewModel::GroupingIgnoreGrouping); actionIgnoreGrouping->setData(QVariant::fromValue(TableViewModel::GroupingIgnoreGrouping)); connect(actionIgnoreGrouping, SIGNAL(triggered(bool)), this, SLOT(slotGroupingModeActionTriggered())); actionList << actionIgnoreGrouping; QAction* const actionShowSubItems = new QAction(i18n("Show grouping in tree"), this); actionShowSubItems->setCheckable(true); actionShowSubItems->setChecked(currentGroupingMode == TableViewModel::GroupingShowSubItems); actionShowSubItems->setData(QVariant::fromValue(TableViewModel::GroupingShowSubItems)); connect(actionShowSubItems, SIGNAL(triggered(bool)), this, SLOT(slotGroupingModeActionTriggered())); actionList << actionShowSubItems; return actionList; } void TableView::slotGroupingModeActionTriggered() { const QAction* const senderAction = qobject_cast(sender()); if (!senderAction) { return; } const TableViewModel::GroupingMode newGroupingMode = senderAction->data().value(); s->tableViewModel->setGroupingMode(newGroupingMode); } int TableView::numberOfSelectedItems() const { return s->tableViewSelectionModel->selectedRows().count(); } void TableView::slotGoToRow(const int rowNumber, const bool relativeMove) { int nextDeepRowNumber = rowNumber; if (relativeMove) { const QModelIndex currentTableViewIndex = s->tableViewSelectionModel->currentIndex(); const int currentDeepRowNumber = s->tableViewModel->indexToDeepRowNumber(currentTableViewIndex); nextDeepRowNumber += currentDeepRowNumber; } const QModelIndex nextIndex = s->tableViewModel->deepRowIndex(nextDeepRowNumber); if (nextIndex.isValid()) { const QItemSelection rowSelection = s->tableViewSelectionModelSyncer->targetIndexToRowItemSelection(nextIndex); s->tableViewSelectionModel->select(rowSelection, QItemSelectionModel::ClearAndSelect); s->tableViewSelectionModel->setCurrentIndex(nextIndex, QItemSelectionModel::Select); } } ItemInfo TableView::deepRowItemInfo(const int rowNumber, const bool relative) const { int targetRowNumber = rowNumber; if (relative) { const QModelIndex& currentTableViewIndex = s->tableViewSelectionModel->currentIndex(); if (!currentTableViewIndex.isValid()) { return ItemInfo(); } const int currentDeepRowNumber = s->tableViewModel->indexToDeepRowNumber(currentTableViewIndex); targetRowNumber += currentDeepRowNumber; } const QModelIndex targetIndex = s->tableViewModel->deepRowIndex(targetRowNumber); return s->tableViewModel->imageInfo(targetIndex); } ItemInfo TableView::nextInfo() const { const QModelIndex cIndex = s->tableViewSelectionModel->currentIndex(); const int currentDeepRowNumber = s->tableViewModel->indexToDeepRowNumber(cIndex); const int nextDeepRowNumber = currentDeepRowNumber + 1; if (nextDeepRowNumber>=s->tableViewModel->deepRowCount()) { return ItemInfo(); } const QModelIndex nextDeepRowIndex = s->tableViewModel->deepRowIndex(nextDeepRowNumber); return s->tableViewModel->imageInfo(nextDeepRowIndex); } ItemInfo TableView::previousInfo() const { const QModelIndex cIndex = s->tableViewSelectionModel->currentIndex(); const int currentDeepRowNumber = s->tableViewModel->indexToDeepRowNumber(cIndex); const int previousDeepRowNumber = currentDeepRowNumber - 1; if (previousDeepRowNumber < 0) { return ItemInfo(); } const QModelIndex previousDeepRowIndex = s->tableViewModel->deepRowIndex(previousDeepRowNumber); return s->tableViewModel->imageInfo(previousDeepRowIndex); } void TableView::slotSetCurrentUrlWhenAvailable(const QUrl& url) { foreach (const ItemInfo& info, allItemInfos()) { if (info.fileUrl() == url) { slotSetCurrentWhenAvailable(info.id()); break; } } } void TableView::slotSetCurrentWhenAvailable(const qlonglong id) { const QModelIndex idx = s->tableViewModel->indexFromImageId(id, 0); if (!idx.isValid()) { /// @todo Actually buffer this request until the model is fully populated return; } s->tableViewSelectionModel->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect); } /** * @brief Unselects the current selection and changes the current item * * @todo This may not work correctly if grouped items are deleted, but are not selected */ void TableView::slotAwayFromSelection() { QModelIndexList selection = s->tableViewSelectionModel->selectedRows(0); if (selection.isEmpty()) { return; } const QModelIndex firstIndex = s->tableViewModel->deepRowIndex(0); const QModelIndex lastIndex = s->tableViewModel->deepRowIndex(-1); if (selection.contains(firstIndex) && selection.contains(lastIndex)) { // both the first and the last index are selected, we have to // select an index inbetween const int nextFreeDeepRow = s->tableViewModel->firstDeepRowNotInList(selection); if (nextFreeDeepRow < 0) { s->tableViewSelectionModel->clearSelection(); s->tableViewSelectionModel->setCurrentIndex(QModelIndex(), QItemSelectionModel::ClearAndSelect); } else { const QModelIndex nextFreeIndex = s->tableViewModel->deepRowIndex(nextFreeDeepRow); s->tableViewSelectionModel->setCurrentIndex(nextFreeIndex, QItemSelectionModel::ClearAndSelect); const QItemSelection nextFreeIndexAsRow = s->tableViewSelectionModelSyncer->targetIndexToRowItemSelection(nextFreeIndex); s->tableViewSelectionModel->select(nextFreeIndexAsRow, QItemSelectionModel::ClearAndSelect); } } else if (selection.contains(lastIndex)) { const int firstSelectedRowNumber = s->tableViewModel->indexToDeepRowNumber(selection.first()); const QModelIndex newIndex = s->tableViewModel->deepRowIndex(firstSelectedRowNumber-1); s->tableViewSelectionModel->setCurrentIndex(newIndex, QItemSelectionModel::ClearAndSelect); const QItemSelection newIndexAsRow = s->tableViewSelectionModelSyncer->targetIndexToRowItemSelection(newIndex); s->tableViewSelectionModel->select(newIndexAsRow, QItemSelectionModel::ClearAndSelect); } else { const int lastSelectedRowNumber = s->tableViewModel->indexToDeepRowNumber(selection.last()); const QModelIndex newIndex = s->tableViewModel->deepRowIndex(lastSelectedRowNumber+1); s->tableViewSelectionModel->setCurrentIndex(newIndex, QItemSelectionModel::ClearAndSelect); const QItemSelection newIndexAsRow = s->tableViewSelectionModelSyncer->targetIndexToRowItemSelection(newIndex); s->tableViewSelectionModel->select(newIndexAsRow, QItemSelectionModel::ClearAndSelect); } } void TableView::clearSelection() { s->tableViewSelectionModel->clearSelection(); } void TableView::invertSelection() { const int deepRowCount = s->tableViewModel->deepRowCount(); QList rowsToSelect; int lastSelectedRow = -1; /// @todo Create a DeepRowIterator because there is a lot of overhead here for (int i = 0 ; i < deepRowCount ; ++i) { const QModelIndex iIndex = s->tableViewModel->deepRowIndex(i); if (s->tableViewSelectionModel->isSelected(iIndex)) { if ((i - 1) > lastSelectedRow) { for (int j = lastSelectedRow + 1 ; j < i ; ++j) { rowsToSelect << j; } } lastSelectedRow = i; } } if (lastSelectedRow + 1 < deepRowCount) { for (int j = lastSelectedRow + 1 ; j < deepRowCount ; ++j) { rowsToSelect << j; } } s->tableViewSelectionModel->clearSelection(); foreach (const int i, rowsToSelect) { const QModelIndex iIndex = s->tableViewModel->deepRowIndex(i); const QItemSelection is = s->tableViewSelectionModelSyncer->targetIndexToRowItemSelection(iIndex); s->tableViewSelectionModel->select(is, QItemSelectionModel::Select); } } void TableView::selectAll() { /// @todo This only selects expanded items. s->treeView->selectAll(); } void TableView::slotSetActive(const bool isActive) { if (s->isActive != isActive) { s->isActive = isActive; s->tableViewModel->slotSetActive(isActive); s->tableViewSelectionModelSyncer->slotSetActive(isActive); } } ItemInfoList TableView::selectedItemInfos(bool grouping) const { ItemInfoList infos = ItemInfoList(s->tableViewModel->imageInfos(s->tableViewSelectionModel->selectedRows())); if (grouping) { return s->treeView->resolveGrouping(infos); } return infos; } ItemInfoList TableView::selectedItemInfosCurrentFirst(bool grouping) const { QModelIndexList indexes = s->tableViewSelectionModel->selectedRows(); const QModelIndex current = s->tableViewModel->toCol0(s->tableViewSelectionModel->currentIndex()); if (!indexes.isEmpty()) { if (indexes.first() != current) { if (indexes.removeOne(current)) { indexes.prepend(current); } } } if (grouping) { return s->treeView->resolveGrouping(ItemInfoList(s->tableViewModel->imageInfos(indexes))); } return ItemInfoList(s->tableViewModel->imageInfos(indexes)); } void TableView::rename() { ItemInfoList infos = selectedItemInfos(); if (s->treeView->needGroupResolving(ApplicationSettings::Rename, infos)) { infos = s->treeView->resolveGrouping(infos); } QList urls = infos.toImageUrlList(); bool loop = false; NewNamesList newNamesList; do { qCDebug(DIGIKAM_GENERAL_LOG) << "Selected URLs to rename: " << urls; QPointer dlg = new AdvancedRenameDialog(this); dlg->slotAddImages(urls); if (dlg->exec() != QDialog::Accepted) { delete dlg; break; } if (!loop) { slotAwayFromSelection(); loop = true; } newNamesList = dlg->newNames(); delete dlg; setFocus(); qApp->processEvents(); if (!newNamesList.isEmpty()) { QPointer dlg2 = new AdvancedRenameProcessDialog(newNamesList, this); dlg2->exec(); s->tableViewModel->scheduleResort(); urls = dlg2->failedUrls(); delete dlg2; } } while (!urls.isEmpty() && !newNamesList.isEmpty()); } } // namespace Digikam