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