File indexing completed on 2025-01-19 03:53:36
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2010-10-27 0007 * Description : Model to an ItemHistoryGraph 0008 * 0009 * SPDX-FileCopyrightText: 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "itemhistorygraphmodel.h" 0016 0017 // Qt includes 0018 0019 #include <QTreeWidgetItem> 0020 #include <QIcon> 0021 0022 // KDE includes 0023 0024 #include <klocalizedstring.h> 0025 0026 // Local includes 0027 0028 #include "dcategorizedsortfilterproxymodel.h" 0029 #include "dimgfiltermanager.h" 0030 #include "itemlistmodel.h" 0031 #include "itemhistorygraphdata.h" 0032 0033 namespace Digikam 0034 { 0035 0036 class Q_DECL_HIDDEN HistoryTreeItem 0037 { 0038 public: 0039 0040 enum HistoryTreeItemType 0041 { 0042 UnspecifiedType, 0043 VertexItemType, 0044 FilterActionItemType, 0045 HeaderItemType, 0046 CategoryItemType, 0047 SeparatorItemType 0048 }; 0049 0050 public: 0051 0052 explicit HistoryTreeItem(); 0053 virtual ~HistoryTreeItem(); 0054 0055 virtual HistoryTreeItemType type() const 0056 { 0057 return UnspecifiedType; 0058 } 0059 0060 bool isType(HistoryTreeItemType t) const 0061 { 0062 return (type() == t); 0063 } 0064 0065 void addItem(HistoryTreeItem* child); 0066 0067 int childCount() const 0068 { 0069 return children.size(); 0070 } 0071 0072 HistoryTreeItem* child(int index) const 0073 { 0074 return children.at(index); 0075 } 0076 0077 public: 0078 0079 HistoryTreeItem* parent; 0080 QList<HistoryTreeItem*> children; 0081 0082 private: 0083 0084 Q_DISABLE_COPY(HistoryTreeItem) 0085 }; 0086 0087 // ------------------------------------------------------------------------ 0088 0089 class Q_DECL_HIDDEN VertexItem : public HistoryTreeItem 0090 { 0091 public: 0092 0093 VertexItem() 0094 { 0095 } 0096 0097 explicit VertexItem(const HistoryGraph::Vertex& v) 0098 : vertex (v), 0099 category(HistoryImageId::InvalidType) 0100 { 0101 } 0102 0103 HistoryTreeItemType type() const override 0104 { 0105 return VertexItemType; 0106 } 0107 0108 public: 0109 0110 HistoryGraph::Vertex vertex; 0111 QModelIndex index; 0112 HistoryImageId::Types category; 0113 0114 private: 0115 0116 Q_DISABLE_COPY(VertexItem) 0117 }; 0118 0119 // ------------------------------------------------------------------------ 0120 0121 class Q_DECL_HIDDEN FilterActionItem : public HistoryTreeItem 0122 { 0123 public: 0124 0125 FilterActionItem() 0126 { 0127 } 0128 0129 explicit FilterActionItem(const FilterAction& action) 0130 : action(action) 0131 { 0132 } 0133 0134 HistoryTreeItemType type() const override 0135 { 0136 return FilterActionItemType; 0137 } 0138 0139 public: 0140 0141 FilterAction action; 0142 0143 private: 0144 0145 Q_DISABLE_COPY(FilterActionItem) 0146 }; 0147 0148 // ------------------------------------------------------------------------ 0149 0150 class Q_DECL_HIDDEN HeaderItem : public HistoryTreeItem 0151 { 0152 public: 0153 0154 explicit HeaderItem(const QString& title) 0155 : title(title) 0156 { 0157 } 0158 0159 HistoryTreeItemType type() const override 0160 { 0161 return HeaderItemType; 0162 } 0163 0164 public: 0165 0166 QString title; 0167 0168 private: 0169 0170 Q_DISABLE_COPY(HeaderItem) 0171 }; 0172 0173 // ------------------------------------------------------------------------ 0174 0175 class Q_DECL_HIDDEN CategoryItem : public HistoryTreeItem 0176 { 0177 public: 0178 0179 explicit CategoryItem(const QString& title) 0180 : title(title) 0181 { 0182 } 0183 0184 HistoryTreeItemType type() const override 0185 { 0186 return CategoryItemType; 0187 } 0188 0189 public: 0190 0191 QString title; 0192 0193 private: 0194 0195 Q_DISABLE_COPY(CategoryItem) 0196 }; 0197 0198 // ------------------------------------------------------------------------ 0199 0200 class Q_DECL_HIDDEN SeparatorItem : public HistoryTreeItem 0201 { 0202 public: 0203 0204 SeparatorItem() = default; 0205 0206 HistoryTreeItemType type() const override 0207 { 0208 return SeparatorItemType; 0209 } 0210 0211 private: 0212 0213 Q_DISABLE_COPY(SeparatorItem) 0214 }; 0215 0216 // ------------------------------------------------------------------------ 0217 0218 #define if_isItem(class, name, pointer) \ 0219 if (pointer && static_cast<HistoryTreeItem*>(pointer)->type() == HistoryTreeItem:: class##Type) \ 0220 for (class* name = static_cast<class*>(pointer) ; name ; name = nullptr) 0221 0222 // ------------------------------------------------------------------------ 0223 0224 HistoryTreeItem::HistoryTreeItem() 0225 : parent(nullptr) 0226 { 0227 } 0228 0229 HistoryTreeItem::~HistoryTreeItem() 0230 { 0231 qDeleteAll(children); 0232 } 0233 0234 void HistoryTreeItem::addItem(HistoryTreeItem* child) 0235 { 0236 children << child; 0237 child->parent = this; 0238 } 0239 0240 // ------------------------------------------------------------------------ 0241 0242 static bool oldestInfoFirst(const ItemInfo&a, const ItemInfo& b) 0243 { 0244 return (a.modDateTime() < b.modDateTime()); 0245 } 0246 0247 static bool newestInfoFirst(const ItemInfo&a, const ItemInfo& b) 0248 { 0249 return (a.modDateTime() > b.modDateTime()); 0250 } 0251 0252 template <typename ItemInfoLessThan> 0253 0254 class Q_DECL_HIDDEN LessThanOnVertexItemInfo 0255 { 0256 public: 0257 0258 LessThanOnVertexItemInfo(const HistoryGraph& graph, ItemInfoLessThan imageInfoLessThan) 0259 : graph (graph), 0260 imageInfoLessThan(imageInfoLessThan) 0261 { 0262 } 0263 0264 bool operator()(const HistoryGraph::Vertex& a, const HistoryGraph::Vertex& b) const 0265 { 0266 const HistoryVertexProperties& propsA = graph.properties(a); 0267 const HistoryVertexProperties& propsB = graph.properties(b); 0268 0269 if (propsA.infos.isEmpty()) 0270 { 0271 return false; 0272 } 0273 else if (propsB.infos.isEmpty()) 0274 { 0275 return true; 0276 } 0277 0278 return imageInfoLessThan(propsA.infos.first(), propsB.infos.first()); 0279 } 0280 0281 public: 0282 0283 const HistoryGraph& graph; 0284 ItemInfoLessThan imageInfoLessThan; 0285 }; 0286 0287 // ------------------------------------------------------------------------ 0288 0289 class Q_DECL_HIDDEN ItemHistoryGraphModel::Private 0290 { 0291 public: 0292 0293 explicit Private() 0294 : mode (ItemHistoryGraphModel::CombinedTreeMode), 0295 rootItem (nullptr), 0296 imageModel(nullptr) 0297 { 0298 } 0299 0300 ItemHistoryGraphModel::Mode mode; 0301 0302 ItemHistoryGraph historyGraph; 0303 ItemInfo info; 0304 0305 HistoryTreeItem* rootItem; 0306 QList<VertexItem*> vertexItems; 0307 ItemListModel* imageModel; 0308 QList<HistoryGraph::Vertex> path; 0309 QHash<HistoryGraph::Vertex, HistoryImageId::Types> categories; 0310 0311 public: 0312 0313 inline const ItemHistoryGraphData& graph() const 0314 { 0315 return historyGraph.data(); 0316 } 0317 0318 inline HistoryTreeItem* historyItem(const QModelIndex& index) const 0319 { 0320 return (index.isValid() ? static_cast<HistoryTreeItem*>(index.internalPointer()) 0321 : rootItem); 0322 } 0323 0324 void build(); 0325 void buildImagesList(); 0326 void buildImagesTree(); 0327 void buildCombinedTree(const HistoryGraph::Vertex& ref); 0328 void addCombinedItemCategory(HistoryTreeItem* parentItem, QList<HistoryGraph::Vertex>& vertices, 0329 const QString& title, const HistoryGraph::Vertex& showActionsFrom, 0330 QList<HistoryGraph::Vertex>& added); 0331 void addItemSubgroup(VertexItem* parent, const QList<HistoryGraph::Vertex>& vertices, const QString& title, bool flat = false); 0332 void addIdenticalItems(HistoryTreeItem* parentItem, const HistoryGraph::Vertex& vertex, 0333 const QList<ItemInfo>& infos, const QString& title); 0334 0335 VertexItem* createVertexItem(const HistoryGraph::Vertex& v, const ItemInfo& info = ItemInfo()); 0336 FilterActionItem* createFilterActionItem(const FilterAction& action); 0337 0338 template <typename ItemInfoLessThan> LessThanOnVertexItemInfo<ItemInfoLessThan> 0339 0340 sortBy(ItemInfoLessThan imageInfoLessThan) 0341 { 0342 return LessThanOnVertexItemInfo<ItemInfoLessThan>(graph(), imageInfoLessThan); 0343 } 0344 }; 0345 0346 // ------------------------------------------------------------------------ 0347 0348 VertexItem* ItemHistoryGraphModel::Private::createVertexItem(const HistoryGraph::Vertex& v, 0349 const ItemInfo& givenInfo) 0350 { 0351 const HistoryVertexProperties& props = graph().properties(v); 0352 ItemInfo info = givenInfo.isNull() ? props.firstItemInfo() : givenInfo; 0353 QModelIndex index = imageModel->indexForItemInfo(info); 0354 /* 0355 qCDebug(DIGIKAM_DATABASE_LOG) << "Added" << info.id() << index; 0356 */ 0357 VertexItem* item = new VertexItem(v); 0358 item->index = index; 0359 item->category = categories.value(v); 0360 vertexItems << item; 0361 /* 0362 qCDebug(DIGIKAM_DATABASE_LOG) << "Adding vertex item" << graph().properties(v).firstItemInfo().id() << index; 0363 */ 0364 return item; 0365 } 0366 0367 FilterActionItem* ItemHistoryGraphModel::Private::createFilterActionItem(const FilterAction& action) 0368 { 0369 /* 0370 qCDebug(DIGIKAM_DATABASE_LOG) << "Adding vertex item for" << action.displayableName(); 0371 */ 0372 return new FilterActionItem(action); 0373 } 0374 0375 void ItemHistoryGraphModel::Private::build() 0376 { 0377 delete rootItem; 0378 vertexItems.clear(); 0379 rootItem = new HistoryTreeItem; 0380 /* 0381 qCDebug(DIGIKAM_DATABASE_LOG) << historyGraph; 0382 */ 0383 HistoryGraph::Vertex ref = graph().findVertexByProperties(info); 0384 path = graph().longestPathTouching(ref, sortBy(newestInfoFirst)); 0385 categories = graph().categorize(); 0386 0387 if (path.isEmpty()) 0388 { 0389 return; 0390 } 0391 0392 if (mode == ItemHistoryGraphModel::ImagesListMode) 0393 { 0394 buildImagesList(); 0395 } 0396 else if (mode == ItemHistoryGraphModel::ImagesTreeMode) 0397 { 0398 buildImagesTree(); 0399 } 0400 else if (mode == CombinedTreeMode) 0401 { 0402 buildCombinedTree(ref); 0403 } 0404 } 0405 0406 void ItemHistoryGraphModel::Private::buildImagesList() 0407 { 0408 QList<HistoryGraph::Vertex> verticesOrdered = graph().verticesDepthFirstSorted(path.first(), sortBy(oldestInfoFirst)); // clazy:exclude=missing-typeinfo 0409 0410 Q_FOREACH (const HistoryGraph::Vertex& v, verticesOrdered) 0411 { 0412 rootItem->addItem(createVertexItem(v)); 0413 } 0414 } 0415 0416 void ItemHistoryGraphModel::Private::buildImagesTree() 0417 { 0418 QList<HistoryGraph::Vertex> verticesOrdered = graph().verticesDepthFirstSorted(path.first(), sortBy(oldestInfoFirst)); // clazy:exclude=missing-typeinfo 0419 0420 QMap<HistoryGraph::Vertex, int> distances = graph().shortestDistancesFrom(path.first()); // clazy:exclude=missing-typeinfo 0421 0422 0423 QList<HistoryGraph::Vertex> sources; // clazy:exclude=missing-typeinfo 0424 int previousLevel = 0; 0425 HistoryTreeItem* parent = rootItem; 0426 VertexItem* item = nullptr; 0427 VertexItem* previousItem = nullptr; 0428 0429 Q_FOREACH (const HistoryGraph::Vertex& v, verticesOrdered) 0430 { 0431 int currentLevel = distances.value(v); 0432 0433 if (currentLevel == -1) 0434 { 0435 // unreachable from first root 0436 0437 if (graph().isRoot(v) && (parent == rootItem)) 0438 { 0439 // other first-level root? 0440 0441 parent->addItem(createVertexItem(v)); 0442 } 0443 else 0444 { 0445 // add later as sources 0446 0447 sources << v; 0448 } 0449 0450 continue; 0451 } 0452 0453 item = createVertexItem(v); 0454 0455 if (!sources.isEmpty()) 0456 { 0457 addItemSubgroup(item, sources, i18nc("@title", "Source Images")); 0458 } 0459 0460 if (currentLevel == previousLevel) 0461 { 0462 parent->addItem(item); 0463 } 0464 else if (currentLevel > previousLevel && previousItem) // check pointer, prevent crash is distances are faulty 0465 { 0466 previousItem->addItem(item); 0467 parent = previousItem; 0468 } 0469 else if (currentLevel < previousLevel) 0470 { 0471 for (int level = currentLevel ; level < previousLevel ; ++level) 0472 { 0473 parent = parent->parent; 0474 } 0475 parent->addItem(item); 0476 } 0477 0478 previousItem = item; 0479 previousLevel = currentLevel; 0480 } 0481 } 0482 0483 void ItemHistoryGraphModel::Private::buildCombinedTree(const HistoryGraph::Vertex& ref) 0484 { 0485 VertexItem* item = nullptr; 0486 CategoryItem *categoryItem = new CategoryItem(i18nc("@title", "Image History")); 0487 rootItem->addItem(categoryItem); 0488 0489 QList<HistoryGraph::Vertex> added; // clazy:exclude=missing-typeinfo 0490 QList<HistoryGraph::Vertex> currentVersions = categories.keys(HistoryImageId::Current); // clazy:exclude=missing-typeinfo 0491 QList<HistoryGraph::Vertex> leavesFromRef = graph().leavesFrom(ref); // clazy:exclude=missing-typeinfo 0492 0493 bool onePath = (leavesFromRef.size() <= 1); 0494 0495 for (int i = 0 ; i < path.size() ; ++i) 0496 { 0497 const HistoryGraph::Vertex& v = path.at(i); 0498 HistoryGraph::Vertex previous = i ? path.at(i-1) : HistoryGraph::Vertex(); 0499 /* 0500 HistoryGraph::Vertex next = i < path.size() - 1 ? path[i+1] : HistoryGraph::Vertex(); 0501 */ 0502 /* 0503 qCDebug(DIGIKAM_DATABASE_LOG) << "Vertex on path" << path[i]; 0504 */ 0505 // create new item 0506 0507 item = createVertexItem(v); 0508 0509 QList<HistoryGraph::Vertex> vertices; // clazy:exclude=missing-typeinfo 0510 0511 // any extra sources? 0512 0513 QList<HistoryGraph::Vertex> sources = graph().adjacentVertices(item->vertex, HistoryGraph::EdgesToRoot); // clazy:exclude=missing-typeinfo 0514 0515 Q_FOREACH (const HistoryGraph::Vertex& source, sources) 0516 { 0517 if (source != previous) 0518 { 0519 rootItem->addItem(createVertexItem(source)); 0520 } 0521 } 0522 0523 /* 0524 // Any other egdes off the main path? 0525 0526 QList<HistoryGraph::Vertex> branches = graph().adjacentVertices(v, HistoryGraph::EdgesToLeaf); 0527 QList<HistoryGraph::Vertex> subgraph; 0528 0529 Q_FOREACH (const HistoryGraph::Vertex& branch, branches) 0530 { 0531 if (branch != next) 0532 { 0533 subgraph << graph().verticesDominatedByDepthFirstSorted(branch, v, sortBy(oldestInfoFirst)); 0534 } 0535 } 0536 0537 addItemSubgroup(item, subgraph, i18nc("@title", "More Derived Images")); 0538 */ 0539 0540 // Add filter actions above item 0541 0542 HistoryEdgeProperties props = graph().properties(v, previous); 0543 0544 Q_FOREACH (const FilterAction& action, props.actions) 0545 { 0546 rootItem->addItem(createFilterActionItem(action)); 0547 } 0548 0549 // now, add item 0550 0551 rootItem->addItem(item); 0552 added << v; 0553 0554 // If there are multiple derived images, we display them in the next section 0555 0556 if ((v == ref) && !onePath) 0557 { 0558 break; 0559 } 0560 } 0561 0562 Q_FOREACH (const HistoryGraph::Vertex& v, added) 0563 { 0564 leavesFromRef.removeOne(v); 0565 } 0566 0567 if (!leavesFromRef.isEmpty()) 0568 { 0569 addCombinedItemCategory(rootItem, leavesFromRef, i18nc("@title", "Derived Images"), ref, added); 0570 } 0571 0572 Q_FOREACH (const HistoryGraph::Vertex& v, added) 0573 { 0574 currentVersions.removeOne(v); 0575 } 0576 0577 if (!currentVersions.isEmpty()) 0578 { 0579 addCombinedItemCategory(rootItem, currentVersions, i18nc("@title", "Related Images"), path.first(), added); 0580 } 0581 0582 QList<ItemInfo> allInfos = graph().properties(ref).infos; 0583 0584 if (allInfos.size() > 1) 0585 { 0586 addIdenticalItems(rootItem, ref, allInfos, i18nc("@title", "Identical Images")); 0587 } 0588 } 0589 0590 void ItemHistoryGraphModel::Private::addCombinedItemCategory(HistoryTreeItem* parentItem, 0591 QList<HistoryGraph::Vertex>& vertices, 0592 const QString& title, 0593 const HistoryGraph::Vertex& showActionsFrom, 0594 QList<HistoryGraph::Vertex>& added) 0595 { 0596 parentItem->addItem(new CategoryItem(title)); 0597 0598 std::sort(vertices.begin(), vertices.end(), sortBy(oldestInfoFirst)); 0599 bool isFirst = true; 0600 VertexItem* item = nullptr; 0601 0602 Q_FOREACH (const HistoryGraph::Vertex& v, vertices) 0603 { 0604 if (isFirst) 0605 { 0606 isFirst = false; 0607 } 0608 else 0609 { 0610 parentItem->addItem(new SeparatorItem); 0611 } 0612 0613 item = createVertexItem(v); 0614 0615 QList<HistoryGraph::Vertex> shortestPath = graph().shortestPath(showActionsFrom, v); // clazy:exclude=missing-typeinfo 0616 0617 // add all filter actions showActionsFrom -> v above item 0618 0619 for (int i = 1 ; i < shortestPath.size() ; ++i) 0620 { 0621 HistoryEdgeProperties props = graph().properties(shortestPath.at(i), shortestPath.at(i-1)); 0622 0623 Q_FOREACH (const FilterAction& action, props.actions) 0624 { 0625 parentItem->addItem(createFilterActionItem(action)); 0626 } 0627 } 0628 0629 parentItem->addItem(item); 0630 added << v; 0631 0632 // Provide access to intermediates 0633 0634 shortestPath.removeOne(showActionsFrom); 0635 shortestPath.removeOne(v); 0636 0637 Q_FOREACH (const HistoryGraph::Vertex& addedVertex, added) 0638 { 0639 shortestPath.removeOne(addedVertex); 0640 } 0641 0642 addItemSubgroup(item, shortestPath, i18nc("@title", "Intermediate Steps:"), true); 0643 } 0644 } 0645 0646 void ItemHistoryGraphModel::Private::addItemSubgroup(VertexItem* parent, 0647 const QList<HistoryGraph::Vertex>& vertices, 0648 const QString& title, 0649 bool flat) 0650 { 0651 if (vertices.isEmpty()) 0652 { 0653 return; 0654 } 0655 0656 HeaderItem* const header = new HeaderItem(title); 0657 parent->addItem(header); 0658 HistoryTreeItem* const addToItem = flat ? static_cast<HistoryTreeItem*>(parent) : static_cast<HistoryTreeItem*>(header); 0659 0660 Q_FOREACH (const HistoryGraph::Vertex& v, vertices) 0661 { 0662 addToItem->addItem(createVertexItem(v)); 0663 } 0664 } 0665 0666 void ItemHistoryGraphModel::Private::addIdenticalItems(HistoryTreeItem* parentItem, 0667 const HistoryGraph::Vertex& vertex, 0668 const QList<ItemInfo>& infos, 0669 const QString& title) 0670 { 0671 parentItem->addItem(new CategoryItem(title)); 0672 0673 // the properties image info list is already sorted by proximity to subject 0674 0675 VertexItem* item = nullptr; 0676 bool isFirst = true; 0677 0678 for (int i = 1 ; i < infos.size() ; ++i) 0679 { 0680 if (isFirst) 0681 { 0682 isFirst = false; 0683 } 0684 else 0685 { 0686 parentItem->addItem(new SeparatorItem); 0687 } 0688 0689 item = createVertexItem(vertex, infos.at(i)); 0690 parentItem->addItem(item); 0691 } 0692 } 0693 0694 // ------------------------------------------------------------------------ 0695 0696 ItemHistoryGraphModel::ItemHistoryGraphModel(QWidget* const parent) 0697 : QAbstractItemModel(parent), 0698 d (new Private) 0699 { 0700 d->rootItem = new HistoryTreeItem; 0701 d->imageModel = new ItemListModel(parent); 0702 } 0703 0704 ItemHistoryGraphModel::~ItemHistoryGraphModel() 0705 { 0706 delete d->rootItem; 0707 delete d; 0708 } 0709 0710 void ItemHistoryGraphModel::setMode(Mode mode) 0711 { 0712 if (d->mode == mode) 0713 { 0714 return; 0715 } 0716 0717 d->mode = mode; 0718 setHistory(d->info, d->historyGraph); 0719 } 0720 0721 ItemHistoryGraphModel::Mode ItemHistoryGraphModel::mode() const 0722 { 0723 return d->mode; 0724 } 0725 0726 void ItemHistoryGraphModel::setHistory(const ItemInfo& subject, const ItemHistoryGraph& graph) 0727 { 0728 beginResetModel(); 0729 0730 d->info = subject; 0731 0732 if (graph.isNull()) 0733 { 0734 d->historyGraph = ItemHistoryGraph::fromInfo(subject); 0735 } 0736 else 0737 { 0738 d->historyGraph = graph; 0739 d->historyGraph.prepareForDisplay(subject); 0740 } 0741 0742 // fill helper model 0743 0744 d->imageModel->clearItemInfos(); 0745 d->imageModel->addItemInfos(d->historyGraph.allImages()); 0746 0747 d->build(); 0748 0749 endResetModel(); 0750 } 0751 0752 ItemInfo ItemHistoryGraphModel::subject() const 0753 { 0754 return d->info; 0755 } 0756 0757 bool ItemHistoryGraphModel::isImage(const QModelIndex& index) const 0758 { 0759 HistoryTreeItem* const item = d->historyItem(index); 0760 0761 return (item && item->isType(HistoryTreeItem::VertexItemType)); 0762 } 0763 0764 bool ItemHistoryGraphModel::isFilterAction(const QModelIndex& index) const 0765 { 0766 HistoryTreeItem* const item = d->historyItem(index); 0767 0768 return (item && item->isType(HistoryTreeItem::FilterActionItemType)); 0769 } 0770 0771 FilterAction ItemHistoryGraphModel::filterAction(const QModelIndex& index) const 0772 { 0773 HistoryTreeItem* const item = d->historyItem(index); 0774 0775 if_isItem(FilterActionItem, filterActionItem, item) 0776 { 0777 return filterActionItem->action; 0778 } 0779 0780 return FilterAction(); 0781 } 0782 0783 bool ItemHistoryGraphModel::hasImage(const ItemInfo& info) 0784 { 0785 return d->imageModel->hasImage(info); 0786 } 0787 0788 ItemInfo ItemHistoryGraphModel::imageInfo(const QModelIndex& index) const 0789 { 0790 QModelIndex imageIndex = imageModelIndex(index); 0791 0792 return ItemModel::retrieveItemInfo(imageIndex); 0793 } 0794 0795 QModelIndex ItemHistoryGraphModel::indexForInfo(const ItemInfo& info) const 0796 { 0797 if (info.isNull()) 0798 { 0799 return QModelIndex(); 0800 } 0801 0802 // try with primary info 0803 0804 Q_FOREACH (VertexItem* const item, d->vertexItems) 0805 { 0806 if (ItemModel::retrieveItemInfo(item->index) == info) 0807 { // cppcheck-suppress useStlAlgorithm 0808 return createIndex(item->parent->children.indexOf(item), 0, item); 0809 } 0810 } 0811 0812 // try all associated infos 0813 0814 Q_FOREACH (VertexItem* const item, d->vertexItems) 0815 { 0816 if (d->graph().properties(item->vertex).infos.contains(info)) 0817 { 0818 return createIndex(item->parent->children.indexOf(item), 0, item); 0819 } 0820 } 0821 0822 return QModelIndex(); 0823 } 0824 0825 QVariant ItemHistoryGraphModel::data(const QModelIndex& index, int role) const 0826 { 0827 if (!index.isValid()) 0828 { 0829 return QVariant(); 0830 } 0831 0832 HistoryTreeItem* const item = d->historyItem(index); 0833 0834 if_isItem(VertexItem, vertexItem, item) 0835 { 0836 if (vertexItem->index.isValid()) 0837 { 0838 QVariant data = vertexItem->index.data(role); 0839 0840 switch (role) 0841 { 0842 case IsImageItemRole: 0843 { 0844 return true; 0845 } 0846 0847 case IsSubjectImageRole: 0848 { 0849 return (bool)d->graph().properties(vertexItem->vertex).infos.contains(d->info); 0850 } 0851 0852 case Qt::DisplayRole: 0853 { 0854 if (vertexItem->category & HistoryImageId::Original) 0855 { 0856 return i18nc("@item filename", "%1\n(Original Image)", data.toString()); 0857 } 0858 0859 if (vertexItem->category & HistoryImageId::Source) 0860 { 0861 return i18nc("@item filename", "%1\n(Source Image)", data.toString()); 0862 } 0863 0864 break; 0865 } 0866 } 0867 0868 return data; 0869 } 0870 0871 // else: read HistoryImageId from d->graph().properties(vertexItem->vertex)? 0872 } 0873 else if_isItem(FilterActionItem, filterActionItem, item) 0874 { 0875 switch (role) 0876 { 0877 case IsFilterActionItemRole: 0878 { 0879 return true; 0880 } 0881 0882 case Qt::DisplayRole: 0883 { 0884 return DImgFilterManager::instance()->i18nDisplayableName(filterActionItem->action); 0885 } 0886 0887 case Qt::DecorationRole: 0888 { 0889 QString iconName = DImgFilterManager::instance()->filterIcon(filterActionItem->action); 0890 return QIcon::fromTheme(iconName); 0891 } 0892 0893 case FilterActionRole: 0894 { 0895 return QVariant::fromValue(filterActionItem->action); 0896 } 0897 0898 default: 0899 { 0900 break; 0901 } 0902 } 0903 } 0904 else if_isItem(HeaderItem, headerItem, item) 0905 { 0906 switch (role) 0907 { 0908 case IsHeaderItemRole: 0909 return true; 0910 0911 case Qt::DisplayRole: 0912 /* 0913 case Qt::ToolTipRole: 0914 */ 0915 return headerItem->title; 0916 break; 0917 } 0918 } 0919 else if_isItem(CategoryItem, categoryItem, item) 0920 { 0921 switch (role) 0922 { 0923 case IsCategoryItemRole: 0924 return true; 0925 0926 case Qt::DisplayRole: 0927 case DCategorizedSortFilterProxyModel::CategoryDisplayRole: 0928 /* 0929 case Qt::ToolTipRole: 0930 */ 0931 return categoryItem->title; 0932 } 0933 } 0934 else if_isItem(SeparatorItem, separatorItem, item) 0935 { 0936 switch (role) 0937 { 0938 case IsSeparatorItemRole: 0939 return true; 0940 } 0941 } 0942 0943 switch (role) 0944 { 0945 case IsImageItemRole: 0946 case IsFilterActionItemRole: 0947 case IsHeaderItemRole: 0948 case IsCategoryItemRole: 0949 case IsSubjectImageRole: 0950 return false; 0951 0952 default: 0953 return QVariant(); 0954 } 0955 } 0956 0957 bool ItemHistoryGraphModel::setData(const QModelIndex& index, const QVariant& value, int role) 0958 { 0959 HistoryTreeItem* const item = d->historyItem(index); 0960 0961 if_isItem(VertexItem, vertexItem, item) 0962 { 0963 if (vertexItem->index.isValid()) 0964 { 0965 return d->imageModel->setData(vertexItem->index, value, role); 0966 } 0967 } 0968 0969 return false; 0970 } 0971 0972 ItemListModel* ItemHistoryGraphModel::imageModel() const 0973 { 0974 return d->imageModel; 0975 } 0976 0977 QModelIndex ItemHistoryGraphModel::imageModelIndex(const QModelIndex& index) const 0978 { 0979 HistoryTreeItem* const item = d->historyItem(index); 0980 0981 if_isItem(VertexItem, vertexItem, item) 0982 { 0983 return vertexItem->index; 0984 } 0985 0986 return QModelIndex(); 0987 } 0988 0989 QVariant ItemHistoryGraphModel::headerData(int section, Qt::Orientation orientation, int role) const 0990 { 0991 Q_UNUSED(section) 0992 Q_UNUSED(orientation) 0993 Q_UNUSED(role) 0994 0995 return QVariant(); 0996 } 0997 0998 int ItemHistoryGraphModel::rowCount(const QModelIndex& parent) const 0999 { 1000 return d->historyItem(parent)->childCount(); 1001 } 1002 1003 int ItemHistoryGraphModel::columnCount(const QModelIndex&) const 1004 { 1005 return 1; 1006 } 1007 1008 Qt::ItemFlags ItemHistoryGraphModel::flags(const QModelIndex& index) const 1009 { 1010 if (!index.isValid()) 1011 { 1012 return Qt::NoItemFlags; 1013 } 1014 1015 HistoryTreeItem* const item = d->historyItem(index); 1016 1017 if_isItem(VertexItem, vertexItem, item) 1018 { 1019 return d->imageModel->flags(vertexItem->index); 1020 } 1021 1022 if (item) 1023 { 1024 switch (item->type()) 1025 { 1026 case HistoryTreeItem::FilterActionItemType: 1027 return (Qt::ItemIsEnabled | Qt::ItemIsSelectable); 1028 1029 case HistoryTreeItem::HeaderItemType: 1030 case HistoryTreeItem::CategoryItemType: 1031 case HistoryTreeItem::SeparatorItemType: 1032 default: 1033 return Qt::ItemIsEnabled; 1034 } 1035 } 1036 1037 return Qt::ItemIsEnabled; 1038 } 1039 1040 QModelIndex ItemHistoryGraphModel::index(int row, int column, const QModelIndex& parent) const 1041 { 1042 if ((column != 0) || (row < 0)) 1043 { 1044 return QModelIndex(); 1045 } 1046 1047 HistoryTreeItem* const item = d->historyItem(parent); 1048 1049 if (row >= item->childCount()) 1050 { 1051 return QModelIndex(); 1052 } 1053 1054 return createIndex(row, 0, item->child(row)); 1055 } 1056 1057 bool ItemHistoryGraphModel::hasChildren(const QModelIndex& parent) const 1058 { 1059 return d->historyItem(parent)->childCount(); 1060 } 1061 1062 QModelIndex ItemHistoryGraphModel::parent(const QModelIndex& index) const 1063 { 1064 HistoryTreeItem* const item = d->historyItem(index); 1065 HistoryTreeItem* const parent = item->parent; 1066 1067 if (!parent) 1068 { 1069 return QModelIndex(); // index was an invalid index 1070 } 1071 1072 HistoryTreeItem* const grandparent = parent->parent; 1073 1074 if (!grandparent) 1075 { 1076 return QModelIndex(); // index was a top-level index, was the invisible rootItem as parent 1077 } 1078 1079 return createIndex(grandparent->children.indexOf(parent), 0, parent); 1080 } 1081 1082 } // namespace Digikam 1083 1084 #include "moc_itemhistorygraphmodel.cpp"