File indexing completed on 2024-12-08 13:28:01

0001 /***************************************************************************
0002  *  Copyright (C) 2019 by Renaud Guezennec                                 *
0003  *   http://www.rolisteam.org/contact                                      *
0004  *                                                                         *
0005  *   This software is free software; you can redistribute it and/or modify *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; if not, write to the                         *
0017  *   Free Software Foundation, Inc.,                                       *
0018  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
0019  ***************************************************************************/
0020 #include "minditemmodel.h"
0021 
0022 #include <QColor>
0023 #include <QDebug>
0024 #include <QRectF>
0025 
0026 #include "mindmap/data/linkcontroller.h"
0027 #include "mindmap/model/imagemodel.h"
0028 #include "mindmap/model/nodeimageprovider.h"
0029 
0030 namespace mindmap
0031 {
0032 std::tuple<std::vector<MindItem*>&, int> getVector(std::vector<MindItem*>& links, std::vector<MindItem*>& package,
0033                                                    std::vector<MindItem*>& node, MindItem::Type type)
0034 {
0035 
0036     if(type == MindItem::LinkType)
0037         return {links, 0};
0038     else if(type == MindItem::PackageType)
0039         return {package, links.size()};
0040     else // if(type == MindItem::NodeType)
0041         return {node, links.size() + package.size()};
0042 }
0043 
0044 MindItemModel::MindItemModel(ImageModel* imgModel, QObject* parent) : QAbstractListModel(parent), m_imgModel(imgModel)
0045 {
0046 }
0047 
0048 MindItemModel::~MindItemModel() {}
0049 
0050 int MindItemModel::rowCount(const QModelIndex& parent) const
0051 {
0052     // qDebug() << "rowCount :";
0053     if(parent.isValid())
0054         return 0;
0055     // qDebug() << "links" << m_links.size() << "node:" << m_nodes.size();
0056     return static_cast<int>(m_links.size() + m_packages.size() + m_nodes.size());
0057 }
0058 
0059 QVariant MindItemModel::data(const QModelIndex& index, int role) const
0060 {
0061 
0062     if(!index.isValid())
0063         return QVariant();
0064 
0065     QVariant result;
0066 
0067     auto r= index.row();
0068     auto linkCount= static_cast<int>(m_links.size());
0069     auto packageCount= static_cast<int>(m_packages.size());
0070     auto nodeCount= static_cast<int>(m_nodes.size());
0071 
0072     MindItem* mindNode= nullptr;
0073 
0074     if(r < linkCount)
0075     {
0076         mindNode= m_links[r];
0077     }
0078     else
0079     {
0080         r-= linkCount;
0081         if(r < packageCount)
0082             mindNode= m_packages[r];
0083         else
0084         {
0085             r-= packageCount;
0086             if(r < nodeCount)
0087                 mindNode= m_nodes[r];
0088         }
0089     }
0090 
0091     if(!mindNode)
0092         return {};
0093 
0094     if(role == Qt::DisplayRole)
0095         role= Label;
0096 
0097     QSet<int> allowedRole{Visible, Label, Selected, Type, Uuid, Object, HasPicture};
0098 
0099     if(!allowedRole.contains(role))
0100         return {};
0101 
0102     switch(role)
0103     {
0104     case Visible:
0105         result= mindNode->isVisible();
0106         break;
0107     case Label:
0108         result= mindNode->text().isEmpty() ? QString("Node") : mindNode->text();
0109         break;
0110     case Selected:
0111         result= mindNode->selected();
0112         break;
0113     case Type:
0114         result= mindNode->type();
0115         break;
0116     case Uuid:
0117         result= mindNode->id();
0118         break;
0119     case Object:
0120         result= QVariant::fromValue(mindNode);
0121         break;
0122     case HasPicture:
0123         result= mindNode->type() == MindItem::NodeType ? m_imgModel->hasPixmap(mindNode->id()) : false;
0124         break;
0125     }
0126     return result;
0127 }
0128 
0129 bool MindItemModel::setData(const QModelIndex& index, const QVariant& value, int role)
0130 {
0131     if(data(index, role) != value)
0132     {
0133         emit dataChanged(index, index, QVector<int>() << role);
0134         return true;
0135     }
0136     return false;
0137 }
0138 
0139 void MindItemModel::update(const QString& id, int role)
0140 {
0141     auto current= item(id);
0142 
0143     if(!current)
0144         return;
0145 
0146     int row= 0;
0147     if(current->type() == MindItem::LinkType)
0148     {
0149         auto it= std::find(std::begin(m_links), std::end(m_links), current);
0150         if(it != std::end(m_links))
0151             row= std::distance(std::begin(m_links), it);
0152     }
0153     else if(current->type() == MindItem::PackageType)
0154     {
0155         auto it= std::find(std::begin(m_packages), std::end(m_packages), current);
0156         if(it != std::end(m_packages))
0157             row= std::distance(std::begin(m_packages), it) + m_links.size();
0158     }
0159     else if(current->type() == MindItem::NodeType)
0160     {
0161         auto it= std::find(std::begin(m_nodes), std::end(m_nodes), current);
0162         if(it != std::end(m_nodes))
0163             row= std::distance(std::begin(m_nodes), it) + m_links.size() + m_packages.size();
0164     }
0165 
0166     emit dataChanged(index(row, 0), index(row, 0), {role});
0167 }
0168 
0169 void MindItemModel::setImageUriToNode(const QString& id, const QString& url)
0170 {
0171     auto it= std::find_if(m_nodes.begin(), m_nodes.end(), [id](const MindItem* node) { return node->id() == id; });
0172     if(it == m_nodes.end())
0173         return;
0174 
0175     auto dis= std::distance(m_nodes.begin(), it);
0176     auto node= dynamic_cast<MindNode*>(*it);
0177     if(!node)
0178         return;
0179 
0180     node->setImageUri(url);
0181     auto idx= index(dis, 0, QModelIndex());
0182     emit dataChanged(idx, idx, QVector<int>());
0183 }
0184 
0185 Qt::ItemFlags MindItemModel::flags(const QModelIndex& index) const
0186 {
0187     if(!index.isValid())
0188         return Qt::NoItemFlags;
0189 
0190     return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
0191 }
0192 
0193 QHash<int, QByteArray> MindItemModel::roleNames() const
0194 {
0195     static QHash<int, QByteArray> roles= {{MindItemModel::Label, "label"},
0196                                           {MindItemModel::Visible, "visible"},
0197                                           {MindItemModel::Selected, "selected"},
0198                                           {MindItemModel::Type, "type"},
0199                                           {MindItemModel::Uuid, "id"},
0200                                           {MindItemModel::Object, "object"},
0201                                           {MindItemModel::HasPicture, "hasPicture"}};
0202     return roles;
0203 }
0204 
0205 void MindItemModel::clear()
0206 {
0207 
0208     QVector<MindItem*> backup;
0209     backup.reserve(rowCount());
0210     for(auto const& l : m_links)
0211         backup.append(l);
0212 
0213     for(auto const& l : m_packages)
0214         backup.append(l);
0215 
0216     for(auto const& l : m_nodes)
0217         backup.append(l);
0218 
0219     beginResetModel();
0220     m_links.clear();
0221     m_packages.clear();
0222     m_nodes.clear();
0223     endResetModel();
0224 }
0225 
0226 void MindItemModel::appendItem(const QList<MindItem*>& nodes)
0227 {
0228     for(auto const& node : nodes)
0229     {
0230         if(node == nullptr)
0231             return;
0232 
0233         auto [vec, offset]= getVector(m_links, m_packages, m_nodes, node->type());
0234 
0235         int row= offset + vec.size();
0236 
0237         if(node->type() == MindItem::LinkType)
0238         {
0239 
0240             auto link= dynamic_cast<mindmap::LinkController*>(node);
0241             if(link)
0242             {
0243                 connect(link, &mindmap::LinkController::startPointChanged, this,
0244                         [this, link]()
0245                         {
0246                             QModelIndex parent;
0247                             auto it= std::find(m_links.begin(), m_links.end(), link);
0248                             if(it == m_links.end())
0249                                 return;
0250                             auto offset= std::distance(m_links.begin(), it);
0251                             auto idx1= index(offset, 0, parent);
0252                             auto start= link->start();
0253                             emit dataChanged(idx1, idx1,
0254                                              start->isDragged() ? QVector<int>{Object, LinkStartPosition} :
0255                                                                   QVector<int>{Object, LinkPositionFromSpacing});
0256                         });
0257             }
0258         }
0259         else
0260         {
0261             auto pItem= dynamic_cast<mindmap::PositionedItem*>(node);
0262             if(pItem)
0263             {
0264                 connect(pItem, &mindmap::PositionedItem::positionChanged, this, &MindItemModel::geometryChanged);
0265                 connect(pItem, &mindmap::PositionedItem::textChanged, this, &MindItemModel::geometryChanged);
0266                 connect(pItem, &mindmap::PositionedItem::textChanged, this, [pItem, this](){
0267                     auto [vec, offset]= getVector(m_links, m_packages, m_nodes, pItem->type());
0268                     auto row= offset + static_cast<int>(vec.size());
0269                     auto idx = index(row, 0);
0270                     emit dataChanged(idx,idx,{Roles::Label});
0271                 });
0272                 connect(pItem, &mindmap::PositionedItem::widthChanged, this, &MindItemModel::geometryChanged);
0273                 connect(pItem, &mindmap::PositionedItem::heightChanged, this, &MindItemModel::geometryChanged);
0274             }
0275         }
0276 
0277         beginInsertRows(QModelIndex(), row, row);
0278         vec.push_back(node);
0279         endInsertRows();
0280     }
0281 }
0282 std::vector<PositionedItem*> MindItemModel::positionnedItems() const
0283 {
0284     std::vector<PositionedItem*> vec;
0285     vec.reserve(m_packages.size() + m_nodes.size());
0286 
0287     for(auto const& item : m_packages)
0288     {
0289         auto pack= dynamic_cast<PositionedItem*>(item);
0290 
0291         if(!pack)
0292             continue;
0293 
0294         vec.push_back(pack);
0295     }
0296 
0297     for(auto const& item : m_nodes)
0298     {
0299         auto pack= dynamic_cast<PositionedItem*>(item);
0300 
0301         if(!pack)
0302             continue;
0303 
0304         vec.push_back(pack);
0305     }
0306 
0307     return vec;
0308 }
0309 
0310 std::pair<MindItem*, LinkController*> MindItemModel::addItem(const QString& idparent, MindItem::Type type)
0311 {
0312 
0313     auto [vec, offset]= getVector(m_links, m_packages, m_nodes, type);
0314 
0315     auto row= offset + static_cast<int>(vec.size());
0316     Q_UNUSED(row)
0317 
0318     std::pair<MindItem*, LinkController*> result;
0319 
0320     if(type == MindItem::NodeType)
0321     {
0322 
0323         auto root= new MindNode();
0324         root->setStyleIndex(defaultStyleIndex());
0325         appendItem({root});
0326 
0327         if(idparent.isEmpty())
0328             return {root, nullptr};
0329 
0330         auto data= positionnedItems();
0331         auto id= std::find_if(data.begin(), data.end(),
0332                               [idparent](const PositionedItem* node) { return idparent == node->id(); });
0333         if(id == data.end())
0334             return {root, nullptr};
0335 
0336         auto rectParent= (*id)->boundingRect();
0337         auto pos= rectParent.topLeft() + QPointF(rectParent.width() * 1.5, rectParent.height() * 1.5);
0338         root->setPosition(pos);
0339 
0340         auto link= new mindmap::LinkController();
0341         link->setStart((*id));
0342         link->setEnd(root);
0343         root->setParentNode(*id);
0344 
0345         appendItem({link});
0346 
0347         result= std::make_pair(root, link);
0348     }
0349     else if(type == MindItem::PackageType)
0350     {
0351         auto pack= new PackageNode();
0352         appendItem({pack});
0353         emit latestInsertedPackage(pack);
0354         result= std::make_pair(pack, nullptr);
0355     }
0356     else if(type == MindItem::LinkType)
0357     {
0358         auto link= new LinkController();
0359         appendItem({link});
0360         result= std::make_pair(link, link);
0361     }
0362 
0363     return result;
0364 }
0365 
0366 bool MindItemModel::removeItem(const MindItem* node)
0367 {
0368     if(node == nullptr)
0369         return false;
0370 
0371     auto [vec, offset]= getVector(m_links, m_packages, m_nodes, node->type());
0372 
0373     auto it= std::find(vec.begin(), vec.end(), node);
0374 
0375     if(it == std::end(m_nodes))
0376         return false;
0377 
0378     auto idx= offset + static_cast<int>(std::distance(vec.begin(), it));
0379 
0380     // qDebug() << "Indexremove item:" << idx << node->type();
0381 
0382     if(node->type() == MindItem::LinkType)
0383     {
0384 
0385         auto const link= dynamic_cast<const mindmap::LinkController*>(node);
0386         if(link)
0387         {
0388             disconnect(link, 0, this, 0);
0389             /*connect(link, &mindmap::LinkController::startPointChanged, this, [this, link]() {
0390                 QModelIndex parent;
0391                 auto it= std::find(m_links.begin(), m_links.end(), link);
0392                 if(it == m_links.end())
0393                     return;
0394                 auto offset= std::distance(m_links.begin(), it);
0395                 auto idx1= index(offset, 0, parent);
0396                 emit dataChanged(idx1, idx1, QVector<int>());
0397             });*/
0398         }
0399     }
0400 
0401     beginRemoveRows(QModelIndex(), idx, idx);
0402     vec.erase(it);
0403     endRemoveRows();
0404 
0405     return true;
0406 }
0407 
0408 void MindItemModel::openItem(const QString& id, bool status)
0409 {
0410     auto it= item(id);
0411 
0412     if(nullptr == it)
0413         return;
0414 
0415     auto node= dynamic_cast<PositionedItem*>(it);
0416 
0417     if(nullptr == node)
0418         return;
0419 
0420     if(node->open() == status)
0421         return;
0422 
0423     node->setOpen(status);
0424 }
0425 
0426 MindItem* MindItemModel::item(const QString& id) const
0427 {
0428     // MindItem* result= nullptr;
0429 
0430     auto it= std::find_if(m_nodes.begin(), m_nodes.end(), [id](const MindItem* node) { return node->id() == id; });
0431 
0432     if(it != m_nodes.end())
0433         return *it;
0434 
0435     auto it2= std::find_if(m_links.begin(), m_links.end(), [id](const MindItem* node) { return node->id() == id; });
0436 
0437     if(it2 != m_links.end())
0438         return *it2;
0439 
0440     auto it3
0441         = std::find_if(m_packages.begin(), m_packages.end(), [id](const MindItem* node) { return node->id() == id; });
0442 
0443     if(it3 != m_packages.end())
0444         return *it3;
0445 
0446     return nullptr;
0447 }
0448 
0449 PositionedItem* MindItemModel::positionItem(const QString& id) const
0450 {
0451     auto it= std::find_if(m_nodes.begin(), m_nodes.end(), [id](const MindItem* node) { return node->id() == id; });
0452 
0453     if(it != m_nodes.end())
0454         return dynamic_cast<PositionedItem*>(*it);
0455 
0456     auto it3
0457         = std::find_if(m_packages.begin(), m_packages.end(), [id](const MindItem* node) { return node->id() == id; });
0458 
0459     if(it3 != m_packages.end())
0460         return dynamic_cast<PositionedItem*>(*it3);
0461 
0462     return nullptr;
0463 }
0464 
0465 QRectF MindItemModel::contentRect() const
0466 {
0467     QRectF rect(0., 0., 1., 1.);
0468 
0469     for(auto const& item : m_nodes)
0470     {
0471         auto node= dynamic_cast<PositionedItem*>(item);
0472         if(!node)
0473             continue;
0474         rect= node->boundingRect().united(rect);
0475     }
0476 
0477     for(auto const& item : m_packages)
0478     {
0479         auto pack= dynamic_cast<PositionedItem*>(item);
0480         if(!pack)
0481             continue;
0482         rect= pack->boundingRect().united(rect);
0483     }
0484     return rect;
0485 }
0486 
0487 std::vector<MindItem*>& MindItemModel::items(MindItem::Type type)
0488 {
0489     return std::get<0>(getVector(m_links, m_packages, m_nodes, type));
0490 }
0491 
0492 std::vector<LinkController*> MindItemModel::sublink(const QString& id) const
0493 {
0494     std::vector<LinkController*> vec;
0495 
0496     for(auto item : m_links)
0497     {
0498         auto link= dynamic_cast<LinkController*>(item);
0499         if(!link)
0500             continue;
0501 
0502         auto st= link->start();
0503         if(!st)
0504             continue;
0505 
0506         if(id == st->id() && (std::end(vec) == std::find(std::begin(vec), std::end(vec), link)))
0507             vec.push_back(link);
0508 
0509         auto end= link->end();
0510         if(!end)
0511             continue;
0512 
0513         if(id == end->id() && (std::end(vec) == std::find(std::begin(vec), std::end(vec), link)))
0514             vec.push_back(link);
0515     }
0516 
0517     return vec;
0518 }
0519 
0520 int MindItemModel::defaultStyleIndex() const
0521 {
0522     return m_defaultStyleIndex;
0523 }
0524 
0525 void MindItemModel::setDefaultStyleIndex(int newDefaultStyleIndex)
0526 {
0527     if (m_defaultStyleIndex == newDefaultStyleIndex)
0528         return;
0529     m_defaultStyleIndex = newDefaultStyleIndex;
0530     emit defaultStyleIndexChanged();
0531 }
0532 
0533 } // namespace mindmap