| 1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. |
| 2 | // |
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 4 | |
| 5 | #include "projectgenerator.h" |
| 6 | #include "projectservice.h" |
| 7 | #include "window/windowservice.h" |
| 8 | #include "window/windowelement.h" |
| 9 | |
| 10 | #include <QFileDialog> |
| 11 | |
| 12 | /*! |
| 13 | * \brief The ProjectGenerator class 工程生成器对象 |
| 14 | * 该类主要功能为生成各类工程提供接口, 该类与ProjectService接口分开 |
| 15 | * 达到接口隔离和适应插件组合变化 |
| 16 | * 模式1. 仅仅包含ProjectGenerator的插件(独立功能的插件) |
| 17 | * 模式2. 包含ProjectGenerator、主界面框架组件发布、流程执行(倾向于业务流程) |
| 18 | * ProjectService 面向工程核心(ProjectCore) 提供对外接口 |
| 19 | * ProjectGenerator 面向工程扩展实现其他插件对外的接口 |
| 20 | * ProjectCore <-> ProjectService <-> [outside plugin] |
| 21 | */ |
| 22 | |
| 23 | |
| 24 | QStringList dpfservice::ProjectGenerator::supportLanguages() |
| 25 | { |
| 26 | return {}; |
| 27 | } |
| 28 | |
| 29 | QStringList dpfservice::ProjectGenerator::supportFileNames() |
| 30 | { |
| 31 | return {}; |
| 32 | } |
| 33 | |
| 34 | QAction *dpfservice::ProjectGenerator::openProjectAction(const QString &language, |
| 35 | const QString &actionText) |
| 36 | { |
| 37 | auto result = new QAction(actionText); |
| 38 | QObject::connect(result, &QAction::triggered, [=](){ |
| 39 | QString iniPath = CustomPaths::user(CustomPaths::Flags::Configures) |
| 40 | + QDir::separator() + QString("project_record.support" ); |
| 41 | QSettings setting(iniPath, QSettings::IniFormat); |
| 42 | QString lastPath = setting.value(language + "-" + actionText).toString(); |
| 43 | if (lastPath.isEmpty()) { |
| 44 | lastPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); |
| 45 | } |
| 46 | |
| 47 | QFileDialog fileDialog; |
| 48 | fileDialog.setFileMode(QFileDialog::Directory); |
| 49 | fileDialog.setOption(QFileDialog::DontResolveSymlinks); |
| 50 | fileDialog.setWindowTitle(QFileDialog::tr("Open %0 Project Directory" ).arg(language)); |
| 51 | fileDialog.setDirectory(lastPath); |
| 52 | fileDialog.setWindowFlags(fileDialog.windowFlags() | Qt::WindowStaysOnTopHint); |
| 53 | if (fileDialog.exec() == QDialog::Accepted) { |
| 54 | QString workspace = fileDialog.selectedUrls().first().path(); |
| 55 | setting.setValue(language + "-" + actionText, workspace); // save open history |
| 56 | if (canOpenProject(actionText, language, workspace)) |
| 57 | doProjectOpen(language, actionText, workspace); |
| 58 | } |
| 59 | }); |
| 60 | return result; |
| 61 | } |
| 62 | |
| 63 | /*! |
| 64 | * \brief dpfservice::ProjectGenerator::projectIsOpened |
| 65 | * check project is opened, default can't reopen opened project |
| 66 | * \param kitName project kitName |
| 67 | * \param language project language |
| 68 | * \param projectPath to open project path |
| 69 | * \return is opened return true, else return false; |
| 70 | */ |
| 71 | bool dpfservice::ProjectGenerator::canOpenProject(const QString &kitName, |
| 72 | const QString &language, |
| 73 | const QString &workspace) |
| 74 | { |
| 75 | if (isOpenedProject(kitName, language, workspace)) |
| 76 | return false; |
| 77 | |
| 78 | QStringList fileNames = supportFileNames(); |
| 79 | if (!fileNames.isEmpty()) { |
| 80 | for (auto filename : fileNames) { |
| 81 | if (QDir(workspace).exists(filename)) |
| 82 | return true; |
| 83 | } |
| 84 | ContextDialog::ok(QDialog::tr("Cannot open the project!\n" |
| 85 | "not exists support files: %0" ) |
| 86 | .arg(supportFileNames().join("," ))); |
| 87 | return false; |
| 88 | } |
| 89 | return true; |
| 90 | } |
| 91 | |
| 92 | bool dpfservice::ProjectGenerator::isOpenedProject(const QString &kitName, |
| 93 | const QString &language, |
| 94 | const QString &workspace) |
| 95 | { |
| 96 | auto &ctx = dpfInstance.serviceContext(); |
| 97 | ProjectService *projectService = ctx.service<ProjectService>(ProjectService::name()); |
| 98 | auto proInfos = projectService->getAllProjectInfo(); |
| 99 | for (auto info : proInfos) { |
| 100 | if (info.kitName() == kitName |
| 101 | && info.language() == language |
| 102 | && info.workspaceFolder() == workspace) { |
| 103 | return true; |
| 104 | } |
| 105 | } |
| 106 | return false; |
| 107 | } |
| 108 | |
| 109 | void dpfservice::ProjectGenerator::doProjectOpen(const QString &language, |
| 110 | const QString &actionText, |
| 111 | const QString &workspace) |
| 112 | { |
| 113 | using namespace dpfservice; |
| 114 | auto &ctx = dpfInstance.serviceContext(); |
| 115 | ProjectService *projectService = ctx.service<ProjectService>(ProjectService::name()); |
| 116 | |
| 117 | if (!projectService) |
| 118 | return; |
| 119 | |
| 120 | auto generator = projectService->createGenerator<ProjectGenerator>(actionText); |
| 121 | if (!generator) |
| 122 | return; |
| 123 | |
| 124 | auto widget = generator->configureWidget(language, workspace); |
| 125 | if (widget) { |
| 126 | widget->exec(); |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | /*! |
| 131 | * \brief configure 生成器配置界面,子类需要重载实现 |
| 132 | * 按照一般的做法,当前界面应当生成配置文件与工程路径绑定, |
| 133 | * 在界面上选中、输入信息存入配置文件 |
| 134 | * \param projectPath 工程路径 |
| 135 | * \return 返回工程配置界面 |
| 136 | */ |
| 137 | QDialog *dpfservice::ProjectGenerator::configureWidget(const QString &language, const QString &workspace) |
| 138 | { |
| 139 | Q_UNUSED(language) |
| 140 | Q_UNUSED(workspace) |
| 141 | return nullptr; |
| 142 | } |
| 143 | |
| 144 | /*! |
| 145 | * \brief configure 执行生成器的过程 |
| 146 | * 按照生成器配置界面约定规则,实现执行过程, |
| 147 | * 例如在cmake中,需要执行configure指令生成cache等相关文件 |
| 148 | * 执行该函数,应当首先确保前置条件满足(配置文件已生成)。 |
| 149 | * \param projectPath 工程路径 |
| 150 | * \return 过程执行结果 |
| 151 | */ |
| 152 | bool dpfservice::ProjectGenerator::configure(const dpfservice::ProjectInfo &projectInfo) { |
| 153 | |
| 154 | // "filePath", "kitName", "language", "workspace" |
| 155 | recent.saveOpenedProject(projectInfo.kitName(), |
| 156 | projectInfo.language(), |
| 157 | projectInfo.workspaceFolder()); |
| 158 | navigation.doSwitch(MWNA_EDIT); |
| 159 | |
| 160 | editor.switchWorkspace(MWCWT_PROJECTS); |
| 161 | |
| 162 | if (!projectInfo.workspaceFolder().isEmpty()) { |
| 163 | dpf::Event event; |
| 164 | event.setTopic(T_COLLABORATORS); |
| 165 | event.setData(D_OPEN_REPOS); |
| 166 | event.setProperty(P_WORKSPACEFOLDER, projectInfo.workspaceFolder()); |
| 167 | dpf::EventCallProxy::instance().pubEvent(event); |
| 168 | } |
| 169 | |
| 170 | Generator::started(); // emit starded |
| 171 | |
| 172 | return false; |
| 173 | } |
| 174 | |
| 175 | /*! |
| 176 | * \brief createRootItem 创建文件树路径,子类需要重载实现 |
| 177 | * 执行该函数应当首先确定前置条件的满足,比如已经执行了生成器的过程。 |
| 178 | * \param ProjectInfo 工程信息 |
| 179 | * \return |
| 180 | */ |
| 181 | QStandardItem *dpfservice::ProjectGenerator::createRootItem(const dpfservice::ProjectInfo &info) |
| 182 | { |
| 183 | QIcon icon = CustomIcons::icon(info.workspaceFolder()); |
| 184 | QString displyName = QFileInfo(info.workspaceFolder()).fileName(); |
| 185 | QString tooltip = info.workspaceFolder(); |
| 186 | auto rootItem = new QStandardItem(icon, displyName); |
| 187 | rootItem->setToolTip(tooltip); |
| 188 | return rootItem; |
| 189 | } |
| 190 | |
| 191 | /*! |
| 192 | * \brief removeRootItem 删除文件树根节点,子类需要重载实现 |
| 193 | * 执行函数如果存在异步加载的情况,则需要通过该接口实现异步释放 |
| 194 | * \param info |
| 195 | */ |
| 196 | void dpfservice::ProjectGenerator::removeRootItem(QStandardItem *root) |
| 197 | { |
| 198 | Q_UNUSED(root) |
| 199 | } |
| 200 | |
| 201 | /*! |
| 202 | * \brief createIndexMenu 文件树index点击时触发,子类需要重载实现 |
| 203 | * 文件树中右键触发的创建Menu,传入树中QModelIndex,它与Item数据相通(Qt规范) |
| 204 | * QMenu对象在插件使用方会被释放 |
| 205 | * \param index 索引数据 |
| 206 | * \return |
| 207 | */ |
| 208 | QMenu *dpfservice::ProjectGenerator::createItemMenu(const QStandardItem *item) |
| 209 | { |
| 210 | Q_UNUSED(item) |
| 211 | return nullptr; |
| 212 | } |
| 213 | |
| 214 | /*! |
| 215 | * \brief root 获取子节点的根节点 |
| 216 | * \param child 子节点 |
| 217 | * \return 根节点 |
| 218 | */ |
| 219 | QStandardItem *dpfservice::ProjectGenerator::root(QStandardItem *child) |
| 220 | { |
| 221 | if (!child) |
| 222 | return nullptr; |
| 223 | QStandardItem *parent = child->parent(); |
| 224 | if (parent) |
| 225 | return root(parent); |
| 226 | return child; |
| 227 | } |
| 228 | |
| 229 | /*! |
| 230 | * \brief root 获取子节点的根节点 |
| 231 | * \param child 子节点 |
| 232 | * \return 根节点 |
| 233 | */ |
| 234 | const QModelIndex dpfservice::ProjectGenerator::root(const QModelIndex &child) |
| 235 | { |
| 236 | if (!child.isValid()) |
| 237 | return {}; |
| 238 | const QModelIndex parent = child.parent(); |
| 239 | if (parent.isValid()) |
| 240 | return root(parent); |
| 241 | return child; |
| 242 | } |
| 243 | |