File indexing completed on 2024-05-19 05:40:36

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 "controller/view_controller/mindmapcontrollerbase.h"
0021 
0022 #include <QDebug>
0023 #include <QFile>
0024 #include <QJsonArray>
0025 #include <QJsonDocument>
0026 #include <QJsonObject>
0027 #include <QThread>
0028 #include <QUrl>
0029 #include <random>
0030 
0031 #include "mindmap/command/additemcommand.h"
0032 #include "mindmap/command/removeimagefromnodecommand.h"
0033 #include "mindmap/command/removenodecommand.h"
0034 #include "mindmap/command/reparentingnodecommand.h"
0035 #include "mindmap/controller/selectioncontroller.h"
0036 #include "mindmap/controller/spacingcontroller.h"
0037 #include "mindmap/data/mindnode.h"
0038 #include "mindmap/data/packagenode.h"
0039 #include "mindmap/data/positioneditem.h"
0040 #include "mindmap/model/imagemodel.h"
0041 #include "mindmap/model/nodestylemodel.h"
0042 #include "mindmap/worker/fileserializer.h"
0043 #include "mindmap/command/addimagetonodecommand.h"
0044 
0045 namespace mindmap
0046 {
0047 MindMapControllerBase::MindMapControllerBase(bool hasNetwork, const QString& id, QObject* parent)
0048     : MediaControllerBase(id, Core::ContentType::MINDMAP, parent)
0049     , m_selectionController(new SelectionController())
0050     , m_imgModel(new mindmap::ImageModel)
0051     , m_itemModel(new MindItemModel(m_imgModel.get()))
0052     , m_styleModel(new NodeStyleModel())
0053     , m_hasNetwork(hasNetwork)
0054 {
0055     m_selectionController->setUndoStack(&m_stack);
0056 
0057     connect(&m_stack, &QUndoStack::canRedoChanged, this, &MindMapControllerBase::canRedoChanged);
0058     connect(&m_stack, &QUndoStack::canUndoChanged, this, &MindMapControllerBase::canUndoChanged);
0059     connect(m_itemModel.get(), &MindItemModel::defaultStyleIndexChanged, this, &MindMapControllerBase::defaultStyleIndexChanged);
0060 
0061     connect(m_itemModel.get(), &MindItemModel::latestInsertedPackage, this, &MindMapControllerBase::setCurrentPackage);
0062     connect(m_itemModel.get(), &MindItemModel::rowsInserted, this,
0063             [this]() { setContentRect(m_itemModel->contentRect()); });
0064     connect(m_itemModel.get(), &MindItemModel::rowsRemoved, this,
0065             [this]() { setContentRect(m_itemModel->contentRect()); });
0066     connect(m_itemModel.get(), &MindItemModel::geometryChanged, this,
0067             [this]() { setContentRect(m_itemModel->contentRect()); });
0068 
0069     connect(m_selectionController.get(), &SelectionController::hasSelectionChanged, this,
0070             &MindMapControllerBase::hasSelectionChanged);
0071 
0072     // Spacing
0073     m_spacing.reset(new QThread());
0074     m_spacingController.reset(new SpacingController(m_itemModel.get()));
0075     m_spacingController->moveToThread(m_spacing.get());
0076     connect(m_spacing.get(), &QThread::started, m_spacingController.get(), &SpacingController::computeInLoop);
0077     connect(m_spacingController.get(), &SpacingController::runningChanged, this,
0078             &MindMapControllerBase::spacingChanged);
0079     connect(m_spacingController.get(), &SpacingController::runningChanged, this, [this]() {
0080         if(m_spacingController->running())
0081             m_spacing->start();
0082     });
0083     connect(m_spacingController.get(), &SpacingController::finished, m_spacing.get(), &QThread::quit);
0084     m_spacing->start();
0085     // end Spacing
0086 
0087     m_contentRect= m_itemModel->contentRect();
0088 
0089     resetData();
0090 }
0091 
0092 MindMapControllerBase::~MindMapControllerBase()
0093 {
0094     if(m_spacing && m_spacing->isRunning())
0095     {
0096         m_spacingController->setRunning(false);
0097         m_spacing->quit();
0098         m_spacing->wait();
0099         m_spacing.release()->deleteLater();
0100     }
0101 
0102     auto model= m_itemModel.release();
0103     auto spacingCtrl= m_spacingController.release();
0104 
0105     delete spacingCtrl;
0106     delete model;
0107 }
0108 
0109 MindItemModel* MindMapControllerBase::itemModel() const
0110 {
0111     return m_itemModel.get();
0112 }
0113 
0114 QAbstractItemModel* MindMapControllerBase::styleModel() const
0115 {
0116     return m_styleModel.get();
0117 }
0118 
0119 ImageModel* MindMapControllerBase::imgModel() const
0120 {
0121     return m_imgModel.get();
0122 }
0123 
0124 mindmap::SpacingController* MindMapControllerBase::spacingCtrl() const
0125 {
0126     return m_spacingController.get();
0127 }
0128 
0129 const QString& MindMapControllerBase::errorMsg() const
0130 {
0131     return m_errorMsg;
0132 }
0133 
0134 QRectF MindMapControllerBase::contentRect() const
0135 {
0136     return m_contentRect;
0137 }
0138 
0139 void MindMapControllerBase::clearData()
0140 {
0141     // m_linkModel->clear();
0142     m_itemModel->clear();
0143 }
0144 
0145 void MindMapControllerBase::resetData()
0146 {
0147     clearData();
0148 
0149     auto root= new MindNode();
0150     root->setText(tr("Root"));
0151     m_itemModel->appendItem({root});
0152 }
0153 
0154 void MindMapControllerBase::setErrorMsg(const QString& msg)
0155 {
0156     if(m_errorMsg == msg)
0157         return;
0158     m_errorMsg= msg;
0159     emit errorMsgChanged();
0160 }
0161 
0162 void MindMapControllerBase::importFile(const QString& path)
0163 {
0164     clearData();
0165     /*  if(!FileSerializer::readTextFile(m_itemModel.get(), path))
0166           setErrorMsg(tr("File can't be loaded: %1").arg(m_filename));*/
0167 }
0168 
0169 void MindMapControllerBase::setDefaultStyleIndex(int indx)
0170 {
0171     m_itemModel->setDefaultStyleIndex(indx);
0172 }
0173 
0174 NodeStyle* MindMapControllerBase::style(int index) const
0175 {
0176     return m_styleModel->getStyle(index);
0177 }
0178 
0179 void MindMapControllerBase::setSpacing(bool status)
0180 {
0181     m_spacingController->setRunning(status);
0182 }
0183 
0184 void MindMapControllerBase::redo()
0185 {
0186     m_stack.redo();
0187 }
0188 
0189 void MindMapControllerBase::undo()
0190 {
0191     m_stack.undo();
0192 }
0193 
0194 bool MindMapControllerBase::spacing() const
0195 {
0196     return m_spacingController->running();
0197 }
0198 
0199 SelectionController* MindMapControllerBase::selectionController() const
0200 {
0201     return m_selectionController.get();
0202 }
0203 
0204 bool MindMapControllerBase::canRedo() const
0205 {
0206     return m_stack.canRedo();
0207 }
0208 
0209 void MindMapControllerBase::addNode(const QString& idparent)
0210 {
0211     auto cmd= new mindmap::AddItemCommand(m_itemModel.get(), MindItem::NodeType, idparent);
0212     m_stack.push(cmd);
0213 }
0214 
0215 void MindMapControllerBase::centerItems(qreal w, qreal h)
0216 {
0217     auto isSpacing= spacing();
0218     if(isSpacing)
0219         setSpacing(false);
0220 
0221     auto rect= contentRect();
0222     auto viewport= QRectF{0, 0, w, h};
0223 
0224     auto vec= viewport.center() - rect.center();
0225 
0226     auto items= m_itemModel->positionnedItems();
0227 
0228     for(auto& item : qAsConst(items))
0229     {
0230         item->setPosition(item->position() + vec);
0231     }
0232 
0233     if(isSpacing)
0234         setSpacing(true);
0235 }
0236 
0237 void MindMapControllerBase::addImageFor(const QString& idNode, const QString& path, const QByteArray& data)
0238 {
0239     m_stack.push(new mindmap::AddImageToNodeCommand(m_itemModel.get(), m_imgModel.get(), idNode, path, data));
0240 }
0241 
0242 void MindMapControllerBase::removeImageFor(const QString& nodeId)
0243 {
0244     m_stack.push(new mindmap::RemoveImageFromNodeCommand(m_itemModel.get(), m_imgModel.get(), nodeId));
0245 }
0246 
0247 void MindMapControllerBase::refresh()
0248 {
0249     emit contentRectChanged();
0250 }
0251 
0252 void MindMapControllerBase::openImage(const QString& id, const QUrl& path)
0253 {
0254     QPixmap map(path.toLocalFile());
0255 
0256     if(map.isNull())
0257         return;
0258 
0259     m_imgModel->insertPixmap(id, map, path);
0260     m_itemModel->update(id, MindItemModel::HasPicture);
0261 }
0262 
0263 void MindMapControllerBase::removeImage(const QString& id)
0264 {
0265     m_imgModel->removePixmap(id);
0266 }
0267 
0268 void MindMapControllerBase::removeLink(const QStringList& id)
0269 {
0270     std::vector<MindItem*> res;
0271     std::transform(std::begin(id), std::end(id), std::back_inserter(res),
0272                    [this](const QString& id) { return m_itemModel->item(id); });
0273 
0274     res.erase(std::remove(std::begin(res), std::end(res), nullptr),
0275                  std::end(res));
0276 
0277     if(res.empty())
0278         return;
0279 
0280     auto cmd= new mindmap::RemoveNodeCommand(uuid(), res, m_itemModel.get());
0281     m_stack.push(cmd);
0282 }
0283 
0284 void MindMapControllerBase::removeNode(const QStringList& id)
0285 {
0286     std::vector<MindItem*> res;
0287     std::transform(std::begin(id), std::end(id), std::back_inserter(res),
0288                    [this](const QString& id) { return m_itemModel->item(id); });
0289 
0290     res.erase(std::remove(std::begin(res), std::end(res), nullptr),
0291               std::end(res));
0292 
0293     if(res.empty())
0294         return;
0295 
0296     auto cmd= new mindmap::RemoveNodeCommand(uuid(), res, m_itemModel.get());
0297     m_stack.push(cmd);
0298 }
0299 
0300 void MindMapControllerBase::addPackage(const QPointF& pos)
0301 {
0302     auto cmd= new mindmap::AddItemCommand(m_itemModel.get(), MindItem::PackageType, {}, pos);
0303     m_stack.push(cmd);
0304 }
0305 
0306 void MindMapControllerBase::updatePackage(const QPointF& pos)
0307 {
0308     if(!m_package)
0309         return;
0310 
0311     auto offset= pos - m_package->position();
0312     m_package->setWidth(offset.x());
0313     m_package->setHeight(offset.y());
0314 }
0315 
0316 void MindMapControllerBase::addLink(const QString& start, const QString& id)
0317 {
0318     if(start == id || start.isEmpty() || id.isEmpty())
0319         return;
0320 
0321     auto cmd= new mindmap::AddLinkCommand(m_itemModel.get(), start, id);
0322     m_stack.push(cmd);
0323 }
0324 
0325 void MindMapControllerBase::addNode(const QList<MindNode*>& nodes, bool network)
0326 {
0327     QList<mindmap::MindItem*> items;
0328 
0329     std::transform(std::begin(nodes), std::end(nodes), std::back_inserter(items),
0330                    [](MindNode* node) -> mindmap::MindItem* { return node; });
0331 
0332     m_itemModel->appendItem(items);
0333 }
0334 
0335 bool MindMapControllerBase::pasteData(const QMimeData& mimeData)
0336 {
0337     return false;
0338 }
0339 
0340 void MindMapControllerBase::reparenting(MindItem* parent, const QString& id)
0341 {
0342     auto cmd= new ReparentingNodeCommand(m_itemModel.get(), dynamic_cast<PositionedItem*>(parent), id);
0343     m_stack.push(cmd);
0344 }
0345 void MindMapControllerBase::removeSelection()
0346 {
0347     auto nodes= m_selectionController->selectedNodes();
0348     auto cmd= new mindmap::RemoveNodeCommand(uuid(), nodes, m_itemModel.get());
0349     m_stack.push(cmd);
0350 }
0351 
0352 void MindMapControllerBase::setCurrentPackage(PositionedItem* item)
0353 {
0354     m_package= item;
0355 }
0356 
0357 bool MindMapControllerBase::canUndo() const
0358 {
0359     return m_stack.canUndo();
0360 }
0361 
0362 int MindMapControllerBase::defaultStyleIndex() const
0363 {
0364     return m_itemModel->defaultStyleIndex();
0365 }
0366 
0367 bool MindMapControllerBase::linkLabelVisibility() const
0368 {
0369     return m_linkLabelVisibility;
0370 }
0371 
0372 void MindMapControllerBase::setLinkLabelVisibility(bool newLinkLabelVisibility)
0373 {
0374     if(m_linkLabelVisibility == newLinkLabelVisibility)
0375         return;
0376     m_linkLabelVisibility= newLinkLabelVisibility;
0377     emit linkLabelVisibilityChanged();
0378 }
0379 
0380 bool MindMapControllerBase::hasSelection() const
0381 {
0382     return m_selectionController->hasSelection();
0383 }
0384 
0385 mindmap::MindNode* MindMapControllerBase::nodeFromId(const QString& id) const
0386 {
0387     return dynamic_cast<mindmap::MindNode*>(m_itemModel->item(id));
0388 }
0389 
0390 void MindMapControllerBase::addLink(const QList<LinkController*>& link, bool network) {}
0391 
0392 void MindMapControllerBase::addItemIntoPackage(const QString& idNode, const QString& idPack)
0393 {
0394     auto node= dynamic_cast<PositionedItem*>(m_itemModel->item(idNode));
0395     auto pack= dynamic_cast<PackageNode*>(m_itemModel->item(idPack));
0396 
0397     if(!node || !pack)
0398         return;
0399 
0400     pack->addChild(node);
0401     node->setLocked(true);
0402 }
0403 
0404 QObject* MindMapControllerBase::subItem(const QString& id, mindmap::MindItem::Type type) const
0405 {
0406     auto item= m_itemModel->item(id);
0407     if(!item)
0408         return nullptr;
0409     return item->type() == type ? item : nullptr;
0410 }
0411 
0412 void MindMapControllerBase::setContentRect(const QRectF& rect)
0413 {
0414     if(m_contentRect == rect || !qFuzzyCompare(m_zoomLevel, 1.))
0415         return;
0416     m_contentRect= rect;
0417     emit contentRectChanged();
0418 }
0419 
0420 qreal MindMapControllerBase::zoomLevel() const
0421 {
0422     return m_zoomLevel;
0423 }
0424 
0425 void MindMapControllerBase::setZoomLevel(qreal newZoomLevel)
0426 {
0427     if(qFuzzyCompare(m_zoomLevel, newZoomLevel))
0428         return;
0429     m_zoomLevel= newZoomLevel;
0430     emit zoomLevelChanged();
0431 }
0432 
0433 bool MindMapControllerBase::hasNetwork() const
0434 {
0435     return m_hasNetwork;
0436 }
0437 
0438 } // namespace mindmap