File indexing completed on 2024-05-19 05:42:22
0001 // ct_lvtqtw_tool_add_package.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_package.h> 0021 0022 #include <ct_lvtqtc_undo_add_package.h> 0023 0024 #include <ct_lvtqtc_graphicsscene.h> 0025 #include <ct_lvtqtc_graphicsview.h> 0026 #include <ct_lvtqtc_packageentity.h> 0027 #include <ct_lvtshr_functional.h> 0028 0029 #include <ct_lvtqtc_iconhelpers.h> 0030 #include <ct_lvtqtc_util.h> 0031 0032 #include <ct_lvtldr_nodestorage.h> 0033 0034 #include <QApplication> 0035 #include <QDebug> 0036 #include <QInputDialog> 0037 #include <QMessageBox> 0038 0039 #include <preferences.h> 0040 0041 using namespace Codethink::lvtldr; 0042 0043 namespace Codethink::lvtqtc { 0044 0045 struct ToolAddPackage::Private { 0046 NodeStorage& nodeStorage; 0047 0048 explicit Private(NodeStorage& nodeStorage): nodeStorage(nodeStorage) 0049 { 0050 } 0051 }; 0052 0053 ToolAddPackage::ToolAddPackage(GraphicsView *gv, NodeStorage& nodeStorage): 0054 BaseAddEntityTool(tr("Package"), tr("Creates a Package"), IconHelpers::iconFrom(":/icons/package"), gv), 0055 d(std::make_unique<Private>(nodeStorage)) 0056 { 0057 auto *inputDataDialog = inputDialog(); 0058 inputDataDialog->addTextField("name", tr("Name:")); 0059 inputDataDialog->finish(); 0060 } 0061 0062 ToolAddPackage::~ToolAddPackage() = default; 0063 0064 template<typename T> 0065 cpp::result<void, InvalidNameError> 0066 checkNameError(bool hasParent, const std::string& name, const std::string& parentName) 0067 { 0068 return T::checkName(hasParent, name, parentName); 0069 } 0070 0071 void ToolAddPackage::activate() 0072 { 0073 Q_EMIT sendMessage(tr("Click on an empty spot to add a new package group," 0074 "or on a package group to add a new package."), 0075 KMessageWidget::Information); 0076 0077 BaseAddEntityTool::activate(); 0078 } 0079 0080 void ToolAddPackage::mouseReleaseEvent(QMouseEvent *event) 0081 { 0082 qCDebug(LogTool) << name() << "Mouse Release Event"; 0083 0084 using Codethink::lvtshr::ScopeExit; 0085 ScopeExit _([&]() { 0086 deactivate(); 0087 }); 0088 0089 event->accept(); 0090 0091 auto *parentView = graphicsView()->itemByTypeAt<PackageEntity>(event->pos()); 0092 auto *parent = parentView ? parentView->internalNode() : nullptr; 0093 auto parentName = parent ? parent->name() : ""; 0094 auto parentQualifiedName = parent ? parent->qualifiedName() : ""; 0095 0096 m_nameDialog->setTextFieldValue("name", QString::fromStdString(parentName)); 0097 if (m_nameDialog->exec() == QDialog::Rejected) { 0098 return; 0099 } 0100 0101 // Verify if the names are correct / sane. 0102 const std::string name = std::any_cast<QString>(m_nameDialog->fieldValue("name")).toStdString(); 0103 if (Preferences::useLakosianRules()) { 0104 const auto result = checkNameError<LakosianNameRules>(parent != nullptr, name, parentName); 0105 if (result.has_error()) { 0106 Q_EMIT sendMessage(result.error().what, KMessageWidget::Error); 0107 return; 0108 } 0109 } 0110 0111 auto qualifiedName = parent ? parentName + "/" + name : name; 0112 auto *scene = qobject_cast<GraphicsScene *>(graphicsView()->scene()); 0113 0114 auto result = d->nodeStorage.addPackage(name, qualifiedName, parent, scene); 0115 if (result.has_error()) { 0116 using Kind = ErrorAddPackage::Kind; 0117 switch (result.error().kind) { 0118 case Kind::QualifiedNameAlreadyRegistered: { 0119 Q_EMIT sendMessage( 0120 tr("Qualified name already registered %1").arg(QString::fromStdString(result.error().what)), 0121 KMessageWidget::Error); 0122 return; 0123 } 0124 case Kind::CannotAddPackageToStandalonePackage: { 0125 Q_EMIT sendMessage( 0126 tr("Cannot add a package to a standalone package (because it already contains a component)"), 0127 KMessageWidget::Error); 0128 return; 0129 } 0130 case Kind::CantAddChildren: { 0131 Q_EMIT sendMessage(tr("Cannot add Children on the package"), KMessageWidget::Error); 0132 return; 0133 } 0134 } 0135 } 0136 auto *newPackage = result.value(); 0137 Q_EMIT sendMessage(QString(), KMessageWidget::Information); 0138 0139 const QPointF scenePos = graphicsView()->mapToScene(event->pos()); 0140 const QPointF itemPos = parentView ? parentView->mapFromScene(scenePos) : scenePos; 0141 scene->setEntityPos(newPackage->uid(), itemPos); 0142 scene->updateBoundingRect(); 0143 0144 Q_EMIT undoCommandCreated(new UndoAddPackage(scene, 0145 itemPos, 0146 name, 0147 qualifiedName, 0148 parentQualifiedName, 0149 QtcUtil::UndoActionType::e_Add, 0150 d->nodeStorage)); 0151 } 0152 0153 cpp::result<void, InvalidNameError> 0154 LakosianNameRules::checkName(bool hasParent, const std::string& name, const std::string& parentName) 0155 { 0156 const auto header = QObject::tr("BDE Guidelines Enforced: "); 0157 if (hasParent) { 0158 if (name.find('_') != std::string::npos) { 0159 return cpp::fail(InvalidNameError{header + QObject::tr("Package names should not contain underscore")}); 0160 } 0161 if (name.size() < 4) { 0162 return cpp::fail(InvalidNameError{ 0163 header + QObject::tr("Name too short, must be at least four letters (parent package(3) + name(1-5))")}); 0164 } 0165 0166 if (name.size() > 8) { 0167 return cpp::fail(InvalidNameError{ 0168 header + QObject::tr("Name too long, must be at most eight letters (parent package(3) + name(1-5))")}); 0169 } 0170 if (!QString::fromStdString(name).startsWith(QString::fromStdString(parentName))) { 0171 return cpp::fail(InvalidNameError{header 0172 + QObject::tr("the package name must start with the parent's name (%1)") 0173 .arg(QString::fromStdString(parentName))}); 0174 } 0175 } else if (name.size() != 3) { 0176 return cpp::fail(InvalidNameError{header + QObject::tr("top level packages must be three letters long.")}); 0177 } 0178 return {}; 0179 } 0180 0181 } // namespace Codethink::lvtqtc