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"