diff --git a/core/app/items/digikamimageview.cpp b/core/app/items/digikamimageview.cpp index c184630571..20817ff0e7 100644 --- a/core/app/items/digikamimageview.cpp +++ b/core/app/items/digikamimageview.cpp @@ -1,542 +1,542 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-04-24 * Description : Qt item view for images * * Copyright (C) 2009-2011 by Marcel Wiesweg * Copyright (C) 2009-2018 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 "digikamimageview.h" #include "digikamimageview_p.h" // Qt includes #include #include #include #include #include // Local includes #include "digikam_debug.h" #include "albummanager.h" #include "coredb.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 "digikamimagedelegate.h" #include "digikamimagefacedelegate.h" #include "dio.h" #include "groupindicatoroverlay.h" #include "imagealbumfiltermodel.h" #include "imagealbummodel.h" #include "imagedragdrop.h" #include "imageratingoverlay.h" #include "imagefsoverlay.h" #include "imagecoordinatesoverlay.h" #include "tagslineeditoverlay.h" #include "imageviewutilities.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 { DigikamImageView::DigikamImageView(QWidget* const parent) : ImageCategorizedView(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 DigikamImageDelegate(this); d->faceDelegate = new DigikamImageFaceDelegate(this); setItemDelegate(d->normalDelegate); setSpacing(10); ApplicationSettings* const settings = ApplicationSettings::instance(); imageFilterModel()->setCategorizationMode(ImageSortSettings::CategoryByAlbum); imageAlbumModel()->setThumbnailLoadThread(ThumbnailLoadThread::defaultIconViewThread()); setThumbnailSize(ThumbnailSize(settings->getDefaultIconSize())); imageAlbumModel()->setPreloadThumbnails(true); imageModel()->setDragDropHandler(new ImageDragDropHandler(imageModel())); setDragEnabled(true); setAcceptDrops(true); setDropIndicatorShown(false); setToolTipEnabled(settings->showToolTipsIsValid()); imageFilterModel()->setStringTypeNatural(settings->isStringTypeNatural()); imageFilterModel()->setSortRole((ImageSortSettings::SortRole)settings->getImageSortOrder()); imageFilterModel()->setSortOrder((ImageSortSettings::SortOrder)settings->getImageSorting()); imageFilterModel()->setCategorizationMode((ImageSortSettings::CategorizationMode)settings->getImageSeparationMode()); imageFilterModel()->setCategorizationSortOrder((ImageSortSettings::SortOrder) settings->getImageSeparationSortOrder()); // selection overlay addSelectionOverlay(d->normalDelegate); addSelectionOverlay(d->faceDelegate); // rotation overlays d->rotateLeftOverlay = ImageRotateOverlay::left(this); d->rotateRightOverlay = ImageRotateOverlay::right(this); d->fullscreenOverlay = ImageFsOverlay::instance(this); d->updateOverlays(); // rating overlay ImageRatingOverlay* const ratingOverlay = new ImageRatingOverlay(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 ImageCoordinatesOverlay(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 ImageViewUtilities(this); connect(imageModel()->dragDropHandler(), SIGNAL(assignTags(QList,QList)), FileActionMngr::instance(), SLOT(assignTags(QList,QList))); connect(imageModel()->dragDropHandler(), SIGNAL(addToGroup(ImageInfo,QList)), FileActionMngr::instance(), SLOT(addToGroup(ImageInfo,QList))); connect(d->utilities, SIGNAL(editorCurrentUrlChanged(QUrl)), this, SLOT(setCurrentUrlWhenAvailable(QUrl))); connect(settings, SIGNAL(setupChanged()), this, SLOT(slotSetupChanged())); slotSetupChanged(); } DigikamImageView::~DigikamImageView() { delete d; } ImageViewUtilities* DigikamImageView::utilities() const { return d->utilities; } void DigikamImageView::setThumbnailSize(const ThumbnailSize& size) { imageThumbnailModel()->setPreloadThumbnailSize(size); ImageCategorizedView::setThumbnailSize(size); } ImageInfoList DigikamImageView::allImageInfos(bool grouping) const { if (grouping) { return resolveGrouping(ImageCategorizedView::allImageInfos()); } return ImageCategorizedView::allImageInfos(); } ImageInfoList DigikamImageView::selectedImageInfos(bool grouping) const { if (grouping) { return resolveGrouping(ImageCategorizedView::selectedImageInfos()); } return ImageCategorizedView::selectedImageInfos(); } ImageInfoList DigikamImageView::selectedImageInfosCurrentFirst(bool grouping) const { if (grouping) { return resolveGrouping(ImageCategorizedView::selectedImageInfosCurrentFirst()); } return ImageCategorizedView::selectedImageInfosCurrentFirst(); } bool DigikamImageView::allNeedGroupResolving(const ApplicationSettings::OperationType type) const { return needGroupResolving(type, allImageInfos()); } bool DigikamImageView::selectedNeedGroupResolving(const ApplicationSettings::OperationType type) const { return needGroupResolving(type, selectedImageInfos()); } int DigikamImageView::fitToWidthIcons() { return delegate()->calculatethumbSizeToFit(viewport()->size().width()); } void DigikamImageView::slotSetupChanged() { imageFilterModel()->setStringTypeNatural(ApplicationSettings::instance()->isStringTypeNatural()); setToolTipEnabled(ApplicationSettings::instance()->showToolTipsIsValid()); setFont(ApplicationSettings::instance()->getIconViewFont()); d->updateOverlays(); ImageCategorizedView::slotSetupChanged(); } bool DigikamImageView::hasHiddenGroupedImages(const ImageInfo& info) const { return info.hasGroupedImages() && !imageFilterModel()->isGroupOpen(info.id()); } ImageInfoList DigikamImageView::imageInfos(const QList& indexes, ApplicationSettings::OperationType type) const { ImageInfoList infos = ImageCategorizedView::imageInfos(indexes); if (needGroupResolving(type, infos)) { return resolveGrouping(infos); } return infos; } void DigikamImageView::setFaceMode(bool on) { d->faceMode = on; if (on) { // See ImageLister, 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); imageFilterModel()->setAllGroupsOpen(false); } } void DigikamImageView::addRejectionOverlay(ImageDelegate* delegate) { FaceRejectionOverlay* const rejectionOverlay = new FaceRejectionOverlay(this); connect(rejectionOverlay, SIGNAL(rejectFaces(QList)), this, SLOT(removeFaces(QList))); addOverlay(rejectionOverlay, delegate); } /* void DigikamImageView::addTagEditOverlay(ImageDelegate* delegate) { TagsLineEditOverlay* tagOverlay = new TagsLineEditOverlay(this); connect(tagOverlay, SIGNAL(tagEdited(QModelIndex,QString)), this, SLOT(assignTag(QModelIndex,QString))); addOverlay(tagOverlay, delegate); } */ void DigikamImageView::addAssignNameOverlay(ImageDelegate* 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 DigikamImageView::confirmFaces(const QList& indexes, int tagId) { QList infos; QList faces; QList sourceIndexes; // fast-remove in the "unknown person" view bool needFastRemove = false; if (imageAlbumModel()->currentAlbums().size() == 1) { needFastRemove = d->faceMode && (tagId != imageAlbumModel()->currentAlbums().first()->id()); } foreach (const QModelIndex& index, indexes) { infos << ImageModel::retrieveImageInfo(index); faces << d->faceDelegate->face(index); if (needFastRemove) { sourceIndexes << imageSortFilterModel()->mapToSourceImageModel(index); } } imageAlbumModel()->removeIndexes(sourceIndexes); for (int i = 0 ; i < infos.size() ; i++) { d->editPipeline.confirm(infos[i], faces[i], tagId); } } void DigikamImageView::removeFaces(const QList& indexes) { QList infos; QList faces; QList sourceIndexes; foreach (const QModelIndex& index, indexes) { infos << ImageModel::retrieveImageInfo(index); faces << d->faceDelegate->face(index); sourceIndexes << imageSortFilterModel()->mapToSourceImageModel(index); } imageAlbumModel()->removeIndexes(sourceIndexes); for (int i = 0 ; i < infos.size() ; i++) { d->editPipeline.remove(infos[i], faces[i]); } } void DigikamImageView::activated(const ImageInfo& info, Qt::KeyboardModifiers modifiers) { if (info.isNull()) { return; } if (modifiers != Qt::MetaModifier) { if (ApplicationSettings::instance()->getItemLeftClickAction() == ApplicationSettings::ShowPreview) { emit previewRequested(info); } else { openFile(info); } } else { d->utilities->openInfosWithDefaultApplication(QList() << info); } } void DigikamImageView::showContextMenuOnInfo(QContextMenuEvent* event, const ImageInfo& info) { emit signalShowContextMenuOnInfo(event, info, QList(), imageFilterModel()); } void DigikamImageView::showGroupContextMenu(const QModelIndex& index, QContextMenuEvent* event) { Q_UNUSED(index); emit signalShowGroupContextMenu(event, selectedImageInfosCurrentFirst(), imageFilterModel()); } void DigikamImageView::showContextMenu(QContextMenuEvent* event) { emit signalShowContextMenu(event); } void DigikamImageView::openFile(const ImageInfo& info) { d->utilities->openInfos(info, allImageInfos(), currentAlbum()); } void DigikamImageView::deleteSelected(const ImageViewUtilities::DeleteMode deleteMode) { ImageInfoList imageInfoList = selectedImageInfos(true); if (d->utilities->deleteImages(imageInfoList, deleteMode)) { awayFromSelection(); } } void DigikamImageView::deleteSelectedDirectly(const ImageViewUtilities::DeleteMode deleteMode) { ImageInfoList imageInfoList = selectedImageInfos(true); d->utilities->deleteImagesDirectly(imageInfoList, deleteMode); awayFromSelection(); } void DigikamImageView::assignRating(const QList& indexes, int rating) { ImageInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata); FileActionMngr::instance()->assignRating(infos, rating); } void DigikamImageView::groupIndicatorClicked(const QModelIndex& index) { ImageInfo info = imageFilterModel()->imageInfo(index); if (info.isNull()) { return; } setCurrentIndex(index); imageFilterModel()->toggleGroupOpen(info.id()); imageAlbumModel()->ensureHasGroupedImages(info); } void DigikamImageView::rename() { ImageInfoList infos = selectedImageInfos(); if (needGroupResolving(ApplicationSettings::Rename, infos)) { infos = resolveGrouping(infos); } - QList urls = infos.toImageUrlList(); - bool loop = false; + 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(); if (!newNamesList.isEmpty()) { QPointer dlg = new AdvancedRenameProcessDialog(newNamesList, this); dlg->exec(); urls = dlg->failedUrls(); delete dlg; setFocus(); } } while (!urls.isEmpty() && !newNamesList.isEmpty()); } void DigikamImageView::slotRotateLeft(const QList& indexes) { ImageInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata); FileActionMngr::instance()->transform(infos, MetaEngineRotation::Rotate270); } void DigikamImageView::slotRotateRight(const QList& indexes) { ImageInfoList infos = imageInfos(indexes, ApplicationSettings::Metadata); FileActionMngr::instance()->transform(infos, MetaEngineRotation::Rotate90); } void DigikamImageView::slotFullscreen(const QList& indexes) { QList infos = imageInfos(indexes); if (infos.isEmpty()) { return; } // Just fullscreen the first. const ImageInfo& info = infos.at(0); emit fullscreenRequested(info); } void DigikamImageView::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 0b3940aade..d452365f3a 100644 --- a/core/app/views/tableview/tableview.cpp +++ b/core/app/views/tableview/tableview.cpp @@ -1,649 +1,648 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2013-02-11 * Description : Table view * * Copyright (C) 2013 by Michael G. Hansen * 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 "tableview.h" // Qt includes #include #include #include #include #include #include #include #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 "imageviewutilities.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 ImageAlbumModel; class ImageFilterModel; class TableView::Private { public: explicit Private() : columnProfiles(), thumbnailSize(), imageViewUtilities(0) { } QList columnProfiles; ThumbnailSize thumbnailSize; ImageViewUtilities* 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 ImageViewUtilities(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); 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 ImageInfo info = s->tableViewModel->imageInfo(tableViewIndex); if (info.isNull()) { return; } if (qApp->queryKeyboardModifiers() != Qt::MetaModifier) { if (ApplicationSettings::instance()->getItemLeftClickAction() == ApplicationSettings::ShowPreview) { emit signalPreviewRequested(info); } else { d->imageViewUtilities->openInfos(info, allImageInfos(), currentAlbum()); } } 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 { ImageAlbumModel* const albumModel = qobject_cast(s->imageModel); if (!albumModel) { return 0; } if (albumModel->currentAlbums().isEmpty()) { return 0; } return albumModel->currentAlbums().first(); } void TableView::slotPaste() { DragDropViewImplementation* const dragDropViewImplementation = s->treeView; dragDropViewImplementation->paste(); } ImageInfo TableView::currentInfo() const { return s->tableViewModel->imageInfo(s->tableViewSelectionModel->currentIndex()); } ImageInfoList TableView::allImageInfos(bool grouping) const { if (grouping) { return s->treeView->resolveGrouping(s->tableViewModel->allImageInfo()); } return s->tableViewModel->allImageInfo(); } bool TableView::allNeedGroupResolving(const ApplicationSettings::OperationType type) const { return s->treeView->needGroupResolving(type, allImageInfos()); } bool TableView::selectedNeedGroupResolving(const ApplicationSettings::OperationType type) const { return s->treeView->needGroupResolving(type, selectedImageInfos()); } void TableView::slotDeleteSelected(const ImageViewUtilities::DeleteMode deleteMode) { const ImageInfoList infoList = selectedImageInfos(true); /// @todo Update parameter naming for deleteImages if (d->imageViewUtilities->deleteImages(infoList, deleteMode)) { slotAwayFromSelection(); } } void TableView::slotDeleteSelectedWithoutConfirmation(const ImageViewUtilities::DeleteMode deleteMode) { const ImageInfoList infoList = selectedImageInfos(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); } } ImageInfo TableView::deepRowImageInfo(const int rowNumber, const bool relative) const { int targetRowNumber = rowNumber; if (relative) { const QModelIndex& currentTableViewIndex = s->tableViewSelectionModel->currentIndex(); if (!currentTableViewIndex.isValid()) { return ImageInfo(); } const int currentDeepRowNumber = s->tableViewModel->indexToDeepRowNumber(currentTableViewIndex); targetRowNumber += currentDeepRowNumber; } const QModelIndex targetIndex = s->tableViewModel->deepRowIndex(targetRowNumber); return s->tableViewModel->imageInfo(targetIndex); } ImageInfo 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 ImageInfo(); } const QModelIndex nextDeepRowIndex = s->tableViewModel->deepRowIndex(nextDeepRowNumber); return s->tableViewModel->imageInfo(nextDeepRowIndex); } ImageInfo 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 ImageInfo(); } const QModelIndex previousDeepRowIndex = s->tableViewModel->deepRowIndex(previousDeepRowNumber); return s->tableViewModel->imageInfo(previousDeepRowIndex); } 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); } } ImageInfoList TableView::selectedImageInfos(bool grouping) const { ImageInfoList infos = s->tableViewModel->imageInfos(s->tableViewSelectionModel->selectedRows()); if (grouping) { return s->treeView->resolveGrouping(infos); } return infos; } ImageInfoList TableView::selectedImageInfosCurrentFirst(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(s->tableViewModel->imageInfos(indexes)); } return s->tableViewModel->imageInfos(indexes); } void TableView::rename() { ImageInfoList infos = selectedImageInfos(); if (s->treeView->needGroupResolving(ApplicationSettings::Rename, infos)) { infos = s->treeView->resolveGrouping(infos); } - QList urls = infos.toImageUrlList(); - - bool loop = false; + 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(); if (!newNamesList.isEmpty()) { QPointer dlg = new AdvancedRenameProcessDialog(newNamesList, this); dlg->exec(); urls = dlg->failedUrls(); delete dlg; setFocus(); } } while (!urls.isEmpty() && !newNamesList.isEmpty()); } } // namespace Digikam