diff --git a/core/app/items/views/digikamitemview.cpp b/core/app/items/views/digikamitemview.cpp index f8ffbabeb8..f447b5d0bb 100644 --- a/core/app/items/views/digikamitemview.cpp +++ b/core/app/items/views/digikamitemview.cpp @@ -1,608 +1,600 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-04-24 * Description : Qt model-view for items * * 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 "digikamitemview.h" #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))); connect(settings, SIGNAL(setupChanged()), this, SLOT(slotSetupChanged())); 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()->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; } 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); imageFilterModel()->setAllGroupsOpen(false); } } 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 bool needFastRemove = false; if (imageAlbumModel()->currentAlbums().size() == 1) { needFastRemove = d->faceMode && (tagId != imageAlbumModel()->currentAlbums().first()->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, QMouseEvent* const event) +void DigikamItemView::activated(const ItemInfo& info, Qt::KeyboardModifiers modifiers) { if (info.isNull()) { return; } - if (event) + if (modifiers != Qt::MetaModifier) { - const bool acceptedClick = ((!qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) && - event->type() == QEvent::MouseButtonDblClick) || - ( qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) && - event->type() == QEvent::MouseButtonRelease)); - - if (event->modifiers() != Qt::MetaModifier) + if (ApplicationSettings::instance()->getItemLeftClickAction() == ApplicationSettings::ShowPreview) { - if (ApplicationSettings::instance()->getItemLeftClickAction() == ApplicationSettings::ShowPreview) - { - emit previewRequested(info); - } - else if (acceptedClick) - { - openFile(info); - } + emit previewRequested(info); } - else if (acceptedClick) + else { - d->utilities->openInfosWithDefaultApplication(QList() << info); + openFile(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 dlg = new AdvancedRenameProcessDialog(newNamesList, this); dlg->exec(); imageFilterModel()->invalidate(); urls = dlg->failedUrls(); delete dlg; } } 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); emit fullscreenRequested(info); } 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/items/views/digikamitemview.h b/core/app/items/views/digikamitemview.h index 7c30e00b58..3339dec309 100644 --- a/core/app/items/views/digikamitemview.h +++ b/core/app/items/views/digikamitemview.h @@ -1,129 +1,129 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-04-24 * Description : Qt model-view for items * * Copyright (C) 2009-2011 by Marcel Wiesweg * Copyright (C) 2009-2018 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DIGIKAMITEM_VIEW_H #define DIGIKAM_DIGIKAMITEM_VIEW_H // Local includes #include "applicationsettings.h" #include "itemcategorizedview.h" #include "itemviewutilities.h" #include "groupingviewimplementation.h" namespace Digikam { class ItemViewUtilities; class ItemInfoList; class DigikamItemView : public ItemCategorizedView, public GroupingViewImplementation { Q_OBJECT public: explicit DigikamItemView(QWidget* const parent = 0); ~DigikamItemView(); ItemViewUtilities* utilities() const; int fitToWidthIcons(); virtual void setThumbnailSize(const ThumbnailSize& size); ItemInfoList allItemInfos(bool grouping = false) const; ItemInfoList selectedItemInfos(bool grouping = false) const; ItemInfoList selectedItemInfosCurrentFirst(bool grouping = false) const; bool allNeedGroupResolving(const ApplicationSettings::OperationType type) const; bool selectedNeedGroupResolving(const ApplicationSettings::OperationType type) const; public Q_SLOTS: void openFile(const ItemInfo& info); void deleteSelected(const ItemViewUtilities::DeleteMode deleteMode = ItemViewUtilities::DeleteUseTrash); void deleteSelectedDirectly(const ItemViewUtilities::DeleteMode deleteMode = ItemViewUtilities::DeleteUseTrash); void rename(); void assignRating(const QList& index, int rating); void setFaceMode(bool on); void confirmFaces(const QList& indexes, int tagId); void removeFaces(const QList& indexes); void dragDropSort(const ItemInfo& pick, const QList& infos); Q_SIGNALS: void previewRequested(const ItemInfo& info); void fullscreenRequested(const ItemInfo& info); void signalShowContextMenu(QContextMenuEvent* event, const QList& actions = QList()); void signalShowContextMenuOnInfo(QContextMenuEvent* event, const ItemInfo& info, const QList& actions, ItemFilterModel* filterModel); void signalShowGroupContextMenu(QContextMenuEvent* event, const QList& selectedInfos, ItemFilterModel* filterModel); protected Q_SLOTS: void groupIndicatorClicked(const QModelIndex& index); void showGroupContextMenu(const QModelIndex& index, QContextMenuEvent* event); protected: void addRejectionOverlay(ItemDelegate* delegate = 0); void addAssignNameOverlay(ItemDelegate* delegate = 0); - virtual void activated(const ItemInfo& info, QMouseEvent* const event); + virtual void activated(const ItemInfo& info, Qt::KeyboardModifiers modifiers); virtual void showContextMenuOnInfo(QContextMenuEvent* event, const ItemInfo& info); virtual void showContextMenu(QContextMenuEvent* event); virtual void slotSetupChanged(); virtual bool hasHiddenGroupedImages(const ItemInfo& info) const; ItemInfoList imageInfos(const QList& indexes, ApplicationSettings::OperationType type) const; private Q_SLOTS: void slotRotateLeft(const QList&); void slotRotateRight(const QList&); void slotFullscreen(const QList&); void slotInitProgressIndicator(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_DIGIKAMITEM_VIEW_H diff --git a/core/app/items/views/itemcategorizedview.cpp b/core/app/items/views/itemcategorizedview.cpp index c14e77a9c2..5d01021354 100644 --- a/core/app/items/views/itemcategorizedview.cpp +++ b/core/app/items/views/itemcategorizedview.cpp @@ -1,740 +1,740 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-04-22 * Description : Qt model-view for items * * Copyright (C) 2009-2012 by Marcel Wiesweg * 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 "itemcategorizedview.h" // Qt includes #include #include // Local includes #include "digikam_debug.h" #include "album.h" #include "albummanager.h" #include "coredbfields.h" #include "iccsettings.h" #include "itemalbummodel.h" #include "itemalbumfiltermodel.h" #include "itemcategorydrawer.h" #include "itemdelegate.h" #include "itemdelegateoverlay.h" #include "itemthumbnailmodel.h" #include "itemselectionoverlay.h" #include "itemviewtooltip.h" #include "loadingcacheinterface.h" #include "thumbnailloadthread.h" #include "tooltipfiller.h" #include "itemfacedelegate.h" namespace Digikam { class Q_DECL_HIDDEN ItemCategorizedViewToolTip : public ItemViewToolTip { public: explicit ItemCategorizedViewToolTip(ItemCategorizedView* const view) : ItemViewToolTip(view) { } ItemCategorizedView* view() const { return static_cast(ItemViewToolTip::view()); } protected: virtual QString tipContents() { ItemInfo info = ItemModel::retrieveItemInfo(currentIndex()); return ToolTipFiller::imageInfoTipContents(info); } }; // ------------------------------------------------------------------------------- class Q_DECL_HIDDEN ItemCategorizedView::Private { public: explicit Private() : model(0), filterModel(0), delegate(0), showToolTip(false), scrollToItemId(0), delayedEnterTimer(0), currentMouseEvent(0) { } ItemModel* model; ImageSortFilterModel* filterModel; ItemDelegate* delegate; bool showToolTip; qlonglong scrollToItemId; QUrl unknownCurrentUrl; QTimer* delayedEnterTimer; QMouseEvent* currentMouseEvent; }; // ------------------------------------------------------------------------------- ItemCategorizedView::ItemCategorizedView(QWidget* const parent) : ItemViewCategorized(parent), d(new Private) { setToolTip(new ItemCategorizedViewToolTip(this)); LoadingCacheInterface::connectToSignalFileChanged(this, SLOT(slotFileChanged(QString))); connect(IccSettings::instance(), SIGNAL(settingsChanged(ICCSettingsContainer,ICCSettingsContainer)), this, SLOT(slotIccSettingsChanged(ICCSettingsContainer,ICCSettingsContainer))); d->delayedEnterTimer = new QTimer(this); d->delayedEnterTimer->setInterval(10); d->delayedEnterTimer->setSingleShot(true); connect(d->delayedEnterTimer, SIGNAL(timeout()), this, SLOT(slotDelayedEnter())); } ItemCategorizedView::~ItemCategorizedView() { d->delegate->removeAllOverlays(); delete d; } void ItemCategorizedView::installDefaultModels() { ItemAlbumModel* model = new ItemAlbumModel(this); ItemAlbumFilterModel* filterModel = new ItemAlbumFilterModel(this); filterModel->setSourceItemModel(model); filterModel->setSortRole(ItemSortSettings::SortByFileName); filterModel->setCategorizationMode(ItemSortSettings::CategoryByAlbum); filterModel->sort(0); // an initial sorting is necessary // set flags that we want to get dataChanged() signals for model->setWatchFlags(filterModel->suggestedWatchFlags()); setModels(model, filterModel); } void ItemCategorizedView::setModels(ItemModel* model, ImageSortFilterModel* filterModel) { if (d->delegate) { d->delegate->setAllOverlaysActive(false); } if (d->filterModel) { disconnect(d->filterModel, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged())); disconnect(d->filterModel, SIGNAL(layoutChanged()), this, SLOT(layoutWasChanged())); } if (d->model) { disconnect(d->model, SIGNAL(imageInfosAdded(QList)), this, SLOT(slotItemInfosAdded())); } d->model = model; d->filterModel = filterModel; setModel(d->filterModel); connect(d->filterModel, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged())); connect(d->filterModel, SIGNAL(layoutChanged()), this, SLOT(layoutWasChanged()), Qt::QueuedConnection); connect(d->model, SIGNAL(imageInfosAdded(QList)), this, SLOT(slotItemInfosAdded())); emit modelChanged(); if (d->delegate) { d->delegate->setAllOverlaysActive(true); } } ItemModel* ItemCategorizedView::imageModel() const { return d->model; } ImageSortFilterModel* ItemCategorizedView::imageSortFilterModel() const { return d->filterModel; } ItemFilterModel* ItemCategorizedView::imageFilterModel() const { return d->filterModel->imageFilterModel(); } ItemThumbnailModel* ItemCategorizedView::imageThumbnailModel() const { return qobject_cast(d->model); } ItemAlbumModel* ItemCategorizedView::imageAlbumModel() const { return qobject_cast(d->model); } ItemAlbumFilterModel* ItemCategorizedView::imageAlbumFilterModel() const { return qobject_cast(d->filterModel->imageFilterModel()); } QSortFilterProxyModel* ItemCategorizedView::filterModel() const { return d->filterModel; } ItemDelegate* ItemCategorizedView::delegate() const { return d->delegate; } void ItemCategorizedView::setItemDelegate(ItemDelegate* delegate) { ThumbnailSize oldSize = thumbnailSize(); ItemDelegate* oldDelegate = d->delegate; if (oldDelegate) { hideIndexNotification(); d->delegate->setAllOverlaysActive(false); d->delegate->setViewOnAllOverlays(0); // Note: Be precise, no wildcard disconnect! disconnect(d->delegate, SIGNAL(requestNotification(QModelIndex,QString)), this, SLOT(showIndexNotification(QModelIndex,QString))); disconnect(d->delegate, SIGNAL(hideNotification()), this, SLOT(hideIndexNotification())); } d->delegate = delegate; d->delegate->setThumbnailSize(oldSize); if (oldDelegate) { d->delegate->setSpacing(oldDelegate->spacing()); } ItemViewCategorized::setItemDelegate(d->delegate); setCategoryDrawer(d->delegate->categoryDrawer()); updateDelegateSizes(); d->delegate->setViewOnAllOverlays(this); d->delegate->setAllOverlaysActive(true); connect(d->delegate, SIGNAL(requestNotification(QModelIndex,QString)), this, SLOT(showIndexNotification(QModelIndex,QString))); connect(d->delegate, SIGNAL(hideNotification()), this, SLOT(hideIndexNotification())); } Album* ItemCategorizedView::currentAlbum() const { ItemAlbumModel* albumModel = imageAlbumModel(); /** TODO: Change to QList return type **/ if (albumModel && !(albumModel->currentAlbums().isEmpty())) { return albumModel->currentAlbums().first(); } return 0; } ItemInfo ItemCategorizedView::currentInfo() const { return imageInfo(currentIndex()); } QUrl ItemCategorizedView::currentUrl() const { return currentInfo().fileUrl(); } ItemInfo ItemCategorizedView::imageInfo(const QModelIndex& index) const { return d->filterModel->imageInfo(index); } ItemInfoList ItemCategorizedView::imageInfos(const QList& indexes) const { return ItemInfoList(d->filterModel->imageInfos(indexes)); } ItemInfoList ItemCategorizedView::allItemInfos() const { return ItemInfoList(d->filterModel->imageInfosSorted()); } QList ItemCategorizedView::allUrls() const { return allItemInfos().toImageUrlList(); } ItemInfoList ItemCategorizedView::selectedItemInfos() const { return imageInfos(selectedIndexes()); } ItemInfoList ItemCategorizedView::selectedItemInfosCurrentFirst() const { QModelIndexList indexes = selectedIndexes(); const QModelIndex current = currentIndex(); if (!indexes.isEmpty()) { if (indexes.first() != current) { if (indexes.removeOne(current)) { indexes.prepend(current); } } } return imageInfos(indexes); } void ItemCategorizedView::toIndex(const QUrl& url) { ItemViewCategorized::toIndex(d->filterModel->indexForPath(url.toLocalFile())); } QModelIndex ItemCategorizedView::indexForInfo(const ItemInfo& info) const { return d->filterModel->indexForItemInfo(info); } ItemInfo ItemCategorizedView::nextInOrder(const ItemInfo& startingPoint, int nth) { QModelIndex index = d->filterModel->indexForItemInfo(startingPoint); if (!index.isValid()) { return ItemInfo(); } return imageInfo(d->filterModel->index(index.row() + nth, 0, QModelIndex())); } QModelIndex ItemCategorizedView::nextIndexHint(const QModelIndex& anchor, const QItemSelectionRange& removed) const { QModelIndex hint = ItemViewCategorized::nextIndexHint(anchor, removed); ItemInfo info = imageInfo(anchor); //qCDebug(DIGIKAM_GENERAL_LOG) << "Having initial hint" << hint << "for" << anchor << d->model->numberOfIndexesForItemInfo(info); // Fixes a special case of multiple (face) entries for the same image. // If one is removed, any entry of the same image shall be preferred. if (d->model->numberOfIndexesForItemInfo(info) > 1) { // The hint is for a different info, but we may have a hint for the same info if (info != imageInfo(hint)) { int minDiff = d->filterModel->rowCount(); QList indexesForItemInfo = d->filterModel->mapListFromSource(d->model->indexesForItemInfo(info)); foreach(const QModelIndex& index, indexesForItemInfo) { if (index == anchor || !index.isValid() || removed.contains(index)) { continue; } int distance = qAbs(index.row() - anchor.row()); if (distance < minDiff) { minDiff = distance; hint = index; //qCDebug(DIGIKAM_GENERAL_LOG) << "Chose index" << hint << "at distance" << minDiff << "to" << anchor; } } } } return hint; } void ItemCategorizedView::openAlbum(const QList& albums) { ItemAlbumModel* const albumModel = imageAlbumModel(); if (albumModel) { albumModel->openAlbum(albums); } } ThumbnailSize ItemCategorizedView::thumbnailSize() const { /* ItemThumbnailModel* const thumbModel = imageThumbnailModel(); if (thumbModel) return thumbModel->thumbnailSize(); */ if (d->delegate) { return d->delegate->thumbnailSize(); } return ThumbnailSize(); } void ItemCategorizedView::setThumbnailSize(int size) { setThumbnailSize(ThumbnailSize(size)); } void ItemCategorizedView::setThumbnailSize(const ThumbnailSize& s) { // we abuse this pair of method calls to restore scroll position layoutAboutToBeChanged(); d->delegate->setThumbnailSize(s); layoutWasChanged(); } void ItemCategorizedView::setCurrentWhenAvailable(qlonglong imageId) { d->scrollToItemId = imageId; } void ItemCategorizedView::setCurrentUrlWhenAvailable(const QUrl& url) { if (url.isEmpty()) { clearSelection(); setCurrentIndex(QModelIndex()); return; } QString path = url.toLocalFile(); QModelIndex index = d->filterModel->indexForPath(path); if (!index.isValid()) { d->unknownCurrentUrl = url; return; } clearSelection(); setCurrentIndex(index); d->unknownCurrentUrl.clear(); } void ItemCategorizedView::setCurrentUrl(const QUrl& url) { if (url.isEmpty()) { clearSelection(); setCurrentIndex(QModelIndex()); return; } QString path = url.toLocalFile(); QModelIndex index = d->filterModel->indexForPath(path); if (!index.isValid()) { return; } clearSelection(); setCurrentIndex(index); } void ItemCategorizedView::setCurrentInfo(const ItemInfo& info) { QModelIndex index = d->filterModel->indexForItemInfo(info); clearSelection(); setCurrentIndex(index); } void ItemCategorizedView::setSelectedUrls(const QList& urlList) { QItemSelection mySelection; for (QList::const_iterator it = urlList.constBegin(); it!=urlList.constEnd(); ++it) { const QString path = it->toLocalFile(); const QModelIndex index = d->filterModel->indexForPath(path); if (!index.isValid()) { qCWarning(DIGIKAM_GENERAL_LOG) << "no QModelIndex found for" << *it; } else { // TODO: is there a better way? mySelection.select(index, index); } } clearSelection(); selectionModel()->select(mySelection, QItemSelectionModel::Select); } void ItemCategorizedView::setSelectedItemInfos(const QList& infos) { QItemSelection mySelection; foreach(const ItemInfo& info, infos) { QModelIndex index = d->filterModel->indexForItemInfo(info); mySelection.select(index, index); } selectionModel()->select(mySelection, QItemSelectionModel::ClearAndSelect); } void ItemCategorizedView::hintAt(const ItemInfo& info) { if (info.isNull()) { return; } QModelIndex index = d->filterModel->indexForItemInfo(info); if (!index.isValid()) { return; } selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); scrollTo(index); } void ItemCategorizedView::addOverlay(ItemDelegateOverlay* overlay, ItemDelegate* delegate) { if (!delegate) { delegate = d->delegate; } delegate->installOverlay(overlay); if (delegate == d->delegate) { overlay->setView(this); overlay->setActive(true); } } void ItemCategorizedView::removeOverlay(ItemDelegateOverlay* overlay) { ItemDelegate* delegate = dynamic_cast(overlay->delegate()); if (delegate) { delegate->removeOverlay(overlay); } overlay->setView(0); } void ItemCategorizedView::updateGeometries() { ItemViewCategorized::updateGeometries(); d->delayedEnterTimer->start(); } void ItemCategorizedView::slotDelayedEnter() { // re-emit entered() for index under mouse (after layout). QModelIndex mouseIndex = indexAt(mapFromGlobal(QCursor::pos())); if (mouseIndex.isValid()) { emit DCategorizedView::entered(mouseIndex); } } void ItemCategorizedView::addSelectionOverlay(ItemDelegate* delegate) { addOverlay(new ItemSelectionOverlay(this), delegate); } void ItemCategorizedView::scrollToStoredItem() { if (d->scrollToItemId) { if (d->model->hasImage(d->scrollToItemId)) { QModelIndex index = d->filterModel->indexForImageId(d->scrollToItemId); setCurrentIndex(index); scrollToRelaxed(index, QAbstractItemView::PositionAtCenter); d->scrollToItemId = 0; } } } void ItemCategorizedView::slotItemInfosAdded() { if (d->scrollToItemId) { scrollToStoredItem(); } else if (!d->unknownCurrentUrl.isEmpty()) { QTimer::singleShot(100, this, SLOT(slotCurrentUrlTimer())); } } void ItemCategorizedView::slotCurrentUrlTimer() { setCurrentUrl(d->unknownCurrentUrl); d->unknownCurrentUrl.clear(); } void ItemCategorizedView::slotFileChanged(const QString& filePath) { QModelIndex index = d->filterModel->indexForPath(filePath); if (index.isValid()) { update(index); } } -void ItemCategorizedView::indexActivated(const QModelIndex& index, QMouseEvent* const event) +void ItemCategorizedView::indexActivated(const QModelIndex& index, Qt::KeyboardModifiers modifiers) { ItemInfo info = imageInfo(index); if (!info.isNull()) { - activated(info, event); + activated(info, modifiers); emit imageActivated(info); } } void ItemCategorizedView::currentChanged(const QModelIndex& index, const QModelIndex& previous) { ItemViewCategorized::currentChanged(index, previous); emit currentChanged(imageInfo(index)); } void ItemCategorizedView::selectionChanged(const QItemSelection& selectedItems, const QItemSelection& deselectedItems) { ItemViewCategorized::selectionChanged(selectedItems, deselectedItems); if (!selectedItems.isEmpty()) { emit selected(imageInfos(selectedItems.indexes())); } if (!deselectedItems.isEmpty()) { emit deselected(imageInfos(deselectedItems.indexes())); } } Album* ItemCategorizedView::albumAt(const QPoint& pos) const { if (imageFilterModel()->imageSortSettings().categorizationMode == ItemSortSettings::CategoryByAlbum) { QModelIndex categoryIndex = indexForCategoryAt(pos); if (categoryIndex.isValid()) { int albumId = categoryIndex.data(ItemFilterModel::CategoryAlbumIdRole).toInt(); return AlbumManager::instance()->findPAlbum(albumId); } } return currentAlbum(); } -void ItemCategorizedView::activated(const ItemInfo&, QMouseEvent*) +void ItemCategorizedView::activated(const ItemInfo&, Qt::KeyboardModifiers) { // implemented in subclass } void ItemCategorizedView::showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index) { showContextMenuOnInfo(event, imageInfo(index)); } void ItemCategorizedView::showContextMenuOnInfo(QContextMenuEvent*, const ItemInfo&) { // implemented in subclass } void ItemCategorizedView::paintEvent(QPaintEvent* e) { // We want the thumbnails to be loaded in order. ItemThumbnailModel* const thumbModel = imageThumbnailModel(); if (thumbModel) { QModelIndexList indexesToThumbnail = imageFilterModel()->mapListToSource(categorizedIndexesIn(viewport()->rect())); d->delegate->prepareThumbnails(thumbModel, indexesToThumbnail); } ItemViewCategorized::paintEvent(e); } QItemSelectionModel* ItemCategorizedView::getSelectionModel() const { return selectionModel(); } AbstractItemDragDropHandler* ItemCategorizedView::dragDropHandler() const { return d->model->dragDropHandler(); } void ItemCategorizedView::slotIccSettingsChanged(const ICCSettingsContainer&, const ICCSettingsContainer&) { viewport()->update(); } } // namespace Digikam diff --git a/core/app/items/views/itemcategorizedview.h b/core/app/items/views/itemcategorizedview.h index 5fccbd0168..ae9926f914 100644 --- a/core/app/items/views/itemcategorizedview.h +++ b/core/app/items/views/itemcategorizedview.h @@ -1,228 +1,228 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-04-22 * Description : Qt model-view for items * * Copyright (C) 2009-2012 by Marcel Wiesweg * 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_ITEM_CATEGORIZED_VIEW_H #define DIGIKAM_ITEM_CATEGORIZED_VIEW_H // Local includes #include "iteminfo.h" #include "itemviewcategorized.h" #include "thumbnailsize.h" #include "iccsettingscontainer.h" namespace Digikam { class Album; class ItemAlbumModel; class ItemAlbumFilterModel; class ItemModel; class ItemFilterModel; class ImageSortFilterModel; class ItemDelegate; class ItemDelegateOverlay; class ItemThumbnailModel; class ItemCategorizedView : public ItemViewCategorized { Q_OBJECT public: explicit ItemCategorizedView(QWidget* const parent = 0); ~ItemCategorizedView(); void setModels(ItemModel* model, ImageSortFilterModel* filterModel); ItemModel* imageModel() const; ImageSortFilterModel* imageSortFilterModel() const; QItemSelectionModel* getSelectionModel() const; /// Returns any ItemFilterMode in chain. May not be sourceModel() ItemFilterModel* imageFilterModel() const; /// Returns 0 if the ItemModel is not an ItemThumbnailModel ItemThumbnailModel* imageThumbnailModel() const; /// Returns 0 if the ItemModel is not an ItemAlbumModel ItemAlbumModel* imageAlbumModel() const; ItemAlbumFilterModel* imageAlbumFilterModel() const; ItemDelegate* delegate() const; Album* currentAlbum() const; ItemInfo currentInfo() const; QUrl currentUrl() const; ItemInfoList allItemInfos() const; QList allUrls() const; ItemInfoList selectedItemInfos() const; ItemInfoList selectedItemInfosCurrentFirst() const; /** Selects the index as current and scrolls to it. */ void toIndex(const QUrl& url); /** Returns the n-th info after the given one. * Specifically, return the previous info for nth = -1 * and the next info for n = 1. * Returns a null info if either startingPoint or the nth info are * not contained in the model. */ ItemInfo nextInOrder(const ItemInfo& startingPoint, int nth); ItemInfo previousInfo(const ItemInfo& info) { return nextInOrder(info, -1); } ItemInfo nextInfo(const ItemInfo& info) { return nextInOrder(info, 1); } QModelIndex indexForInfo(const ItemInfo& info) const; ThumbnailSize thumbnailSize() const; virtual void setThumbnailSize(const ThumbnailSize& size); /** If the model is categorized by an album, returns the album of the category * that contains the position. * If this is not applicable, return the current album. May return 0. */ Album* albumAt(const QPoint& pos) const; /// Add and remove an overlay. It will as well be removed automatically when destroyed. /// Unless you pass a different delegate, the current delegate will be used. void addOverlay(ItemDelegateOverlay* overlay, ItemDelegate* delegate = 0); void removeOverlay(ItemDelegateOverlay* overlay); void addSelectionOverlay(ItemDelegate* delegate = 0); public Q_SLOTS: void openAlbum(const QList& album); void setThumbnailSize(int size); /** Scroll the view to the given item when it becomes available. */ void setCurrentWhenAvailable(qlonglong imageId); /** Set as current item when it becomes available, the item identified by its file url. */ void setCurrentUrlWhenAvailable(const QUrl& url); /** Set as current item the item identified by its file url. */ void setCurrentUrl(const QUrl& url); /** Set as current item the item identified by the imageinfo. */ void setCurrentInfo(const ItemInfo& info); /** Set selected items identified by their file urls. */ void setSelectedUrls(const QList& urlList); /** Set selected items. */ void setSelectedItemInfos(const QList& infos); /** Does something to gain attention for info, but not changing current selection. */ void hintAt(const ItemInfo& info); Q_SIGNALS: void currentChanged(const ItemInfo& info); /// Emitted when new items are selected. The parameter includes only the newly selected infos, /// there may be other already selected infos. void selected(const QList& newSelectedInfos); /// Emitted when items are deselected. There may be other selected infos left. /// This signal is not emitted when the model is reset; then only selectionCleared is emitted. void deselected(const QList& nowDeselectedInfos); /// Emitted when the given image is activated. Info is never null. void imageActivated(const ItemInfo& info); /// Emitted when a new model is set void modelChanged(); protected Q_SLOTS: void slotItemInfosAdded(); void slotCurrentUrlTimer(); protected: /// install default ItemAlbumModel and filter model, ready for use void installDefaultModels(); // reimplemented from parent class QSortFilterProxyModel* filterModel() const; AbstractItemDragDropHandler* dragDropHandler() const; QModelIndex nextIndexHint(const QModelIndex& indexToAnchor, const QItemSelectionRange& removed) const; void setItemDelegate(ItemDelegate* delegate); - void indexActivated(const QModelIndex& index, QMouseEvent* const event); + void indexActivated(const QModelIndex& index, Qt::KeyboardModifiers modifiers); void currentChanged(const QModelIndex& index, const QModelIndex& previous); void paintEvent(QPaintEvent* e); void selectionChanged(const QItemSelection&, const QItemSelection&); void updateGeometries(); /// Reimplement these in a subclass - virtual void activated(const ItemInfo& info, QMouseEvent* const event); + virtual void activated(const ItemInfo& info, Qt::KeyboardModifiers modifiers); virtual void showContextMenuOnInfo(QContextMenuEvent* event, const ItemInfo& info); virtual void showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index); ItemInfo imageInfo(const QModelIndex& index) const; ItemInfoList imageInfos(const QList& indexes) const; private Q_SLOTS: void slotIccSettingsChanged(const ICCSettingsContainer&, const ICCSettingsContainer&); void slotFileChanged(const QString& filePath); void slotDelayedEnter(); private: void scrollToStoredItem(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_ITEM_CATEGORIZED_VIEW_H diff --git a/core/libs/widgets/graphicsview/graphicsdimgview.cpp b/core/libs/widgets/graphicsview/graphicsdimgview.cpp index 5150e6f82d..9d319cf695 100644 --- a/core/libs/widgets/graphicsview/graphicsdimgview.cpp +++ b/core/libs/widgets/graphicsview/graphicsdimgview.cpp @@ -1,512 +1,520 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2010-04-30 * Description : Graphics View for DImg preview * * Copyright (C) 2010-2012 by Marcel Wiesweg * Copyright (C) 2011-2018 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 "graphicsdimgview.h" // Qt includes #include #include #include #include #include // Local includes #include "digikam_debug.h" #include "dimgpreviewitem.h" #include "imagezoomsettings.h" #include "paniconwidget.h" #include "previewlayout.h" #include "dimgchilditem.h" namespace Digikam { class Q_DECL_HIDDEN GraphicsDImgView::Private { public: explicit Private() { scene = 0; item = 0; layout = 0; cornerButton = 0; panIconPopup = 0; movingInProgress = false; showText = true; } QGraphicsScene* scene; GraphicsDImgItem* item; SinglePhotoPreviewLayout* layout; QToolButton* cornerButton; PanIconFrame* panIconPopup; QPoint mousePressPos; QPoint panningScrollPos; bool movingInProgress; bool showText; }; GraphicsDImgView::GraphicsDImgView(QWidget* const parent) : QGraphicsView(parent), d(new Private) { d->scene = new QGraphicsScene(this); d->scene->setItemIndexMethod(QGraphicsScene::NoIndex); setScene(d->scene); d->layout = new SinglePhotoPreviewLayout(this); d->layout->setGraphicsView(this); setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate); horizontalScrollBar()->setSingleStep(1); horizontalScrollBar()->setPageStep(1); verticalScrollBar()->setSingleStep(1); verticalScrollBar()->setPageStep(1); connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotContentsMoved())); connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotContentsMoved())); } GraphicsDImgView::~GraphicsDImgView() { delete d; } void GraphicsDImgView::setItem(GraphicsDImgItem* const item) { d->item = item; d->scene->addItem(d->item); d->layout->addItem(d->item); } GraphicsDImgItem* GraphicsDImgView::item() const { return d->item; } DImgPreviewItem* GraphicsDImgView::previewItem() const { return dynamic_cast(item()); } SinglePhotoPreviewLayout* GraphicsDImgView::layout() const { return d->layout; } void GraphicsDImgView::installPanIcon() { d->cornerButton = PanIconWidget::button(); setCornerWidget(d->cornerButton); connect(d->cornerButton, SIGNAL(pressed()), this, SLOT(slotCornerButtonPressed())); } void GraphicsDImgView::drawForeground(QPainter* p, const QRectF& rect) { QGraphicsView::drawForeground(p, rect); if (!d->movingInProgress) { QString text = d->item->userLoadingHint(); if (text.isNull() || !d->showText) { return; } QRect viewportRect = viewport()->rect(); QRect fontRect = p->fontMetrics().boundingRect(viewportRect, 0, text); QPoint drawingPoint(viewportRect.topRight().x() - fontRect.width() - 10, viewportRect.topRight().y() + 5); QPointF sceneDrawingPoint = mapToScene(drawingPoint); QRectF sceneDrawingRect(sceneDrawingPoint, fontRect.size()); if (!rect.intersects(sceneDrawingRect)) { return; } drawText(p, sceneDrawingRect, text); } } void GraphicsDImgView::drawText(QPainter* p, const QRectF& rect, const QString& text) { p->save(); p->setRenderHint(QPainter::Antialiasing, true); p->setBackgroundMode(Qt::TransparentMode); // increase width by 5 and height by 2 QRectF textRect = rect.adjusted(0, 0, 5, 2); // Draw background p->setPen(Qt::black); QColor semiTransBg = palette().color(QPalette::Window); semiTransBg.setAlpha(190); p->setBrush(semiTransBg); //p->translate(0.5, 0.5); p->drawRoundRect(textRect, 10.0, 10.0); // Draw shadow and text p->setPen(palette().color(QPalette::Window).dark(115)); p->drawText(textRect.translated(3, 1), text); p->setPen(palette().color(QPalette::WindowText)); p->drawText(textRect.translated(2, 0), text); p->restore(); } void GraphicsDImgView::mouseDoubleClickEvent(QMouseEvent* e) { QGraphicsView::mouseDoubleClickEvent(e); - +/* if (!acceptsMouseClick(e)) { return; } - +*/ if (e->button() == Qt::LeftButton) { emit leftButtonDoubleClicked(); + + if (!qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) + { + emit activated(); + } } } void GraphicsDImgView::mousePressEvent(QMouseEvent* e) { QGraphicsView::mousePressEvent(e); d->mousePressPos = QPoint(); d->movingInProgress = false; if (!acceptsMouseClick(e)) { return; } if (e->button() == Qt::LeftButton) { emit leftButtonClicked(); } if (e->button() == Qt::LeftButton || e->button() == Qt::MidButton) { d->mousePressPos = e->pos(); - if (e->button() == Qt::MidButton) + if (!qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) || e->button() == Qt::MidButton) { startPanning(e->pos()); } return; } if (e->button() == Qt::RightButton) { emit rightButtonClicked(); } } void GraphicsDImgView::mouseMoveEvent(QMouseEvent* e) { QGraphicsView::mouseMoveEvent(e); if ((e->buttons() & Qt::LeftButton || e->buttons() & Qt::MidButton) && !d->mousePressPos.isNull()) { if (!d->movingInProgress && e->buttons() & Qt::LeftButton) { if ((d->mousePressPos - e->pos()).manhattanLength() > QApplication::startDragDistance()) { startPanning(d->mousePressPos); } } if (d->movingInProgress) { continuePanning(e->pos()); } } } void GraphicsDImgView::mouseReleaseEvent(QMouseEvent* e) { QGraphicsView::mouseReleaseEvent(e); // Do not call acceptsMouseClick() here, only on press. Seems that release event are accepted per default. if ((e->button() == Qt::LeftButton || e->button() == Qt::MidButton) && !d->mousePressPos.isNull()) { if (!d->movingInProgress && e->button() == Qt::LeftButton) { - emit activated(); + if (qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) + { + emit activated(); + } } else { finishPanning(); } } d->movingInProgress = false; d->mousePressPos = QPoint(); } bool GraphicsDImgView::acceptsMouseClick(QMouseEvent* e) { // the basic condition is that now item ate the event if (e->isAccepted()) { return false; } return true; } void GraphicsDImgView::resizeEvent(QResizeEvent* e) { QGraphicsView::resizeEvent(e); d->layout->updateZoomAndSize(); emit resized(); emit viewportRectChanged(mapToScene(viewport()->rect()).boundingRect()); } void GraphicsDImgView::scrollContentsBy(int dx, int dy) { QGraphicsView::scrollContentsBy(dx, dy); emit viewportRectChanged(mapToScene(viewport()->rect()).boundingRect()); } void GraphicsDImgView::startPanning(const QPoint& pos) { if (horizontalScrollBar()->maximum() || verticalScrollBar()->maximum()) { d->movingInProgress = true; d->mousePressPos = pos; d->panningScrollPos = QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value()); viewport()->setCursor(Qt::SizeAllCursor); } } void GraphicsDImgView::continuePanning(const QPoint& pos) { QPoint delta = pos - d->mousePressPos; horizontalScrollBar()->setValue(d->panningScrollPos.x() + (isRightToLeft() ? delta.x() : -delta.x())); verticalScrollBar()->setValue(d->panningScrollPos.y() - delta.y()); emit contentsMoved(false); viewport()->update(); } void GraphicsDImgView::finishPanning() { emit contentsMoved(true); viewport()->unsetCursor(); } void GraphicsDImgView::scrollPointOnPoint(const QPointF& scenePos, const QPoint& viewportPos) { // This is inspired from QGraphicsView's centerOn() QPointF viewPoint = matrix().map(scenePos); if (horizontalScrollBar()->maximum()) { if (isRightToLeft()) { qint64 horizontal = 0; horizontal += horizontalScrollBar()->minimum(); horizontal += horizontalScrollBar()->maximum(); horizontal -= int(viewPoint.x() - viewportPos.x()); horizontalScrollBar()->setValue(horizontal); } else { horizontalScrollBar()->setValue(int(viewPoint.x() - viewportPos.x())); } } if (verticalScrollBar()->maximum()) { verticalScrollBar()->setValue(int(viewPoint.y() - viewportPos.y())); } viewport()->update(); } void GraphicsDImgView::wheelEvent(QWheelEvent* e) { if (e->modifiers() & Qt::ShiftModifier) { e->accept(); if (e->delta() < 0) { emit toNextImage(); } else if (e->delta() > 0) { emit toPreviousImage(); } return; } else if (e->modifiers() & Qt::ControlModifier) { // When zooming with the mouse-wheel, the image center is kept fixed. if (e->delta() < 0) { d->layout->decreaseZoom(e->pos()); } else if (e->delta() > 0) { d->layout->increaseZoom(e->pos()); } return; } QGraphicsView::wheelEvent(e); } void GraphicsDImgView::slotCornerButtonPressed() { if (d->panIconPopup) { d->panIconPopup->hide(); d->panIconPopup->deleteLater(); d->panIconPopup = 0; } d->panIconPopup = new PanIconFrame(this); PanIconWidget* const pan = new PanIconWidget(d->panIconPopup); //connect(pan, SIGNAL(signalSelectionTakeFocus()), // this, SIGNAL(signalContentTakeFocus())); connect(pan, SIGNAL(signalSelectionMoved(QRect,bool)), this, SLOT(slotPanIconSelectionMoved(QRect,bool))); connect(pan, SIGNAL(signalHidden()), this, SLOT(slotPanIconHidden())); pan->setImage(180, 120, item()->image()); QRectF sceneRect(mapToScene(viewport()->rect().topLeft()), mapToScene(viewport()->rect().bottomRight())); pan->setRegionSelection(item()->zoomSettings()->sourceRect(sceneRect).toRect()); pan->setMouseFocus(); d->panIconPopup->setMainWidget(pan); //slotContentTakeFocus(); QPoint g = mapToGlobal(viewport()->pos()); g.setX(g.x()+ viewport()->size().width()); g.setY(g.y()+ viewport()->size().height()); d->panIconPopup->popup(QPoint(g.x() - d->panIconPopup->width(), g.y() - d->panIconPopup->height())); pan->setCursorToLocalRegionSelectionCenter(); } void GraphicsDImgView::slotPanIconHidden() { d->cornerButton->blockSignals(true); d->cornerButton->animateClick(); d->cornerButton->blockSignals(false); } void GraphicsDImgView::slotPanIconSelectionMoved(const QRect& imageRect, bool b) { QRectF zoomRect = item()->zoomSettings()->mapImageToZoom(imageRect); qCDebug(DIGIKAM_WIDGETS_LOG) << imageRect << zoomRect; centerOn(item()->mapToScene(zoomRect.center())); if (b) { d->panIconPopup->hide(); d->panIconPopup->deleteLater(); d->panIconPopup = 0; slotPanIconHidden(); //slotContentLeaveFocus(); } } void GraphicsDImgView::slotContentsMoved() { emit contentsMoving(horizontalScrollBar()->value(), verticalScrollBar()->value()); } int GraphicsDImgView::contentsX() const { return horizontalScrollBar()->value(); } int GraphicsDImgView::contentsY() const { return verticalScrollBar()->value(); } void GraphicsDImgView::setContentsPos(int x, int y) { horizontalScrollBar()->setValue(x); verticalScrollBar()->setValue(y); } void GraphicsDImgView::setShowText(bool val) { d->showText = val; } QRect GraphicsDImgView::visibleArea() const { return (mapToScene(viewport()->geometry()).boundingRect().toRect()); } void GraphicsDImgView::fitToWindow() { layout()->fitToWindow(); update(); } void GraphicsDImgView::toggleFullScreen(bool set) { if (set) { d->scene->setBackgroundBrush(Qt::black); setFrameShape(QFrame::NoFrame); } else { d->scene->setBackgroundBrush(Qt::NoBrush); setFrameShape(QFrame::StyledPanel); } } } // namespace Digikam diff --git a/core/libs/widgets/graphicsview/graphicsdimgview.h b/core/libs/widgets/graphicsview/graphicsdimgview.h index a344cbee6b..860ad57679 100644 --- a/core/libs/widgets/graphicsview/graphicsdimgview.h +++ b/core/libs/widgets/graphicsview/graphicsdimgview.h @@ -1,138 +1,139 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2010-04-30 * Description : Graphics View for DImg preview * * Copyright (C) 2010-2012 by Marcel Wiesweg * Copyright (C) 2011-2018 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_GRAPHICS_DIMG_VIEW_H #define DIGIKAM_GRAPHICS_DIMG_VIEW_H // Qt includes #include // Local includes #include "digikam_export.h" namespace Digikam { class GraphicsDImgItem; class DImgPreviewItem; class SinglePhotoPreviewLayout; class DIGIKAM_EXPORT GraphicsDImgView : public QGraphicsView { Q_OBJECT public: explicit GraphicsDImgView(QWidget* const parent = 0); virtual ~GraphicsDImgView(); /** Store internal instance of item as GraphicsDImgItem. You can store DImgPreviewItem object also by this method. * Use item() or previewItem() to get right version. * Note: if you store a GraphicsDImgItem object, previewItem() will return 0. */ void setItem(GraphicsDImgItem* const item); /** Return the instance of item set by setItem(). */ GraphicsDImgItem* item() const; /** Return a cast of item instance of item set by setItem() as DImgPreviewItem * Note: if you store a GraphicsDImgItem object using setItem(), this method will return 0. */ DImgPreviewItem* previewItem() const; SinglePhotoPreviewLayout* layout() const; /** Scrolls the view such that scenePos (in scene coordinates * is displayed on the viewport at viewportPos (in viewport coordinates). * E.g., calling scrollPointOnPoint(scenePos, viewport()->rect().center()) is * equivalent to calling centerOn(scenePos). */ void scrollPointOnPoint(const QPointF& scenePos, const QPoint& viewportPos); // Change from protected to public to be used by ImageRegionWidget and ImageRegionItem void drawText(QPainter* p, const QRectF& rect, const QString& text); int contentsX() const; int contentsY() const; QRect visibleArea() const; void setContentsPos(int x, int y); void fitToWindow(); void toggleFullScreen(bool set); Q_SIGNALS: void contentsMoving(int, int); void rightButtonClicked(); void leftButtonClicked(); void leftButtonDoubleClicked(); void activated(); void toNextImage(); void toPreviousImage(); void contentsMoved(bool panningFinished); void resized(); + //void contentTakeFocus(); void viewportRectChanged(const QRectF& viewportRect); protected: void drawForeground(QPainter* painter, const QRectF& rect); void installPanIcon(); void mouseDoubleClickEvent(QMouseEvent*); void mousePressEvent(QMouseEvent*); void mouseMoveEvent(QMouseEvent*); void mouseReleaseEvent(QMouseEvent*); void wheelEvent(QWheelEvent*); void resizeEvent(QResizeEvent*); void startPanning(const QPoint& pos); void continuePanning(const QPoint& pos); void finishPanning(); void setShowText(bool value); virtual bool acceptsMouseClick(QMouseEvent* e); virtual void scrollContentsBy(int dx, int dy); protected Q_SLOTS: void slotContentsMoved(); void slotCornerButtonPressed(); void slotPanIconHidden(); virtual void slotPanIconSelectionMoved(const QRect&, bool); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_GRAPHICS_DIMG_VIEW_H diff --git a/core/libs/widgets/itemview/itemviewcategorized.cpp b/core/libs/widgets/itemview/itemviewcategorized.cpp index b56bbbcd68..b9d05c4240 100644 --- a/core/libs/widgets/itemview/itemviewcategorized.cpp +++ b/core/libs/widgets/itemview/itemviewcategorized.cpp @@ -1,1050 +1,1062 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2010-01-16 * Description : Qt item view for images * * Copyright (C) 2009-2012 by Marcel Wiesweg * Copyright (C) 2011-2018 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 "itemviewcategorized.h" // Qt includes #include #include #include #include #include #include // Local includes #include "digikam_debug.h" #include "thememanager.h" #include "ditemdelegate.h" #include "abstractitemdragdrophandler.h" #include "itemviewtooltip.h" namespace Digikam { class Q_DECL_HIDDEN ItemViewCategorized::Private { public: explicit Private(ItemViewCategorized* const q) : delegate(0), toolTip(0), notificationToolTip(0), showToolTip(false), usePointingHand(true), scrollStepFactor(10), currentMouseEvent(0), ensureOneSelectedItem(false), ensureInitialSelectedItem(false), scrollCurrentToCenter(false), mouseButtonPressed(Qt::NoButton), hintAtSelectionRow(-1), q(q) { } QModelIndex scrollPositionHint() const; public: DItemDelegate* delegate; ItemViewToolTip* toolTip; ItemViewToolTip* notificationToolTip; bool showToolTip; bool usePointingHand; int scrollStepFactor; QMouseEvent* currentMouseEvent; bool ensureOneSelectedItem; bool ensureInitialSelectedItem; bool scrollCurrentToCenter; Qt::MouseButton mouseButtonPressed; QPersistentModelIndex hintAtSelectionIndex; int hintAtSelectionRow; QPersistentModelIndex hintAtScrollPosition; ItemViewCategorized* const q; }; QModelIndex ItemViewCategorized::Private::scrollPositionHint() const { if (q->verticalScrollBar()->value() == q->verticalScrollBar()->minimum()) { return QModelIndex(); } QModelIndex hint = q->currentIndex(); // If the user scrolled, do not take current item, but first visible if (!hint.isValid() || !q->viewport()->rect().intersects(q->visualRect(hint))) { QList visibleIndexes = q->categorizedIndexesIn(q->viewport()->rect()); if (!visibleIndexes.isEmpty()) { hint = visibleIndexes.first(); } } return hint; } // ------------------------------------------------------------------------------- ItemViewCategorized::ItemViewCategorized(QWidget* const parent) : DCategorizedView(parent), d(new Private(this)) { setViewMode(QListView::IconMode); setLayoutDirection(Qt::LeftToRight); setFlow(QListView::LeftToRight); setResizeMode(QListView::Adjust); setMovement(QListView::Static); setWrapping(true); // important optimization for layouting setUniformItemSizes(true); // disable "feature" from DCategorizedView setDrawDraggedItems(false); setSelectionMode(QAbstractItemView::ExtendedSelection); setDragEnabled(true); setEditTriggers(QAbstractItemView::NoEditTriggers); viewport()->setAcceptDrops(true); setMouseTracking(true); + connect(this, SIGNAL(activated(QModelIndex)), + this, SLOT(slotActivated(QModelIndex))); + connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(slotClicked(QModelIndex))); connect(this, SIGNAL(entered(QModelIndex)), this, SLOT(slotEntered(QModelIndex))); - connect(this, SIGNAL(clicked(QModelIndex)), - this, SLOT(slotActivated(QModelIndex))); - - connect(this, SIGNAL(doubleClicked(QModelIndex)), - this, SLOT(slotActivated(QModelIndex))); - connect(ThemeManager::instance(), SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); } ItemViewCategorized::~ItemViewCategorized() { delete d; } void ItemViewCategorized::setToolTip(ItemViewToolTip* tip) { d->toolTip = tip; } void ItemViewCategorized::setItemDelegate(DItemDelegate* delegate) { if (d->delegate == delegate) { return; } if (d->delegate) { disconnect(d->delegate, SIGNAL(gridSizeChanged(QSize)), this, SLOT(slotGridSizeChanged(QSize))); } d->delegate = delegate; DCategorizedView::setItemDelegate(d->delegate); connect(d->delegate, SIGNAL(gridSizeChanged(QSize)), this, SLOT(slotGridSizeChanged(QSize))); } void ItemViewCategorized::setSpacing(int spacing) { d->delegate->setSpacing(spacing); } void ItemViewCategorized::setUsePointingHandCursor(bool useCursor) { d->usePointingHand = useCursor; } void ItemViewCategorized::setScrollStepGranularity(int factor) { d->scrollStepFactor = qMax(1, factor); } DItemDelegate* ItemViewCategorized::delegate() const { return d->delegate; } int ItemViewCategorized::numberOfSelectedIndexes() const { return selectedIndexes().size(); } void ItemViewCategorized::toFirstIndex() { QModelIndex index = moveCursor(MoveHome, Qt::NoModifier); clearSelection(); setCurrentIndex(index); scrollToTop(); } void ItemViewCategorized::toLastIndex() { QModelIndex index = moveCursor(MoveEnd, Qt::NoModifier); clearSelection(); setCurrentIndex(index); scrollToBottom(); } void ItemViewCategorized::toNextIndex() { toIndex(moveCursor(MoveNext, Qt::NoModifier)); } void ItemViewCategorized::toPreviousIndex() { toIndex(moveCursor(MovePrevious, Qt::NoModifier)); } void ItemViewCategorized::toIndex(const QModelIndex& index) { if (!index.isValid()) { return; } clearSelection(); setCurrentIndex(index); scrollTo(index); } void ItemViewCategorized::awayFromSelection() { QItemSelection selection = selectionModel()->selection(); if (selection.isEmpty()) { return; } const QModelIndex first = model()->index(0, 0); const QModelIndex last = model()->index(model()->rowCount() - 1, 0); if (selection.contains(first) && selection.contains(last)) { QItemSelection remaining(first, last); remaining.merge(selection, QItemSelectionModel::Toggle); QList indexes = remaining.indexes(); if (indexes.isEmpty()) { clearSelection(); setCurrentIndex(QModelIndex()); } else { toIndex(remaining.indexes().first()); } } else if (selection.contains(last)) { setCurrentIndex(selection.indexes().first()); toPreviousIndex(); } else { setCurrentIndex(selection.indexes().last()); toNextIndex(); } } void ItemViewCategorized::scrollToRelaxed(const QModelIndex& index, QAbstractItemView::ScrollHint hint) { if (viewport()->rect().intersects(visualRect(index))) { return; } scrollTo(index, hint); } void ItemViewCategorized::invertSelection() { const QModelIndex topLeft = model()->index(0, 0); const QModelIndex bottomRight = model()->index(model()->rowCount() - 1, 0); const QItemSelection selection(topLeft, bottomRight); selectionModel()->select(selection, QItemSelectionModel::Toggle); } void ItemViewCategorized::setSelectedIndexes(const QList& indexes) { if (selectedIndexes() == indexes) { return; } QItemSelection mySelection; foreach (const QModelIndex& index, indexes) { mySelection.select(index, index); } selectionModel()->select(mySelection, QItemSelectionModel::ClearAndSelect); } void ItemViewCategorized::setToolTipEnabled(bool enable) { d->showToolTip = enable; } bool ItemViewCategorized::isToolTipEnabled() const { return d->showToolTip; } void ItemViewCategorized::slotThemeChanged() { viewport()->update(); } void ItemViewCategorized::slotSetupChanged() { viewport()->update(); } void ItemViewCategorized::slotGridSizeChanged(const QSize& gridSize) { setGridSize(gridSize); if (!gridSize.isNull()) { horizontalScrollBar()->setSingleStep(gridSize.width() / d->scrollStepFactor); verticalScrollBar()->setSingleStep(gridSize.height() / d->scrollStepFactor); } } void ItemViewCategorized::updateDelegateSizes() { QStyleOptionViewItem option = viewOptions(); /* int frameAroundContents = 0; if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) { frameAroundContents = style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2; } const int contentWidth = viewport()->width() - 1 - frameAroundContents - style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, verticalScrollBar()); const int contentHeight = viewport()->height() - 1 - frameAroundContents - style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, horizontalScrollBar()); option.rect = QRect(0, 0, contentWidth, contentHeight); */ option.rect = QRect(QPoint(0, 0), viewport()->size()); d->delegate->setDefaultViewOptions(option); } void ItemViewCategorized::slotActivated(const QModelIndex& index) { + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + if (d->currentMouseEvent) { // Ignore activation if Ctrl or Shift is pressed (for selection) - const Qt::KeyboardModifiers modifiers = d->currentMouseEvent->modifiers(); - const Qt::MouseButton button = d->currentMouseEvent->button(); - const bool shiftKeyPressed = modifiers & Qt::ShiftModifier; - const bool controlKeyPressed = modifiers & Qt::ControlModifier; - const bool rightButtonPressed = button & Qt::RightButton; + modifiers = d->currentMouseEvent->modifiers(); + const bool shiftKeyPressed = modifiers & Qt::ShiftModifier; + const bool controlKeyPressed = modifiers & Qt::ControlModifier; + + if (shiftKeyPressed || controlKeyPressed) + { + return; + } + + const bool rightClick = d->currentMouseEvent->button() & Qt::RightButton; - if (shiftKeyPressed || controlKeyPressed || rightButtonPressed) + if (rightClick) { return; } // if the activation is caused by mouse click (not keyboard) // we need to check the hot area if (d->currentMouseEvent->isAccepted() && !d->delegate->acceptsActivation(d->currentMouseEvent->pos(), visualRect(index), index)) { return; } } - indexActivated(index, d->currentMouseEvent); + d->currentMouseEvent = 0; + indexActivated(index, modifiers); } void ItemViewCategorized::slotClicked(const QModelIndex& index) { if (d->currentMouseEvent) { emit clicked(d->currentMouseEvent, index); } } void ItemViewCategorized::slotEntered(const QModelIndex& index) { if (d->currentMouseEvent) { emit entered(d->currentMouseEvent, index); } } void ItemViewCategorized::reset() { DCategorizedView::reset(); // FIXME : Emitting this causes a crash importstackedview, because the model is not yet set. // atm there's a check against null models though. emit selectionChanged(); emit selectionCleared(); d->ensureInitialSelectedItem = true; d->hintAtScrollPosition = QModelIndex(); d->hintAtSelectionIndex = QModelIndex(); d->hintAtSelectionRow = -1; verticalScrollBar()->setValue(verticalScrollBar()->minimum()); horizontalScrollBar()->setValue(horizontalScrollBar()->minimum()); } void ItemViewCategorized::selectionChanged(const QItemSelection& selectedItems, const QItemSelection& deselectedItems) { DCategorizedView::selectionChanged(selectedItems, deselectedItems); emit selectionChanged(); if (!selectionModel()->hasSelection()) { emit selectionCleared(); } userInteraction(); } void ItemViewCategorized::rowsInserted(const QModelIndex& parent, int start, int end) { DCategorizedView::rowsInserted(parent, start, end); if (start == 0) { ensureSelectionAfterChanges(); } } void ItemViewCategorized::rowsRemoved(const QModelIndex& parent, int start, int end) { DCategorizedView::rowsRemoved(parent, start, end); if (d->scrollCurrentToCenter) { scrollTo(currentIndex(), QAbstractItemView::PositionAtCenter); } } void ItemViewCategorized::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) { DCategorizedView::rowsAboutToBeRemoved(parent, start, end); // Ensure one selected item int totalToRemove = end - start + 1; bool remainingRows = model()->rowCount(parent) > totalToRemove; if (!remainingRows) { return; } QItemSelection removed(model()->index(start, 0), model()->index(end, 0)); if (selectionModel()->hasSelection()) { // find out which selected indexes are left after rows are removed QItemSelection selected = selectionModel()->selection(); QModelIndex current = currentIndex(); QModelIndex indexToAnchor; if (selected.contains(current)) { indexToAnchor = current; } else if (!selected.isEmpty()) { indexToAnchor = selected.first().topLeft(); } selected.merge(removed, QItemSelectionModel::Deselect); if (selected.isEmpty()) { QModelIndex newCurrent = nextIndexHint(indexToAnchor, removed.first() /*a range*/); setCurrentIndex(newCurrent); } } QModelIndex hint = d->scrollPositionHint(); if (removed.contains(hint)) { d->hintAtScrollPosition = nextIndexHint(hint, removed.first() /*a range*/); } } void ItemViewCategorized::layoutAboutToBeChanged() { if (selectionModel()) { d->ensureOneSelectedItem = selectionModel()->hasSelection(); } else { qCWarning(DIGIKAM_GENERAL_LOG) << "Called without selection model, check whether the models are ok.."; } QModelIndex current = currentIndex(); // store some hints so that if all selected items were removed do not need to default to 0,0. if (d->ensureOneSelectedItem) { QItemSelection currentSelection = selectionModel()->selection(); QModelIndex indexToAnchor; if (currentSelection.contains(current)) { indexToAnchor = current; } else if (!currentSelection.isEmpty()) { indexToAnchor = currentSelection.first().topLeft(); } if (indexToAnchor.isValid()) { d->hintAtSelectionRow = indexToAnchor.row(); d->hintAtSelectionIndex = nextIndexHint(indexToAnchor, QItemSelectionRange(indexToAnchor)); } } // some precautions to keep current scroll position d->hintAtScrollPosition = d->scrollPositionHint(); } QModelIndex ItemViewCategorized::nextIndexHint(const QModelIndex& indexToAnchor, const QItemSelectionRange& removed) const { Q_UNUSED(indexToAnchor); if (removed.bottomRight().row() == model()->rowCount() - 1) { if (removed.topLeft().row() == 0) { return QModelIndex(); } return model()->index(removed.topLeft().row() - 1, 0); // last remaining, no next one left } else { return model()->index(removed.bottomRight().row() + 1, 0); // next remaining } } void ItemViewCategorized::layoutWasChanged() { // connected queued to layoutChanged() ensureSelectionAfterChanges(); if (d->hintAtScrollPosition.isValid()) { scrollToRelaxed(d->hintAtScrollPosition); d->hintAtScrollPosition = QModelIndex(); } else { scrollToRelaxed(currentIndex()); } } void ItemViewCategorized::userInteraction() { // as soon as the user did anything affecting selection, we don't interfere anymore d->ensureInitialSelectedItem = false; d->hintAtSelectionIndex = QModelIndex(); } void ItemViewCategorized::ensureSelectionAfterChanges() { if (d->ensureInitialSelectedItem && model()->rowCount()) { // Ensure the item (0,0) is selected, if the model was reset previously // and the user did not change the selection since reset. // Caveat: Item at (0,0) may have changed. bool hadInitial = d->ensureInitialSelectedItem; d->ensureInitialSelectedItem = false; d->ensureOneSelectedItem = false; QModelIndex index = model()->index(0,0); if (index.isValid()) { selectionModel()->select(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear); setCurrentIndex(index); // we want ensureInitial set to false if and only if the selection // is done from any other place than the previous line (i.e., by user action) // Effect: we select whatever is the current index(0,0) if (hadInitial) { d->ensureInitialSelectedItem = true; } } } else if (d->ensureOneSelectedItem) { // ensure we have a selection if there was one before d->ensureOneSelectedItem = false; if (model()->rowCount() && selectionModel()->selection().isEmpty()) { QModelIndex index; if (d->hintAtSelectionIndex.isValid()) { index = d->hintAtSelectionIndex; } else if (d->hintAtSelectionRow != -1) { index = model()->index(qMin(model()->rowCount(), d->hintAtSelectionRow), 0); } else { index = currentIndex(); } if (!index.isValid()) { index = model()->index(0, 0); } d->hintAtSelectionRow = -1; d->hintAtSelectionIndex = QModelIndex(); if (index.isValid()) { setCurrentIndex(index); selectionModel()->select(index, QItemSelectionModel::SelectCurrent); } } } } QModelIndex ItemViewCategorized::indexForCategoryAt(const QPoint& pos) const { return categoryAt(pos); } QModelIndex ItemViewCategorized::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) { QModelIndex current = currentIndex(); if (!current.isValid()) { return DCategorizedView::moveCursor(cursorAction, modifiers); } // We want a simple wrapping navigation. // Default behavior we do not want: right/left does never change row; Next/Previous is equivalent to Down/Up switch (cursorAction) { case MoveNext: case MoveRight: { QModelIndex next = model()->index(current.row() + 1, 0); if (next.isValid()) { return next; } else { return current; } break; } case MovePrevious: case MoveLeft: { QModelIndex previous = model()->index(current.row() - 1, 0); if (previous.isValid()) { return previous; } else { return current; } break; } default: break; } return DCategorizedView::moveCursor(cursorAction, modifiers); } void ItemViewCategorized::showContextMenuOnIndex(QContextMenuEvent*, const QModelIndex&) { // implemented in subclass } void ItemViewCategorized::showContextMenu(QContextMenuEvent*) { // implemented in subclass } -void ItemViewCategorized::indexActivated(const QModelIndex&, QMouseEvent*) +void ItemViewCategorized::indexActivated(const QModelIndex&, Qt::KeyboardModifiers) { } bool ItemViewCategorized::showToolTip(const QModelIndex& index, QStyleOptionViewItem& option, QHelpEvent* he) { QRect innerRect; QPoint pos; if (he) { pos = he->pos(); } else { pos = option.rect.center(); } if (d->delegate->acceptsToolTip(he->pos(), option.rect, index, &innerRect)) { if (!innerRect.isNull()) { option.rect = innerRect; } d->toolTip->show(option, index); return true; } return false; } void ItemViewCategorized::contextMenuEvent(QContextMenuEvent* event) { userInteraction(); QModelIndex index = indexAt(event->pos()); if (index.isValid()) { showContextMenuOnIndex(event, index); } else { showContextMenu(event); } } void ItemViewCategorized::leaveEvent(QEvent*) { hideIndexNotification(); if (d->mouseButtonPressed != Qt::RightButton) { d->mouseButtonPressed = Qt::NoButton; } } void ItemViewCategorized::mousePressEvent(QMouseEvent* event) { userInteraction(); - // store event for entered(), clicked(), activated() signal handlers - d->currentMouseEvent = event; - const QModelIndex index = indexAt(event->pos()); // Clear selection on click on empty area. Standard behavior, but not done by QAbstractItemView for some reason. Qt::KeyboardModifiers modifiers = event->modifiers(); const Qt::MouseButton button = event->button(); const bool rightButtonPressed = button & Qt::RightButton; const bool shiftKeyPressed = modifiers & Qt::ShiftModifier; const bool controlKeyPressed = modifiers & Qt::ControlModifier; d->mouseButtonPressed = button; if (!index.isValid() && !rightButtonPressed && !shiftKeyPressed && !controlKeyPressed) { clearSelection(); } + // store event for entered(), clicked(), activated() signal handlers + if (!rightButtonPressed) + { + d->currentMouseEvent = event; + } + else + { + d->currentMouseEvent = 0; + } + DCategorizedView::mousePressEvent(event); if (!index.isValid()) { emit viewportClicked(event); } } void ItemViewCategorized::mouseReleaseEvent(QMouseEvent* event) { userInteraction(); if (d->scrollCurrentToCenter) { scrollTo(currentIndex(), QAbstractItemView::PositionAtCenter); } DCategorizedView::mouseReleaseEvent(event); } void ItemViewCategorized::mouseMoveEvent(QMouseEvent* event) { QModelIndex index = indexAt(event->pos()); QRect indexVisualRect; if (index.isValid()) { indexVisualRect = visualRect(index); if (d->usePointingHand && d->delegate->acceptsActivation(event->pos(), indexVisualRect, index)) { setCursor(Qt::PointingHandCursor); } else { unsetCursor(); } } else { unsetCursor(); } if (d->notificationToolTip && d->notificationToolTip->isVisible()) { if (!d->notificationToolTip->rect().adjusted(-50, -50, 50, 50).contains(event->pos())) { hideIndexNotification(); } } DCategorizedView::mouseMoveEvent(event); d->delegate->mouseMoved(event, indexVisualRect, index); } void ItemViewCategorized::wheelEvent(QWheelEvent* event) { // DCategorizedView updates the single step at some occasions in a private methody horizontalScrollBar()->setSingleStep(d->delegate->gridSize().height() / d->scrollStepFactor); verticalScrollBar()->setSingleStep(d->delegate->gridSize().width() / d->scrollStepFactor); if (event->modifiers() & Qt::ControlModifier) { const int delta = event->delta(); if (delta > 0) { emit zoomInStep(); } else if (delta < 0) { emit zoomOutStep(); } event->accept(); return; } if (verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff && event->orientation() == Qt::Vertical) { QWheelEvent n(event->pos(), event->globalPos(), event->delta(), event->buttons(), event->modifiers(), Qt::Horizontal); QApplication::sendEvent(horizontalScrollBar(), &n); event->setAccepted(n.isAccepted()); } else { DCategorizedView::wheelEvent(event); } } void ItemViewCategorized::keyPressEvent(QKeyEvent* event) { userInteraction(); if (event == QKeySequence::Copy) { copy(); event->accept(); return; } else if (event == QKeySequence::Paste) { paste(); event->accept(); return; } /* // from dolphincontroller.cpp const QItemSelectionModel* selModel = m_itemView->selectionModel(); const QModelIndex currentIndex = selModel->currentIndex(); const bool trigger = currentIndex.isValid() && ((event->key() == Qt::Key_Return) || (event->key() == Qt::Key_Enter)) && (selModel->selectedIndexes().count() > 0); if (trigger) { const QModelIndexList indexList = selModel->selectedIndexes(); foreach (const QModelIndex& index, indexList) { emit itemTriggered(itemForIndex(index)); } } */ DCategorizedView::keyPressEvent(event); emit keyPressed(event); } void ItemViewCategorized::resizeEvent(QResizeEvent* e) { QModelIndex oldPosition = d->scrollPositionHint(); DCategorizedView::resizeEvent(e); updateDelegateSizes(); scrollToRelaxed(oldPosition, QAbstractItemView::PositionAtTop); } bool ItemViewCategorized::viewportEvent(QEvent* event) { switch (event->type()) { case QEvent::FontChange: { updateDelegateSizes(); break; } case QEvent::ToolTip: { if (!d->showToolTip) { return true; } QHelpEvent* he = static_cast(event); const QModelIndex index = indexAt(he->pos()); if (!index.isValid()) { break; } QStyleOptionViewItem option = viewOptions(); option.rect = visualRect(index); option.state |= (index == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); showToolTip(index, option, he); return true; } default: break; } return DCategorizedView::viewportEvent(event); } void ItemViewCategorized::showIndexNotification(const QModelIndex& index, const QString& message) { hideIndexNotification(); if (!index.isValid()) { return; } if (!d->notificationToolTip) { d->notificationToolTip = new ItemViewToolTip(this); } d->notificationToolTip->setTipContents(message); QStyleOptionViewItem option = viewOptions(); option.rect = visualRect(index); option.state |= (index == currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); d->notificationToolTip->show(option, index); } void ItemViewCategorized::hideIndexNotification() { if (d->notificationToolTip) { d->notificationToolTip->hide(); } } /** * cut(), copy(), paste(), dragEnterEvent(), dragMoveEvent(), dropEvent(), startDrag() * are implemented by DragDropViewImplementation */ QModelIndex ItemViewCategorized::mapIndexForDragDrop(const QModelIndex& index) const { return filterModel()->mapToSource(index); } QPixmap ItemViewCategorized::pixmapForDrag(const QList& indexes) const { QStyleOptionViewItem option = viewOptions(); option.rect = viewport()->rect(); return d->delegate->pixmapForDrag(option, indexes); } void ItemViewCategorized::setScrollCurrentToCenter(bool enabled) { d->scrollCurrentToCenter = enabled; } void ItemViewCategorized::scrollTo(const QModelIndex& index, ScrollHint hint) { if (d->scrollCurrentToCenter && d->mouseButtonPressed == Qt::NoButton) { hint = QAbstractItemView::PositionAtCenter; } d->mouseButtonPressed = Qt::NoButton; DCategorizedView::scrollTo(index, hint); } } // namespace Digikam diff --git a/core/libs/widgets/itemview/itemviewcategorized.h b/core/libs/widgets/itemview/itemviewcategorized.h index dd5c872771..04d8781fe6 100644 --- a/core/libs/widgets/itemview/itemviewcategorized.h +++ b/core/libs/widgets/itemview/itemviewcategorized.h @@ -1,204 +1,204 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2010-01-16 * Description : Qt item view for images * * Copyright (C) 2009-2012 by Marcel Wiesweg * Copyright (C) 2011-2018 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_ITEM_VIEW_CATEGORIZED_H #define DIGIKAM_ITEM_VIEW_CATEGORIZED_H // Local includes #include "digikam_export.h" #include "dcategorizedview.h" #include "dragdropimplementations.h" class QSortFilterProxyModel; namespace Digikam { class DItemDelegate; class ItemViewToolTip; class DIGIKAM_EXPORT ItemViewCategorized : public DCategorizedView, public DragDropViewImplementation { Q_OBJECT public: explicit ItemViewCategorized(QWidget* const parent = 0); ~ItemViewCategorized(); DItemDelegate* delegate() const; int numberOfSelectedIndexes() const; /** Selects the index as current and scrolls to it */ void toFirstIndex(); void toLastIndex(); void toNextIndex(); void toPreviousIndex(); void toIndex(const QModelIndex& index); void awayFromSelection(); /** Scroll automatically the current index to center of the view. */ void setScrollCurrentToCenter(bool enabled); /** Like scrollTo, but only scrolls if the index is not visible, regardless of hint. */ void scrollToRelaxed(const QModelIndex& index, ScrollHint hint = EnsureVisible); void invertSelection(); void setSelectedIndexes(const QList& indexes); void setToolTipEnabled(bool enabled); bool isToolTipEnabled() const; /** Sets the spacing. Does not use setSpacing()/spacing() from QListView */ void setSpacing(int spacing); /** Set if the PointingHand Cursor should be shown over the activation area */ void setUsePointingHandCursor(bool useCursor); /** Determine a step size for scrolling: The larger this number, * the smaller and more precise is the scrolling. Default is 10. */ void setScrollStepGranularity(int factor); virtual QSortFilterProxyModel* filterModel() const = 0; virtual void scrollTo(const QModelIndex& index, ScrollHint hint = EnsureVisible); public Q_SLOTS: void showIndexNotification(const QModelIndex& index, const QString& message); void hideIndexNotification(); virtual void cut() { DragDropViewImplementation::cut(); } virtual void copy() { DragDropViewImplementation::copy(); } virtual void paste() { DragDropViewImplementation::paste(); } Q_SIGNALS: /// Emitted when any selection change occurs. Any of the signals below will be emitted before. void selectionChanged(); /// Emitted when the selection is completely cleared. void selectionCleared(); void zoomOutStep(); void zoomInStep(); /** For overlays: Like the respective parent class signals, but with additional info. * Do not change the mouse events. */ void clicked(const QMouseEvent* e, const QModelIndex& index); void entered(const QMouseEvent* e, const QModelIndex& index); /// While clicked() is emitted with a valid index, this corresponds to clicking on empty space void viewportClicked(const QMouseEvent* e); /** Remember you may want to check if the event is accepted or ignored. * This signal is emitted after being handled by this widget. * You can accept it if ignored. */ void keyPressed(QKeyEvent* e); protected Q_SLOTS: void slotActivated(const QModelIndex& index); void slotClicked(const QModelIndex& index); void slotEntered(const QModelIndex& index); void layoutAboutToBeChanged(); void layoutWasChanged(); virtual void slotThemeChanged(); virtual void slotSetupChanged(); protected: void encodeIsCutSelection(QMimeData* mime, bool isCutSelection); bool decodeIsCutSelection(const QMimeData* mimeData); void setToolTip(ItemViewToolTip* tip); void setItemDelegate(DItemDelegate* delegate); void updateDelegateSizes(); void userInteraction(); /** Returns an index that is representative for the category at position pos */ QModelIndex indexForCategoryAt(const QPoint& pos) const; // reimplemented from parent class void contextMenuEvent(QContextMenuEvent* event); void keyPressEvent(QKeyEvent* event); void leaveEvent(QEvent* event); void mouseMoveEvent(QMouseEvent* event); void mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); void resizeEvent(QResizeEvent* e); void reset(); void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end); void rowsInserted(const QModelIndex& parent, int start, int end); void rowsRemoved(const QModelIndex& parent, int start, int end); void selectionChanged(const QItemSelection&, const QItemSelection&); bool viewportEvent(QEvent* event); void wheelEvent(QWheelEvent* event); QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers); /// Reimplement these in a subclass virtual void showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index); virtual void showContextMenu(QContextMenuEvent* event); - virtual void indexActivated(const QModelIndex& index, QMouseEvent* const event); + virtual void indexActivated(const QModelIndex& index, Qt::KeyboardModifiers modifiers); /** Provides default behavior, can reimplement in a subclass. * Returns true if a tooltip was shown. * The help event is optional. */ virtual bool showToolTip(const QModelIndex& index, QStyleOptionViewItem& option, QHelpEvent* e = 0); DECLARE_VIEW_DRAG_DROP_METHODS(DCategorizedView) /// Note: pure virtual dragDropHandler() still open from DragDropViewImplementation virtual QModelIndex mapIndexForDragDrop(const QModelIndex& index) const; virtual QPixmap pixmapForDrag(const QList& indexes) const; /** * Assuming the given indexes would be removed (hypothetically!), * return the index to be selected instead, starting from anchor. * The default implementation returns the next remaining sibling. */ virtual QModelIndex nextIndexHint(const QModelIndex& indexToAnchor, const QItemSelectionRange& removed) const; private Q_SLOTS: void slotGridSizeChanged(const QSize&); private: void ensureSelectionAfterChanges(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_ITEM_VIEW_CATEGORIZED_H diff --git a/core/showfoto/thumbbar/showfotocategorizedview.cpp b/core/showfoto/thumbbar/showfotocategorizedview.cpp index 05c4bcebfc..b63dcefb74 100644 --- a/core/showfoto/thumbbar/showfotocategorizedview.cpp +++ b/core/showfoto/thumbbar/showfotocategorizedview.cpp @@ -1,580 +1,580 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2012-07-13 * Description : Qt categorized item view for showfoto items * * Copyright (C) 2013 by Mohamed_Anwer * * 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 "showfotocategorizedview.h" // Qt includes #include // Local include #include "digikam_debug.h" #include "loadingcacheinterface.h" #include "itemviewtooltip.h" #include "showfotodelegate.h" #include "showfototooltipfiller.h" using namespace Digikam; namespace ShowFoto { class Q_DECL_HIDDEN ShowfotoItemViewToolTip : public ItemViewToolTip { public: explicit ShowfotoItemViewToolTip(ShowfotoCategorizedView* const view) : ItemViewToolTip(view) { } ShowfotoCategorizedView* view() const { return static_cast(ItemViewToolTip::view()); } protected: virtual QString tipContents() { ShowfotoItemInfo info = ShowfotoItemModel::retrieveShowfotoItemInfo(currentIndex()); return ShowfotoToolTipFiller::ShowfotoItemInfoTipContents(info); } }; class Q_DECL_HIDDEN ShowfotoCategorizedView::Private { public: explicit Private() : model(0), filterModel(0), delegate(0), showToolTip(false), scrollToItemId(0), delayedEnterTimer(0), currentMouseEvent(0) { } ShowfotoItemModel* model; ShowfotoSortFilterModel* filterModel; ShowfotoDelegate* delegate; bool showToolTip; qlonglong scrollToItemId; QTimer* delayedEnterTimer; QMouseEvent* currentMouseEvent; }; ShowfotoCategorizedView::ShowfotoCategorizedView(QWidget* const parent) : ItemViewCategorized(parent), d(new Private) { setToolTip(new ShowfotoItemViewToolTip(this)); LoadingCacheInterface::connectToSignalFileChanged(this, SLOT(slotFileChanged(QString))); d->delayedEnterTimer = new QTimer(this); d->delayedEnterTimer->setInterval(10); d->delayedEnterTimer->setSingleShot(false); connect(d->delayedEnterTimer, &QTimer::timeout, this, &ShowfotoCategorizedView::slotDelayedEnter); } ShowfotoCategorizedView::~ShowfotoCategorizedView() { d->delegate->removeAllOverlays(); delete d; } void ShowfotoCategorizedView::setModels(ShowfotoItemModel* model, ShowfotoSortFilterModel* filterModel) { if (d->delegate) { d->delegate->setAllOverlaysActive(false); } if (d->filterModel) { disconnect(d->filterModel, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged())); disconnect(d->filterModel, SIGNAL(layoutChanged()), this, SLOT(layoutWasChanged())); } d->model = model; d->filterModel = filterModel; setModel(d->filterModel); connect(d->filterModel, &ShowfotoSortFilterModel::layoutAboutToBeChanged, this, &ShowfotoCategorizedView::layoutAboutToBeChanged); connect(d->filterModel, &ShowfotoSortFilterModel::layoutChanged, this, &ShowfotoCategorizedView::layoutWasChanged, Qt::QueuedConnection); emit modelChanged(); if (d->delegate) { d->delegate->setAllOverlaysActive(true); } } ShowfotoItemModel* ShowfotoCategorizedView::showfotoItemModel() const { return d->model; } ShowfotoSortFilterModel* ShowfotoCategorizedView::showfotoSortFilterModel() const { return d->filterModel; } ShowfotoFilterModel* ShowfotoCategorizedView::showfotoFilterModel() const { return d->filterModel->showfotoFilterModel(); } ShowfotoThumbnailModel* ShowfotoCategorizedView::showfotoThumbnailModel() const { return qobject_cast(d->model); } QSortFilterProxyModel* ShowfotoCategorizedView::filterModel() const { return d->filterModel; } ShowfotoDelegate* ShowfotoCategorizedView::delegate() const { return d->delegate; } void ShowfotoCategorizedView::setItemDelegate(ShowfotoDelegate* delegate) { ThumbnailSize oldSize = thumbnailSize(); ShowfotoDelegate* const oldDelegate = d->delegate; if (oldDelegate) { hideIndexNotification(); d->delegate->setAllOverlaysActive(false); d->delegate->setViewOnAllOverlays(0); // Note: Be precise, no wildcard disconnect! disconnect(d->delegate, SIGNAL(requestNotification(QModelIndex,QString)), this, SLOT(showIndexNotification(QModelIndex,QString))); disconnect(d->delegate, SIGNAL(hideNotification()), this, SLOT(hideIndexNotification())); } d->delegate = delegate; delegate->setThumbnailSize(oldSize); if (oldDelegate) { d->delegate->setSpacing(oldDelegate->spacing()); } ItemViewCategorized::setItemDelegate(d->delegate); updateDelegateSizes(); d->delegate->setViewOnAllOverlays(this); d->delegate->setAllOverlaysActive(true); connect(d->delegate, &ShowfotoDelegate::requestNotification, this, &ShowfotoCategorizedView::showIndexNotification); connect(d->delegate, &ShowfotoDelegate::hideNotification, this, &ShowfotoCategorizedView::hideIndexNotification); } ShowfotoItemInfo ShowfotoCategorizedView::currentInfo() const { return d->filterModel->showfotoItemInfo(currentIndex()); } QUrl ShowfotoCategorizedView::currentUrl() const { return currentInfo().url; } QList ShowfotoCategorizedView::selectedShowfotoItemInfos() const { return d->filterModel->showfotoItemInfos(selectedIndexes()); } QList ShowfotoCategorizedView::selectedShowfotoItemInfosCurrentFirst() const { QList indexes = selectedIndexes(); QModelIndex current = currentIndex(); QList infos; foreach (const QModelIndex& index, indexes) { ShowfotoItemInfo info = d->filterModel->showfotoItemInfo(index); if (index == current) { infos.prepend(info); } else { infos.append(info); } } return infos; } QList ShowfotoCategorizedView::showfotoItemInfos() const { return d->filterModel->showfotoItemInfosSorted(); } QList ShowfotoCategorizedView::urls() const { QList infos = showfotoItemInfos(); QList urls; foreach (const ShowfotoItemInfo& info, infos) { urls << info.url; } return urls; } QList ShowfotoCategorizedView::selectedUrls() const { QList infos = selectedShowfotoItemInfos(); QList urls; foreach (const ShowfotoItemInfo& info, infos) { urls << info.url; } return urls; } void ShowfotoCategorizedView::toIndex(const QUrl& url) { ItemViewCategorized::toIndex(d->filterModel->indexForUrl(url)); } ShowfotoItemInfo ShowfotoCategorizedView::nextInOrder(const ShowfotoItemInfo& startingPoint, int nth) { QModelIndex index = d->filterModel->indexForShowfotoItemInfo(startingPoint); if (!index.isValid()) { return ShowfotoItemInfo(); } return d->filterModel->showfotoItemInfo(d->filterModel->index(index.row() + nth, 0, QModelIndex())); } QModelIndex ShowfotoCategorizedView::nextIndexHint(const QModelIndex& anchor, const QItemSelectionRange& removed) const { QModelIndex hint = ItemViewCategorized::nextIndexHint(anchor, removed); ShowfotoItemInfo info = d->filterModel->showfotoItemInfo(anchor); //qCDebug(DIGIKAM_SHOWFOTO_LOG) << "Having initial hint" << hint << "for" << anchor << d->model->numberOfIndexesForShowfotoItemInfo(info); // Fixes a special case of multiple (face) entries for the same image. // If one is removed, any entry of the same image shall be preferred. if (d->model->indexesForShowfotoItemInfo(info).size() > 1) { // The hint is for a different info, but we may have a hint for the same info if (info != d->filterModel->showfotoItemInfo(hint)) { int minDiff = d->filterModel->rowCount(); QList indexesForShowfotoItemInfo = d->filterModel->mapListFromSource(d->model->indexesForShowfotoItemInfo(info)); foreach (const QModelIndex& index, indexesForShowfotoItemInfo) { if (index == anchor || !index.isValid() || removed.contains(index)) { continue; } int distance = qAbs(index.row() - anchor.row()); if (distance < minDiff) { minDiff = distance; hint = index; //qCDebug(DIGIKAM_SHOWFOTO_LOG) << "Chose index" << hint << "at distance" << minDiff << "to" << anchor; } } } } return hint; } ThumbnailSize ShowfotoCategorizedView::thumbnailSize() const { /* ShowfotoThumbnailModel *thumbModel = ShowfotoThumbnailModel(); if (thumbModel) return thumbModel->thumbnailSize(); */ if (d->delegate) { return d->delegate->thumbnailSize(); } return ThumbnailSize(); } void ShowfotoCategorizedView::setThumbnailSize(int size) { setThumbnailSize(ThumbnailSize(size)); } void ShowfotoCategorizedView::setThumbnailSize(const ThumbnailSize& s) { // we abuse this pair of method calls to restore scroll position layoutAboutToBeChanged(); d->delegate->setThumbnailSize(s); layoutWasChanged(); } void ShowfotoCategorizedView::setCurrentWhenAvailable(qlonglong showfotoItemId) { d->scrollToItemId = showfotoItemId; } void ShowfotoCategorizedView::setCurrentUrl(const QUrl& url) { if (url.isEmpty()) { clearSelection(); setCurrentIndex(QModelIndex()); return; } QModelIndex index = d->filterModel->indexForUrl(url); if (!index.isValid()) { return; } clearSelection(); setCurrentIndex(index); } void ShowfotoCategorizedView::setCurrentInfo(const ShowfotoItemInfo& info) { QModelIndex index = d->filterModel->indexForShowfotoItemInfo(info); clearSelection(); setCurrentIndex(index); } void ShowfotoCategorizedView::setSelectedUrls(const QList& urlList) { QItemSelection mySelection; for (QList::const_iterator it = urlList.constBegin() ; it!=urlList.constEnd() ; ++it) { const QModelIndex index = d->filterModel->indexForUrl(*it); if (!index.isValid()) { qCWarning(DIGIKAM_GENERAL_LOG) << "no QModelIndex found for" << *it; } else { // TODO: is there a better way? mySelection.select(index, index); } } clearSelection(); selectionModel()->select(mySelection, QItemSelectionModel::Select); } void ShowfotoCategorizedView::setSelectedShowfotoItemInfos(const QList& infos) { QItemSelection mySelection; foreach (const ShowfotoItemInfo& info, infos) { QModelIndex index = d->filterModel->indexForShowfotoItemInfo(info); mySelection.select(index, index); } selectionModel()->select(mySelection, QItemSelectionModel::ClearAndSelect); } void ShowfotoCategorizedView::hintAt(const ShowfotoItemInfo& info) { if (info.isNull()) { return; } QModelIndex index = d->filterModel->indexForShowfotoItemInfo(info); if (!index.isValid()) { return; } selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); scrollTo(index); } void ShowfotoCategorizedView::addOverlay(ItemDelegateOverlay* overlay, ShowfotoDelegate* delegate) { if (!delegate) { delegate = d->delegate; } delegate->installOverlay(overlay); if (delegate == d->delegate) { overlay->setView(this); overlay->setActive(true); } } void ShowfotoCategorizedView::removeOverlay(ItemDelegateOverlay* overlay) { ShowfotoDelegate* const delegate = dynamic_cast(overlay->delegate()); if (delegate) { delegate->removeOverlay(overlay); } overlay->setView(0); } void ShowfotoCategorizedView::updateGeometries() { ItemViewCategorized::updateGeometries(); d->delayedEnterTimer->start(); } void ShowfotoCategorizedView::slotDelayedEnter() { // re-emit entered() for index under mouse (after layout). QModelIndex mouseIndex = indexAt(mapFromGlobal(QCursor::pos())); if (mouseIndex.isValid()) { emit DCategorizedView::entered(mouseIndex); } } void ShowfotoCategorizedView::slotFileChanged(const QString& filePath) { QModelIndex index = d->filterModel->indexForUrl(QUrl::fromLocalFile(filePath)); if (index.isValid()) { update(index); } } -void ShowfotoCategorizedView::indexActivated(const QModelIndex& index, QMouseEvent* const event) +void ShowfotoCategorizedView::indexActivated(const QModelIndex& index, Qt::KeyboardModifiers modifiers) { ShowfotoItemInfo info = d->filterModel->showfotoItemInfo(index); if (!info.isNull()) { - activated(info, event); + activated(info, modifiers); emit showfotoItemInfoActivated(info); } } void ShowfotoCategorizedView::currentChanged(const QModelIndex& index, const QModelIndex& previous) { ItemViewCategorized::currentChanged(index, previous); emit currentChanged(d->filterModel->showfotoItemInfo(index)); } void ShowfotoCategorizedView::selectionChanged(const QItemSelection& selectedItems, const QItemSelection& deselectedItems) { ItemViewCategorized::selectionChanged(selectedItems, deselectedItems); if (!selectedItems.isEmpty()) { emit selected(d->filterModel->showfotoItemInfos(selectedItems.indexes())); } if (!deselectedItems.isEmpty()) { emit deselected(d->filterModel->showfotoItemInfos(deselectedItems.indexes())); } } -void ShowfotoCategorizedView::activated(const ShowfotoItemInfo&, QMouseEvent*) +void ShowfotoCategorizedView::activated(const ShowfotoItemInfo&, Qt::KeyboardModifiers) { // implemented in subclass } void ShowfotoCategorizedView::showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index) { ShowfotoItemInfo info = d->filterModel->showfotoItemInfo(index); showContextMenuOnInfo(event, info); } void ShowfotoCategorizedView::showContextMenuOnInfo(QContextMenuEvent*, const ShowfotoItemInfo&) { // implemented in subclass } void ShowfotoCategorizedView::paintEvent(QPaintEvent* e) { ItemViewCategorized::paintEvent(e); } QItemSelectionModel* ShowfotoCategorizedView::getSelectionModel() const { return selectionModel(); } AbstractItemDragDropHandler* ShowfotoCategorizedView::dragDropHandler() const { return d->model->dragDropHandler(); } } // namespace Showfoto diff --git a/core/showfoto/thumbbar/showfotocategorizedview.h b/core/showfoto/thumbbar/showfotocategorizedview.h index 887440914b..9a93f822a8 100644 --- a/core/showfoto/thumbbar/showfotocategorizedview.h +++ b/core/showfoto/thumbbar/showfotocategorizedview.h @@ -1,193 +1,193 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2012-07-13 * Description : Qt categorized item view for showfoto items * * Copyright (C) 2013 by Mohamed_Anwer * * 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 SHOW_FOTO_CATEGORIZED_VIEW_H #define SHOW_FOTO_CATEGORIZED_VIEW_H // Local includes #include "itemviewcategorized.h" #include "showfotoimagemodel.h" #include "showfotofiltermodel.h" #include "showfotothumbnailmodel.h" #include "showfotoiteminfo.h" #include "itemdelegateoverlay.h" namespace ShowFoto { class ShowfotoDelegate; class ShowfotoCategorizedView : public ItemViewCategorized { public: Q_OBJECT public: explicit ShowfotoCategorizedView(QWidget* const parent = 0); ~ShowfotoCategorizedView(); void setModels(ShowfotoItemModel* model, ShowfotoSortFilterModel* filterModel); ShowfotoItemModel* showfotoItemModel() const; ShowfotoSortFilterModel* showfotoSortFilterModel() const; QItemSelectionModel* getSelectionModel() const; /// Returns any ShowfotoFilterModel in chain. May not be sourceModel() ShowfotoFilterModel* showfotoFilterModel() const; /// Returns 0 if the ShowfotoItemModel is not an ShowfotoThumbnailModel ShowfotoThumbnailModel* showfotoThumbnailModel() const; ShowfotoDelegate* delegate() const; ShowfotoItemInfo currentInfo() const; QUrl currentUrl() const; QList selectedShowfotoItemInfos() const; QList selectedShowfotoItemInfosCurrentFirst() const; QList selectedUrls() const; QList showfotoItemInfos() const; QList urls() const; /** Selects the index as current and scrolls to it */ void toIndex(const QUrl& url); /** Returns the n-th info after the given one. * Specifically, return the previous info for nth = -1 * and the next info for n = 1. * Returns a null info if either startingPoint or the nth info are * not contained in the model */ ShowfotoItemInfo nextInOrder(const ShowfotoItemInfo& startingPoint, int nth); ShowfotoItemInfo previousInfo(const ShowfotoItemInfo& info) { return nextInOrder(info, -1); } ShowfotoItemInfo nextInfo(const ShowfotoItemInfo& info) { return nextInOrder(info, 1); } /// Add and remove an overlay. It will as well be removed automatically when destroyed. /// Unless you pass a different delegate, the current delegate will be used. void addOverlay(ItemDelegateOverlay* overlay, ShowfotoDelegate* delegate = 0); void removeOverlay(ItemDelegateOverlay* overlay); //TODO: Implement This // void addSelectionOverlay(ShowfotoDelegate* delegate = 0); ThumbnailSize thumbnailSize() const; virtual void setThumbnailSize(const ThumbnailSize& size); public Q_SLOTS: void setThumbnailSize(int size); /** Scroll the view to the given item when it becomes available */ void setCurrentWhenAvailable(qlonglong ShowfotoItemId); /** Set as current item the item identified by its file url */ void setCurrentUrl(const QUrl& url); /** Set as current item the item identified by the ShowfotoItemInfo */ void setCurrentInfo(const ShowfotoItemInfo& info); /** Set selected items identified by their file urls */ void setSelectedUrls(const QList& urlList); /** Set selected items */ void setSelectedShowfotoItemInfos(const QList& infos); /** Does something to gain attention for info, but not changing current selection */ void hintAt(const ShowfotoItemInfo& info); Q_SIGNALS: void currentChanged(const ShowfotoItemInfo& info); /// Emitted when new items are selected. The parameter includes only the newly selected infos, /// there may be other already selected infos. void selected(const QList& newSelectedInfos); /// Emitted when items are deselected. There may be other selected infos left. /// This signal is not emitted when the model is reset; then only selectionCleared is emitted. void deselected(const QList& nowDeselectedInfos); /// Emitted when the given ShowfotoItemInfo is activated. Info is never null. void showfotoItemInfoActivated(const ShowfotoItemInfo& info); /// Emitted when a new model is set void modelChanged(); protected: // reimplemented from parent class QSortFilterProxyModel* filterModel() const; AbstractItemDragDropHandler* dragDropHandler() const; QModelIndex nextIndexHint(const QModelIndex& indexToAnchor, const QItemSelectionRange& removed) const; void setItemDelegate(ShowfotoDelegate* delegate); - void indexActivated(const QModelIndex& index, QMouseEvent* const event); + void indexActivated(const QModelIndex& index, Qt::KeyboardModifiers modifiers); void currentChanged(const QModelIndex& index, const QModelIndex& previous); void paintEvent(QPaintEvent* e); void selectionChanged(const QItemSelection&, const QItemSelection&); void updateGeometries(); /// Reimplement these in a subclass - virtual void activated(const ShowfotoItemInfo& info, QMouseEvent* const event); + virtual void activated(const ShowfotoItemInfo& info, Qt::KeyboardModifiers modifiers); virtual void showContextMenuOnInfo(QContextMenuEvent* event, const ShowfotoItemInfo& info); virtual void showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index); private Q_SLOTS: void slotFileChanged(const QString& filePath); void slotDelayedEnter(); private: void scrollToStoredItem(); private: class Private; Private* const d; }; } // namespace Showfoto #endif // SHOW_FOTO_CATEGORIZED_VIEW_H diff --git a/core/utilities/import/views/importcategorizedview.cpp b/core/utilities/import/views/importcategorizedview.cpp index 49ff0c2607..b9592bb7c6 100644 --- a/core/utilities/import/views/importcategorizedview.cpp +++ b/core/utilities/import/views/importcategorizedview.cpp @@ -1,631 +1,631 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2012-07-13 * Description : Qt categorized item view for camera items * * Copyright (C) 2012 by Islam Wazery * * 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 "importcategorizedview.h" // Qt includes #include // Local includes #include "digikam_debug.h" #include "camitemsortsettings.h" #include "iccsettings.h" #include "itemselectionoverlay.h" #include "importdelegate.h" #include "importtooltipfiller.h" #include "importsettings.h" #include "itemviewtooltip.h" #include "loadingcacheinterface.h" #include "thumbnailloadthread.h" namespace Digikam { class Q_DECL_HIDDEN ImportItemViewToolTip : public ItemViewToolTip { public: explicit ImportItemViewToolTip(ImportCategorizedView* const view) : ItemViewToolTip(view) { } ImportCategorizedView* view() const { return static_cast(ItemViewToolTip::view()); } protected: virtual QString tipContents() { CamItemInfo info = ImportItemModel::retrieveCamItemInfo(currentIndex()); return ImportToolTipFiller::CamItemInfoTipContents(info); } }; class Q_DECL_HIDDEN ImportCategorizedView::Private { public: explicit Private() : model(0), filterModel(0), delegate(0), showToolTip(false), scrollToItemId(0), delayedEnterTimer(0), currentMouseEvent(0) { } ImportItemModel* model; ImportSortFilterModel* filterModel; ImportDelegate* delegate; bool showToolTip; qlonglong scrollToItemId; QTimer* delayedEnterTimer; QMouseEvent* currentMouseEvent; }; ImportCategorizedView::ImportCategorizedView(QWidget* const parent) : ItemViewCategorized(parent), d(new Private) { setToolTip(new ImportItemViewToolTip(this)); LoadingCacheInterface::connectToSignalFileChanged(this, SLOT(slotFileChanged(QString))); d->delayedEnterTimer = new QTimer(this); d->delayedEnterTimer->setInterval(10); d->delayedEnterTimer->setSingleShot(true); connect(d->delayedEnterTimer, SIGNAL(timeout()), this, SLOT(slotDelayedEnter())); connect(IccSettings::instance(), SIGNAL(settingsChanged(ICCSettingsContainer,ICCSettingsContainer)), this, SLOT(slotIccSettingsChanged(ICCSettingsContainer,ICCSettingsContainer))); } ImportCategorizedView::~ImportCategorizedView() { d->delegate->removeAllOverlays(); delete d; } void ImportCategorizedView::setModels(ImportItemModel* model, ImportSortFilterModel* filterModel) { if (d->delegate) { d->delegate->setAllOverlaysActive(false); } if (d->filterModel) { disconnect(d->filterModel, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged())); disconnect(d->filterModel, SIGNAL(layoutChanged()), this, SLOT(layoutWasChanged())); } if (d->model) { disconnect(d->model, SIGNAL(itemInfosAdded(QList)), this, SLOT(slotCamItemInfosAdded())); } d->model = model; d->filterModel = filterModel; setModel(d->filterModel); connect(d->filterModel, SIGNAL(layoutAboutToBeChanged()), this, SLOT(layoutAboutToBeChanged())); connect(d->filterModel, SIGNAL(layoutChanged()), this, SLOT(layoutWasChanged()), Qt::QueuedConnection); connect(d->model, SIGNAL(itemInfosAdded(QList)), this, SLOT(slotCamItemInfosAdded())); emit modelChanged(); if (d->delegate) { d->delegate->setAllOverlaysActive(true); } } ImportItemModel* ImportCategorizedView::importItemModel() const { return d->model; } ImportSortFilterModel* ImportCategorizedView::importSortFilterModel() const { return d->filterModel; } ImportFilterModel* ImportCategorizedView::importFilterModel() const { return d->filterModel->importFilterModel(); } ImportThumbnailModel* ImportCategorizedView::importThumbnailModel() const { return qobject_cast(d->model); } QSortFilterProxyModel* ImportCategorizedView::filterModel() const { return d->filterModel; } ImportDelegate* ImportCategorizedView::delegate() const { return d->delegate; } void ImportCategorizedView::setItemDelegate(ImportDelegate* delegate) { ThumbnailSize oldSize = thumbnailSize(); ImportDelegate* oldDelegate = d->delegate; if (oldDelegate) { hideIndexNotification(); d->delegate->setAllOverlaysActive(false); d->delegate->setViewOnAllOverlays(0); // Note: Be precise, no wildcard disconnect! disconnect(d->delegate, SIGNAL(requestNotification(QModelIndex,QString)), this, SLOT(showIndexNotification(QModelIndex,QString))); disconnect(d->delegate, SIGNAL(hideNotification()), this, SLOT(hideIndexNotification())); } d->delegate = delegate; delegate->setThumbnailSize(oldSize); if (oldDelegate) { d->delegate->setSpacing(oldDelegate->spacing()); } ItemViewCategorized::setItemDelegate(d->delegate); setCategoryDrawer(d->delegate->categoryDrawer()); updateDelegateSizes(); d->delegate->setViewOnAllOverlays(this); d->delegate->setAllOverlaysActive(true); connect(d->delegate, SIGNAL(requestNotification(QModelIndex,QString)), this, SLOT(showIndexNotification(QModelIndex,QString))); connect(d->delegate, SIGNAL(hideNotification()), this, SLOT(hideIndexNotification())); } CamItemInfo ImportCategorizedView::currentInfo() const { return d->filterModel->camItemInfo(currentIndex()); } QUrl ImportCategorizedView::currentUrl() const { return currentInfo().url(); } QList ImportCategorizedView::selectedCamItemInfos() const { return d->filterModel->camItemInfos(selectedIndexes()); } QList ImportCategorizedView::selectedCamItemInfosCurrentFirst() const { QList indexes = selectedIndexes(); QModelIndex current = currentIndex(); QList infos; foreach(const QModelIndex& index, indexes) { CamItemInfo info = d->filterModel->camItemInfo(index); if (index == current) { infos.prepend(info); } else { infos.append(info); } } return infos; } QList ImportCategorizedView::camItemInfos() const { return d->filterModel->camItemInfosSorted(); } QList ImportCategorizedView::urls() const { QList infos = camItemInfos(); QList urls; foreach(const CamItemInfo& info, infos) { urls << info.url(); } return urls; } QList ImportCategorizedView::selectedUrls() const { QList infos = selectedCamItemInfos(); QList urls; foreach(const CamItemInfo& info, infos) { urls << info.url(); } return urls; } void ImportCategorizedView::toIndex(const QUrl& url) { ItemViewCategorized::toIndex(d->filterModel->indexForPath(url.toLocalFile())); } CamItemInfo ImportCategorizedView::nextInOrder(const CamItemInfo& startingPoint, int nth) { QModelIndex index = d->filterModel->indexForCamItemInfo(startingPoint); if (!index.isValid()) { return CamItemInfo(); } return d->filterModel->camItemInfo(d->filterModel->index(index.row() + nth, 0, QModelIndex())); } QModelIndex ImportCategorizedView::nextIndexHint(const QModelIndex& anchor, const QItemSelectionRange& removed) const { QModelIndex hint = ItemViewCategorized::nextIndexHint(anchor, removed); CamItemInfo info = d->filterModel->camItemInfo(anchor); //qCDebug(DIGIKAM_IMPORTUI_LOG) << "Having initial hint" << hint << "for" << anchor << d->model->numberOfIndexesForCamItemInfo(info); // Fixes a special case of multiple (face) entries for the same image. // If one is removed, any entry of the same image shall be preferred. if (d->model->numberOfIndexesForCamItemInfo(info) > 1) { // The hint is for a different info, but we may have a hint for the same info if (info != d->filterModel->camItemInfo(hint)) { int minDiff = d->filterModel->rowCount(); QList indexesForCamItemInfo = d->filterModel->mapListFromSource(d->model->indexesForCamItemInfo(info)); foreach(const QModelIndex& index, indexesForCamItemInfo) { if (index == anchor || !index.isValid() || removed.contains(index)) { continue; } int distance = qAbs(index.row() - anchor.row()); if (distance < minDiff) { minDiff = distance; hint = index; //qCDebug(DIGIKAM_IMPORTUI_LOG) << "Chose index" << hint << "at distance" << minDiff << "to" << anchor; } } } } return hint; } ThumbnailSize ImportCategorizedView::thumbnailSize() const { /* ImportThumbnailModel *thumbModel = importThumbnailModel(); if (thumbModel) return thumbModel->thumbnailSize(); */ if (d->delegate) { return d->delegate->thumbnailSize(); } return ThumbnailSize(); } void ImportCategorizedView::setThumbnailSize(int size) { setThumbnailSize(ThumbnailSize(size)); } void ImportCategorizedView::setThumbnailSize(const ThumbnailSize& s) { // we abuse this pair of method calls to restore scroll position // TODO check if needed layoutAboutToBeChanged(); d->delegate->setThumbnailSize(s); layoutWasChanged(); } void ImportCategorizedView::setCurrentWhenAvailable(qlonglong camItemId) { d->scrollToItemId = camItemId; } void ImportCategorizedView::setCurrentUrl(const QUrl& url) { if (url.isEmpty()) { clearSelection(); setCurrentIndex(QModelIndex()); return; } QString path = url.toLocalFile(); QModelIndex index = d->filterModel->indexForPath(path); if (!index.isValid()) { return; } clearSelection(); setCurrentIndex(index); } void ImportCategorizedView::setCurrentInfo(const CamItemInfo& info) { QModelIndex index = d->filterModel->indexForCamItemInfo(info); clearSelection(); setCurrentIndex(index); } void ImportCategorizedView::setSelectedUrls(const QList& urlList) { QItemSelection mySelection; for (QList::const_iterator it = urlList.constBegin(); it!=urlList.constEnd(); ++it) { const QString path = it->toLocalFile(); const QModelIndex index = d->filterModel->indexForPath(path); if (!index.isValid()) { qCWarning(DIGIKAM_IMPORTUI_LOG) << "no QModelIndex found for" << *it; } else { // TODO: is there a better way? mySelection.select(index, index); } } clearSelection(); selectionModel()->select(mySelection, QItemSelectionModel::Select); } void ImportCategorizedView::setSelectedCamItemInfos(const QList& infos) { QItemSelection mySelection; foreach(const CamItemInfo& info, infos) { QModelIndex index = d->filterModel->indexForCamItemInfo(info); mySelection.select(index, index); } selectionModel()->select(mySelection, QItemSelectionModel::ClearAndSelect); } void ImportCategorizedView::hintAt(const CamItemInfo& info) { if (info.isNull()) { return; } QModelIndex index = d->filterModel->indexForCamItemInfo(info); if (!index.isValid()) { return; } selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); scrollTo(index); } void ImportCategorizedView::addOverlay(ItemDelegateOverlay* overlay, ImportDelegate* delegate) { if (!delegate) { delegate = d->delegate; } delegate->installOverlay(overlay); if (delegate == d->delegate) { overlay->setView(this); overlay->setActive(true); } } void ImportCategorizedView::removeOverlay(ItemDelegateOverlay* overlay) { ImportDelegate* delegate = dynamic_cast(overlay->delegate()); if (delegate) { delegate->removeOverlay(overlay); } overlay->setView(0); } void ImportCategorizedView::updateGeometries() { ItemViewCategorized::updateGeometries(); d->delayedEnterTimer->start(); } void ImportCategorizedView::slotDelayedEnter() { // re-emit entered() for index under mouse (after layout). QModelIndex mouseIndex = indexAt(mapFromGlobal(QCursor::pos())); if (mouseIndex.isValid()) { emit DCategorizedView::entered(mouseIndex); } } void ImportCategorizedView::addSelectionOverlay(ImportDelegate* delegate) { addOverlay(new ItemSelectionOverlay(this), delegate); } void ImportCategorizedView::scrollToStoredItem() { if (d->scrollToItemId) { if (d->model->hasImage(d->scrollToItemId)) { QModelIndex index = d->filterModel->indexForCamItemId(d->scrollToItemId); setCurrentIndex(index); scrollToRelaxed(index, QAbstractItemView::PositionAtCenter); d->scrollToItemId = 0; } } } void ImportCategorizedView::slotCamItemInfosAdded() { if (d->scrollToItemId) { scrollToStoredItem(); } } void ImportCategorizedView::slotFileChanged(const QString& filePath) { QModelIndex index = d->filterModel->indexForPath(filePath); if (index.isValid()) { update(index); } } -void ImportCategorizedView::indexActivated(const QModelIndex& index, QMouseEvent* const event) +void ImportCategorizedView::indexActivated(const QModelIndex& index, Qt::KeyboardModifiers modifiers) { CamItemInfo info = d->filterModel->camItemInfo(index); if (!info.isNull()) { - activated(info, event); + activated(info, modifiers); emit camItemInfoActivated(info); } } void ImportCategorizedView::currentChanged(const QModelIndex& index, const QModelIndex& previous) { ItemViewCategorized::currentChanged(index, previous); emit currentChanged(d->filterModel->camItemInfo(index)); } void ImportCategorizedView::selectionChanged(const QItemSelection& selectedItems, const QItemSelection& deselectedItems) { ItemViewCategorized::selectionChanged(selectedItems, deselectedItems); if (!selectedItems.isEmpty()) { emit selected(d->filterModel->camItemInfos(selectedItems.indexes())); } if (!deselectedItems.isEmpty()) { emit deselected(d->filterModel->camItemInfos(deselectedItems.indexes())); } } -void ImportCategorizedView::activated(const CamItemInfo&, QMouseEvent*) +void ImportCategorizedView::activated(const CamItemInfo&, Qt::KeyboardModifiers) { // implemented in subclass } void ImportCategorizedView::showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index) { CamItemInfo info = d->filterModel->camItemInfo(index); showContextMenuOnInfo(event, info); } void ImportCategorizedView::showContextMenuOnInfo(QContextMenuEvent*, const CamItemInfo&) { // implemented in subclass } void ImportCategorizedView::paintEvent(QPaintEvent* e) { ItemViewCategorized::paintEvent(e); } QItemSelectionModel* ImportCategorizedView::getSelectionModel() const { return selectionModel(); } AbstractItemDragDropHandler* ImportCategorizedView::dragDropHandler() const { return d->model->dragDropHandler(); } void ImportCategorizedView::slotIccSettingsChanged(const ICCSettingsContainer&, const ICCSettingsContainer&) { viewport()->update(); } } // namespace Digikam diff --git a/core/utilities/import/views/importcategorizedview.h b/core/utilities/import/views/importcategorizedview.h index 1c30c24fda..f6d46b0c16 100644 --- a/core/utilities/import/views/importcategorizedview.h +++ b/core/utilities/import/views/importcategorizedview.h @@ -1,188 +1,188 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2012-07-13 * Description : Qt categorized item view for camera items * * Copyright (C) 2012 by Islam Wazery * * 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_IMPORT_CATEGORIZED_VIEW_H #define DIGIKAM_IMPORT_CATEGORIZED_VIEW_H // Local includes #include "itemviewcategorized.h" #include "importimagemodel.h" #include "importfiltermodel.h" #include "importthumbnailmodel.h" #include "itemdelegateoverlay.h" #include "camiteminfo.h" #include "digikam_export.h" namespace Digikam { class ImportDelegate; class ICCSettingsContainer; class DIGIKAM_EXPORT ImportCategorizedView : public ItemViewCategorized { Q_OBJECT public: explicit ImportCategorizedView(QWidget* const parent = 0); ~ImportCategorizedView(); void setModels(ImportItemModel* model, ImportSortFilterModel* filterModel); ImportItemModel* importItemModel() const; ImportSortFilterModel* importSortFilterModel() const; QItemSelectionModel* getSelectionModel() const; /// Returns any ImportFilterModel in chain. May not be sourceModel() ImportFilterModel* importFilterModel() const; /// Returns 0 if the ImportItemModel is not an ImportThumbnailModel ImportThumbnailModel* importThumbnailModel() const; ImportDelegate* delegate() const; CamItemInfo currentInfo() const; QUrl currentUrl() const; QList selectedCamItemInfos() const; QList selectedCamItemInfosCurrentFirst() const; QList selectedUrls() const; QList camItemInfos() const; QList urls() const; /** Selects the index as current and scrolls to it */ void toIndex(const QUrl& url); /** Returns the n-th info after the given one. * Specifically, return the previous info for nth = -1 * and the next info for n = 1. * Returns a null info if either startingPoint or the nth info are * not contained in the model */ CamItemInfo nextInOrder(const CamItemInfo& startingPoint, int nth); CamItemInfo previousInfo(const CamItemInfo& info) { return nextInOrder(info, -1); } CamItemInfo nextInfo(const CamItemInfo& info) { return nextInOrder(info, 1); } /// Add and remove an overlay. It will as well be removed automatically when destroyed. /// Unless you pass a different delegate, the current delegate will be used. void addOverlay(ItemDelegateOverlay* overlay, ImportDelegate* delegate = 0); void removeOverlay(ItemDelegateOverlay* overlay); void addSelectionOverlay(ImportDelegate* delegate = 0); ThumbnailSize thumbnailSize() const; virtual void setThumbnailSize(const ThumbnailSize& size); public Q_SLOTS: void setThumbnailSize(int size); /** Scroll the view to the given item when it becomes available */ void setCurrentWhenAvailable(qlonglong camItemId); /** Set as current item the item identified by its file url */ void setCurrentUrl(const QUrl& url); /** Set as current item the item identified by the CamItemInfo */ void setCurrentInfo(const CamItemInfo& info); /** Set selected items identified by their file urls */ void setSelectedUrls(const QList& urlList); /** Set selected items */ void setSelectedCamItemInfos(const QList& infos); /** Does something to gain attention for info, but not changing current selection */ void hintAt(const CamItemInfo& info); Q_SIGNALS: void currentChanged(const CamItemInfo& info); /// Emitted when new items are selected. The parameter includes only the newly selected infos, /// there may be other already selected infos. void selected(const QList& newSelectedInfos); /// Emitted when items are deselected. There may be other selected infos left. /// This signal is not emitted when the model is reset; then only selectionCleared is emitted. void deselected(const QList& nowDeselectedInfos); /// Emitted when the given CamItemInfo is activated. Info is never null. void camItemInfoActivated(const CamItemInfo& info); /// Emitted when a new model is set void modelChanged(); protected Q_SLOTS: void slotCamItemInfosAdded(); protected: // reimplemented from parent class QSortFilterProxyModel* filterModel() const; AbstractItemDragDropHandler* dragDropHandler() const; QModelIndex nextIndexHint(const QModelIndex& indexToAnchor, const QItemSelectionRange& removed) const; void setItemDelegate(ImportDelegate* delegate); - void indexActivated(const QModelIndex& index, QMouseEvent* const event); + void indexActivated(const QModelIndex& index, Qt::KeyboardModifiers modifiers); void currentChanged(const QModelIndex& index, const QModelIndex& previous); void paintEvent(QPaintEvent* e); void selectionChanged(const QItemSelection&, const QItemSelection&); void updateGeometries(); /// Reimplement these in a subclass - virtual void activated(const CamItemInfo& info, QMouseEvent* const event); + virtual void activated(const CamItemInfo& info, Qt::KeyboardModifiers modifiers); virtual void showContextMenuOnInfo(QContextMenuEvent* event, const CamItemInfo& info); virtual void showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index); private Q_SLOTS: void slotFileChanged(const QString& filePath); void slotDelayedEnter(); void slotIccSettingsChanged(const ICCSettingsContainer&, const ICCSettingsContainer&); private: void scrollToStoredItem(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_IMPORT_CATEGORIZED_VIEW_H diff --git a/core/utilities/import/views/importiconview.cpp b/core/utilities/import/views/importiconview.cpp index 4c88e75805..3b82c3f5ac 100644 --- a/core/utilities/import/views/importiconview.cpp +++ b/core/utilities/import/views/importiconview.cpp @@ -1,484 +1,484 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2012-22-07 * Description : Icon view for import tool items * * Copyright (C) 2012 by Islam Wazery * Copyright (C) 2012-2018 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 "importiconview.h" #include "importiconview_p.h" // Qt includes #include #include #include // Local includes #include "importcategorizedview.h" #include "importoverlays.h" #include "importsettings.h" #include "camitemsortsettings.h" #include "fileactionmngr.h" #include "importdelegate.h" #include "advancedrenamedialog.h" #include "advancedrenameprocessdialog.h" #include "itemviewutilities.h" #include "importcontextmenu.h" #include "importdragdrop.h" namespace Digikam { ImportIconView::ImportIconView(QWidget* const parent) : ImportCategorizedView(parent), d(new Private(this)) { ImportThumbnailModel* const model = new ImportThumbnailModel(this); ImportFilterModel* const filterModel = new ImportFilterModel(this); filterModel->setSourceImportModel(model); filterModel->sort(0); // an initial sorting is necessary setModels(model, filterModel); d->normalDelegate = new ImportNormalDelegate(this); setItemDelegate(d->normalDelegate); setSpacing(10); ImportSettings* const settings = ImportSettings::instance(); setThumbnailSize(ThumbnailSize(settings->getDefaultIconSize())); importItemModel()->setDragDropHandler(new ImportDragDropHandler(importItemModel())); setDragEnabled(true); setAcceptDrops(true); setDropIndicatorShown(false); setToolTipEnabled(settings->showToolTipsIsValid()); // selection overlay addSelectionOverlay(d->normalDelegate); //TODO: addSelectionOverlay(d->faceDelegate); // rotation overlays d->rotateLeftOverlay = ImportRotateOverlay::left(this); d->rotateRightOverlay = ImportRotateOverlay::right(this); addOverlay(new ImportDownloadOverlay(this)); addOverlay(new ImportLockOverlay(this)); addOverlay(new ImportCoordinatesOverlay(this)); d->updateOverlays(); // rating overlay ImportRatingOverlay* const ratingOverlay = new ImportRatingOverlay(this); addOverlay(ratingOverlay); //TODO: GroupIndicatorOverlay* groupOverlay = new GroupIndicatorOverlay(this); //TODO: addOverlay(groupOverlay); connect(ratingOverlay, SIGNAL(ratingEdited(QList,int)), this, SLOT(assignRating(QList,int))); //TODO: connect(groupOverlay, SIGNAL(toggleGroupOpen(QModelIndex)), //this, SLOT(groupIndicatorClicked(QModelIndex))); //TODO: connect(groupOverlay, SIGNAL(showButtonContextMenu(QModelIndex,QContextMenuEvent*)), //this, SLOT(showGroupContextMenu(QModelIndex,QContextMenuEvent*))); //TODO: connect(importItemModel()->dragDropHandler(), SIGNAL(assignTags(QList,QList)), //FileActionMngr::instance(), SLOT(assignTags(QList,QList))); //TODO: connect(importItemModel()->dragDropHandler(), SIGNAL(addToGroup(CamItemInfo,QList)), //FileActionMngr::instance(), SLOT(addToGroup(CamItemInfo,QList))); connect(settings, SIGNAL(setupChanged()), this, SLOT(slotSetupChanged())); slotSetupChanged(); } ImportIconView::~ImportIconView() { delete d; } ItemViewUtilities* ImportIconView::utilities() const { return d->utilities; } void ImportIconView::setThumbnailSize(const ThumbnailSize& size) { ImportCategorizedView::setThumbnailSize(size); } int ImportIconView::fitToWidthIcons() { return delegate()->calculatethumbSizeToFit(viewport()->size().width()); } CamItemInfo ImportIconView::camItemInfo(const QString& folder, const QString& file) { QUrl url = QUrl::fromLocalFile(folder); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + file); QModelIndex indexForCamItemInfo = importFilterModel()->indexForPath(url.toLocalFile()); if (indexForCamItemInfo.isValid()) { return importFilterModel()->camItemInfo(indexForCamItemInfo); } return CamItemInfo(); } CamItemInfo& ImportIconView::camItemInfoRef(const QString& folder, const QString& file) { QUrl url = QUrl::fromLocalFile(folder); url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1Char('/') + file); QModelIndex indexForCamItemInfo = importFilterModel()->indexForPath(url.toLocalFile()); QModelIndex mappedIndex = importFilterModel()->mapToSource(indexForCamItemInfo); return importItemModel()->camItemInfoRef(mappedIndex); } void ImportIconView::slotSetupChanged() { setToolTipEnabled(ImportSettings::instance()->showToolTipsIsValid()); setFont(ImportSettings::instance()->getIconViewFont()); d->updateOverlays(); ImportCategorizedView::slotSetupChanged(); } void ImportIconView::rename() { QList urls = selectedUrls(); NewNamesList newNamesList; QPointer dlg = new AdvancedRenameDialog(this); dlg->slotAddImages(urls); if (dlg->exec() == QDialog::Accepted) { newNamesList = dlg->newNames(); } delete dlg; if (!newNamesList.isEmpty()) { QPointer dlg = new AdvancedRenameProcessDialog(newNamesList); dlg->exec(); delete dlg; } } void ImportIconView::deleteSelected(bool /*permanently*/) { CamItemInfoList camItemInfoList = selectedCamItemInfos(); //FIXME: This way of deletion may not working with camera items. /* if (d->utilities->deleteImages(camItemInfoList, permanently)) { awayFromSelection(); } */ } void ImportIconView::deleteSelectedDirectly(bool /*permanently*/) { CamItemInfoList camItemInfoList = selectedCamItemInfos(); //FIXME: This way of deletion may not working with camera items. //d->utilities->deleteImagesDirectly(camItemInfoList, permanently); awayFromSelection(); } void ImportIconView::createGroupFromSelection() { //TODO: Implement grouping in import tool. /* QList selectedInfos = selectedCamItemInfosCurrentFirst(); CamItemInfo groupLeader = selectedInfos.takeFirst(); FileActionMngr::instance()->addToGroup(groupLeader, selectedInfos); */ } void ImportIconView::createGroupByTimeFromSelection() { //TODO: Implement grouping in import tool. /* QList selectedInfos = selectedCamItemInfosCurrentFirst(); while (selectedInfos.size() > 0) { QList group; CamItemInfo groupLeader = selectedInfos.takeFirst(); QDateTime dateTime = groupLeader.dateTime(); while (selectedInfos.size() > 0 && abs(dateTime.secsTo(selectedInfos.first().dateTime())) < 2) { group.push_back(selectedInfos.takeFirst()); } FileActionMngr::instance()->addToGroup(groupLeader, group); } */ } void ImportIconView::ungroupSelected() { //TODO: Implement grouping in import tool. //FileActionMngr::instance()->ungroup(selectedCamItemInfos()); } void ImportIconView::removeSelectedFromGroup() { //TODO: Implement grouping in import tool. //FileActionMngr::instance()->removeFromGroup(selectedCamItemInfos()); } void ImportIconView::slotRotateLeft(const QList& /*indexes*/) { /* QList imageInfos; foreach(const QModelIndex& index, indexes) { ItemInfo imageInfo(importFilterModel()->camItemInfo(index).url()); imageInfos << imageInfo; } FileActionMngr::instance()->transform(imageInfos, MetaEngineRotation::Rotate270); */ } void ImportIconView::slotRotateRight(const QList& /*indexes*/) { /* QList imageInfos; foreach(const QModelIndex& index, indexes) { ItemInfo imageInfo(importFilterModel()->camItemInfo(index).url()); imageInfos << imageInfo; } FileActionMngr::instance()->transform(imageInfos, MetaEngineRotation::Rotate90); */ } -void ImportIconView::activated(const CamItemInfo& info, QMouseEvent*) +void ImportIconView::activated(const CamItemInfo& info, Qt::KeyboardModifiers) { if (info.isNull()) { return; } if (ImportSettings::instance()->getItemLeftClickAction() == ImportSettings::ShowPreview) { emit previewRequested(info, false); } else { //TODO: openFile(info); } } void ImportIconView::showContextMenuOnInfo(QContextMenuEvent* event, const CamItemInfo& /*info*/) { QList selectedInfos = selectedCamItemInfosCurrentFirst(); QList selectedItemIDs; foreach(const CamItemInfo& info, selectedInfos) { selectedItemIDs << info.id; } // -------------------------------------------------------- QMenu popmenu(this); ImportContextMenuHelper cmhelper(&popmenu); cmhelper.addAction(QLatin1String("importui_fullscreen")); cmhelper.addAction(QLatin1String("options_show_menubar")); cmhelper.addAction(QLatin1String("import_zoomfit2window")); cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addAction(QLatin1String("importui_imagedownload")); cmhelper.addAction(QLatin1String("importui_imagemarkasdownloaded")); cmhelper.addAction(QLatin1String("importui_imagelock")); cmhelper.addAction(QLatin1String("importui_delete")); cmhelper.addSeparator(); cmhelper.addAction(QLatin1String("importui_item_view")); cmhelper.addServicesMenu(selectedUrls()); //TODO: cmhelper.addRotateMenu(selectedItemIDs); cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addAction(QLatin1String("importui_selectall")); cmhelper.addAction(QLatin1String("importui_selectnone")); cmhelper.addAction(QLatin1String("importui_selectinvert")); cmhelper.addSeparator(); // -------------------------------------------------------- //cmhelper.addAssignTagsMenu(selectedItemIDs); //cmhelper.addRemoveTagsMenu(selectedItemIDs); //cmhelper.addSeparator(); // -------------------------------------------------------- cmhelper.addLabelsAction(); //if (!d->faceMode) //{ // cmhelper.addGroupMenu(selectedItemIDs); //} // special action handling -------------------------------- //connect(&cmhelper, SIGNAL(signalAssignTag(int)), // this, SLOT(assignTagToSelected(int))); //TODO: Implement tag view for import tool. //connect(&cmhelper, SIGNAL(signalPopupTagsView()), // this, SIGNAL(signalPopupTagsView())); //connect(&cmhelper, SIGNAL(signalRemoveTag(int)), // this, SLOT(removeTagFromSelected(int))); //connect(&cmhelper, SIGNAL(signalGotoTag(int)), //this, SIGNAL(gotoTagAndImageRequested(int))); connect(&cmhelper, SIGNAL(signalAssignPickLabel(int)), this, SLOT(assignPickLabelToSelected(int))); connect(&cmhelper, SIGNAL(signalAssignColorLabel(int)), this, SLOT(assignColorLabelToSelected(int))); connect(&cmhelper, SIGNAL(signalAssignRating(int)), this, SLOT(assignRatingToSelected(int))); //connect(&cmhelper, SIGNAL(signalAddToExistingQueue(int)), //this, SLOT(insertSelectedToExistingQueue(int))); //FIXME: connect(&cmhelper, SIGNAL(signalCreateGroup()), //this, SLOT(createGroupFromSelection())); //connect(&cmhelper, SIGNAL(signalUngroup()), //this, SLOT(ungroupSelected())); //connect(&cmhelper, SIGNAL(signalRemoveFromGroup()), //this, SLOT(removeSelectedFromGroup())); // -------------------------------------------------------- cmhelper.exec(event->globalPos()); } void ImportIconView::showContextMenu(QContextMenuEvent* event) { QMenu popmenu(this); ImportContextMenuHelper cmhelper(&popmenu); cmhelper.addAction(QLatin1String("importui_fullscreen")); cmhelper.addAction(QLatin1String("options_show_menubar")); cmhelper.addSeparator(); cmhelper.addAction(QLatin1String("importui_close")); // -------------------------------------------------------- cmhelper.exec(event->globalPos()); } void ImportIconView::assignTagToSelected(int tagID) { CamItemInfoList infos = selectedCamItemInfos(); foreach(const CamItemInfo& info, infos) { importItemModel()->camItemInfoRef(importItemModel()->indexForCamItemInfo(info)).tagIds.append(tagID); } } void ImportIconView::removeTagFromSelected(int tagID) { CamItemInfoList infos = selectedCamItemInfos(); foreach(const CamItemInfo& info, infos) { importItemModel()->camItemInfoRef(importItemModel()->indexForCamItemInfo(info)).tagIds.removeAll(tagID); } } void ImportIconView::assignPickLabel(const QModelIndex& index, int pickId) { importItemModel()->camItemInfoRef(index).pickLabel = pickId; } void ImportIconView::assignPickLabelToSelected(int pickId) { CamItemInfoList infos = selectedCamItemInfos(); foreach(const CamItemInfo& info, infos) { importItemModel()->camItemInfoRef(importItemModel()->indexForCamItemInfo(info)).pickLabel = pickId; } } void ImportIconView::assignColorLabel(const QModelIndex& index, int colorId) { importItemModel()->camItemInfoRef(index).colorLabel = colorId; } void ImportIconView::assignColorLabelToSelected(int colorId) { CamItemInfoList infos = selectedCamItemInfos(); foreach(const CamItemInfo& info, infos) { importItemModel()->camItemInfoRef(importItemModel()->indexForCamItemInfo(info)).colorLabel = colorId; } } void ImportIconView::assignRating(const QList& indexes, int rating) { foreach(const QModelIndex& index, indexes) { if (index.isValid()) { importItemModel()->camItemInfoRef(index).rating = rating; } } } void ImportIconView::assignRatingToSelected(int rating) { CamItemInfoList infos = selectedCamItemInfos(); foreach(const CamItemInfo& info, infos) { importItemModel()->camItemInfoRef(importItemModel()->indexForCamItemInfo(info)).rating = rating; } } } // namespace Digikam diff --git a/core/utilities/import/views/importiconview.h b/core/utilities/import/views/importiconview.h index 70012a87a6..17dada10be 100644 --- a/core/utilities/import/views/importiconview.h +++ b/core/utilities/import/views/importiconview.h @@ -1,104 +1,104 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2012-22-07 * Description : Icon view for import tool items * * Copyright (C) 2012 by Islam Wazery * Copyright (C) 2012-2018 by Gilles Caulier * * This program is free software you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_IMPORT_ICON_VIEW_H #define DIGIKAM_IMPORT_ICON_VIEW_H // Local includes #include "importcategorizedview.h" namespace Digikam { class ItemViewUtilities; class ImportIconView : public ImportCategorizedView { Q_OBJECT public: explicit ImportIconView(QWidget* const parent = 0); ~ImportIconView(); ItemViewUtilities* utilities() const; int fitToWidthIcons(); CamItemInfo camItemInfo(const QString& folder, const QString& file); CamItemInfo& camItemInfoRef(const QString& folder, const QString& file); virtual void setThumbnailSize(const ThumbnailSize& size); public Q_SLOTS: void deleteSelected(bool permanently = false); void deleteSelectedDirectly(bool permanently = false); void createGroupFromSelection(); void createGroupByTimeFromSelection(); void ungroupSelected(); void removeSelectedFromGroup(); void rename(); void assignTagToSelected(int tagID); void removeTagFromSelected(int tagID); void assignPickLabel(const QModelIndex& index, int pickId); void assignPickLabelToSelected(int pickId); void assignColorLabel(const QModelIndex& index, int colorId); void assignColorLabelToSelected(int colorId); void assignRating(const QList& index, int rating); void assignRatingToSelected(int rating); Q_SIGNALS: void previewRequested(const CamItemInfo& info, bool downloadPreview); //void signalPopupTagsView(); private Q_SLOTS: void slotRotateLeft(const QList&); void slotRotateRight(const QList&); //void slotInitProgressIndicator(); protected: - virtual void activated(const CamItemInfo& info, QMouseEvent* const event); + virtual void activated(const CamItemInfo& info, Qt::KeyboardModifiers modifiers); virtual void showContextMenuOnInfo(QContextMenuEvent* event, const CamItemInfo& info); virtual void showContextMenu(QContextMenuEvent* event); virtual void slotSetupChanged(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_IMPORT_ICON_VIEW_H