File indexing completed on 2024-05-19 05:42:22
0001 // ct_lvtqtwc_tool_add_logical_relation.cpp -*-C++-*- 0002 0003 /* 0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk> 0005 // SPDX-License-Identifier: Apache-2.0 0006 // 0007 // Licensed under the Apache License, Version 2.0 (the "License"); 0008 // you may not use this file except in compliance with the License. 0009 // You may obtain a copy of the License at 0010 // 0011 // http://www.apache.org/licenses/LICENSE-2.0 0012 // 0013 // Unless required by applicable law or agreed to in writing, software 0014 // distributed under the License is distributed on an "AS IS" BASIS, 0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0016 // See the License for the specific language governing permissions and 0017 // limitations under the License. 0018 */ 0019 0020 #include <ct_lvtqtc_tool_add_logical_relation.h> 0021 0022 #include <ct_lvtqtc_graphicsscene.h> 0023 #include <ct_lvtqtc_graphicsview.h> 0024 #include <ct_lvtqtc_iconhelpers.h> 0025 #include <ct_lvtqtc_lakosentity.h> 0026 #include <ct_lvtqtc_undo_add_edge.h> 0027 #include <ct_lvtqtc_util.h> 0028 0029 #include <ct_lvtldr_lakosiannode.h> 0030 #include <ct_lvtldr_nodestorage.h> 0031 #include <ct_lvtldr_typenode.h> 0032 0033 #include <ct_lvtshr_functional.h> 0034 0035 #include <QDebug> 0036 #include <QMessageBox> 0037 0038 using namespace Codethink::lvtldr; 0039 0040 namespace Codethink::lvtqtc { 0041 0042 struct ToolAddLogicalRelation::Private { 0043 NodeStorage& nodeStorage; 0044 0045 explicit Private(NodeStorage& nodeStorage): nodeStorage(nodeStorage) 0046 { 0047 } 0048 }; 0049 0050 ToolAddLogicalRelation::ToolAddLogicalRelation(const QString& name, 0051 const QString& tooltip, 0052 const QIcon& icon, 0053 GraphicsView *gv, 0054 Codethink::lvtldr::NodeStorage& nodeStorage): 0055 EdgeBasedTool(name, tooltip, icon, gv), d(std::make_unique<Private>(nodeStorage)) 0056 { 0057 } 0058 0059 ToolAddLogicalRelation::~ToolAddLogicalRelation() = default; 0060 0061 bool ToolAddLogicalRelation::doRun(LakosEntity *fromItem, LakosEntity *toItem, lvtshr::LakosRelationType type) 0062 { 0063 auto *fromTypeNode = dynamic_cast<TypeNode *>(fromItem->internalNode()); 0064 auto *toTypeNode = dynamic_cast<TypeNode *>(toItem->internalNode()); 0065 0066 auto result = d->nodeStorage.addLogicalRelation(fromTypeNode, toTypeNode, type); 0067 bool needsParentDependencies = false; 0068 if (result.has_error()) { 0069 using Kind = ErrorAddLogicalRelation::Kind; 0070 0071 switch (result.error().kind) { 0072 case Kind::InvalidRelation: { 0073 Q_EMIT sendMessage(tr("Cannot create logical relation between those entities"), KMessageWidget::Error); 0074 return false; 0075 } 0076 case Kind::InvalidLakosRelationType: { 0077 assert(false && "Unexpected bad LogicalRelation Tool implementation"); 0078 return false; 0079 } 0080 case Kind::SelfRelation: { 0081 Q_EMIT sendMessage(tr("Cannot create self-dependency"), KMessageWidget::Error); 0082 return false; 0083 } 0084 case Kind::AlreadyHaveDependency: { 0085 Q_EMIT sendMessage(tr("Those elements already have a dependency"), KMessageWidget::Error); 0086 return false; 0087 } 0088 case Kind::ParentDependencyRequired: { 0089 Q_EMIT sendMessage( 0090 tr("Cannot create logical entity dependency without the parent entities having a dependency"), 0091 KMessageWidget::Error); 0092 return false; 0093 } 0094 case Kind::ComponentDependencyRequired: { 0095 // do not show message box in debug mode. assume "yes". 0096 if (property("debug").isValid()) { 0097 needsParentDependencies = true; 0098 } else { 0099 auto ret = QMessageBox::question( 0100 graphicsView(), 0101 tr("Automatically add Dependencies?"), 0102 tr("Parents are not connected, should we add the dependencies automatically?")); 0103 if (ret == QMessageBox::No) { 0104 Q_EMIT sendMessage(tr("We can only add a dependency if the parents are conected"), 0105 KMessageWidget::Error); 0106 return false; 0107 } 0108 needsParentDependencies = true; 0109 break; 0110 } 0111 } 0112 } 0113 } 0114 0115 if (needsParentDependencies) { 0116 auto hierarchy = calculateHierarchy(fromItem, toItem); 0117 auto *macro = new QUndoCommand("Multiple Add Edges"); 0118 for (const auto& pair : hierarchy) { 0119 const auto& iFrom = pair.first->internalNode(); 0120 const auto& iTo = pair.second->internalNode(); 0121 if (iFrom == iTo) { 0122 // Ignore if we find a common ancestor. For instance, if two different components are inside a common 0123 // package, do not try to connect the package to itself. 0124 continue; 0125 } 0126 0127 auto ret = d->nodeStorage.addPhysicalDependency(iFrom, iTo); 0128 if (ret.has_error()) { 0129 Q_EMIT sendMessage(tr("Error connecting toplevel parents"), KMessageWidget::Error); 0130 return false; 0131 } 0132 0133 // See the `macro` on the end the call, adding this on the child 0134 // list of the QUndoCommand parent. This is not leaking, Qt is just 0135 // weird sometimes. 0136 new UndoAddEdge(pair.first->qualifiedName(), 0137 pair.second->qualifiedName(), 0138 lvtshr::LakosRelationType::PackageDependency, 0139 QtcUtil::UndoActionType::e_Add, 0140 d->nodeStorage, 0141 macro); 0142 } 0143 0144 // try to add the edge again, now that the parents are ok. 0145 auto *fromNode = dynamic_cast<lvtldr::TypeNode *>(fromItem->internalNode()); 0146 auto *toNode = dynamic_cast<lvtldr::TypeNode *>(toItem->internalNode()); 0147 auto result = d->nodeStorage.addLogicalRelation(fromNode, toNode, type); 0148 0149 // should not have an error, since we tested before, but we still need to test. 0150 if (result.has_error()) { 0151 Q_EMIT sendMessage(tr("Error connecting toplevel parents"), KMessageWidget::Error); 0152 return false; 0153 } 0154 0155 new UndoAddEdge(fromNode->qualifiedName(), 0156 toNode->qualifiedName(), 0157 type, 0158 QtcUtil::UndoActionType::e_Add, 0159 d->nodeStorage, 0160 macro); 0161 0162 Q_EMIT undoCommandCreated(macro); 0163 } else { 0164 Q_EMIT undoCommandCreated(new UndoAddEdge(fromItem->qualifiedName(), 0165 toItem->qualifiedName(), 0166 type, 0167 QtcUtil::UndoActionType::e_Add, 0168 d->nodeStorage)); 0169 } 0170 return true; 0171 } 0172 0173 ToolAddIsARelation::ToolAddIsARelation(GraphicsView *gv, Codethink::lvtldr::NodeStorage& nodeStorage): 0174 ToolAddLogicalRelation(tr("Is A"), 0175 tr("Creates an 'isA' relation between two elements"), 0176 IconHelpers::iconFrom(":/icons/add_is_a"), 0177 gv, 0178 nodeStorage) 0179 { 0180 } 0181 0182 bool ToolAddIsARelation::run(LakosEntity *source, LakosEntity *target) 0183 { 0184 return doRun(source, target, lvtshr::IsA); 0185 } 0186 0187 ToolAddUsesInTheImplementation::ToolAddUsesInTheImplementation(GraphicsView *gv, 0188 Codethink::lvtldr::NodeStorage& nodeStorage): 0189 ToolAddLogicalRelation(tr("Uses In The Impl"), 0190 tr("Creates an 'Uses In The Implementation' relation between two elements"), 0191 IconHelpers::iconFrom(":/icons/add_uses_in_the_implementation"), 0192 gv, 0193 nodeStorage) 0194 { 0195 } 0196 0197 bool ToolAddUsesInTheImplementation::run(LakosEntity *source, LakosEntity *target) 0198 { 0199 return doRun(source, target, lvtshr::UsesInTheImplementation); 0200 } 0201 0202 ToolAddUsesInTheInterface::ToolAddUsesInTheInterface(GraphicsView *gv, Codethink::lvtldr::NodeStorage& nodeStorage): 0203 ToolAddLogicalRelation(tr("Uses In The Interface"), 0204 tr("Creates an 'Uses In The Interface' relation between two elements"), 0205 IconHelpers::iconFrom(":/icons/add_uses_in_the_interface"), 0206 gv, 0207 nodeStorage) 0208 { 0209 } 0210 0211 bool ToolAddUsesInTheInterface::run(LakosEntity *source, LakosEntity *target) 0212 { 0213 return doRun(source, target, lvtshr::UsesInTheInterface); 0214 } 0215 0216 } // namespace Codethink::lvtqtc