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 | |