1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. |
2 | // |
3 | // SPDX-License-Identifier: GPL-3.0-or-later |
4 | |
5 | #include "aboutdialog.h" |
6 | #include "plugindialog.h" |
7 | |
8 | #include "windowkeeper.h" |
9 | #include "windowstatusbar.h" |
10 | #include "services/window/windowservice.h" |
11 | #include "services/project/projectservice.h" |
12 | #include "common/common.h" |
13 | |
14 | #include <QAction> |
15 | #include <QMenu> |
16 | #include <QToolBar> |
17 | #include <QMenuBar> |
18 | #include <QStatusBar> |
19 | #include <QMainWindow> |
20 | #include <QFileDialog> |
21 | #include <QApplication> |
22 | #include <QActionGroup> |
23 | #include <QDesktopServices> |
24 | #include <QDesktopWidget> |
25 | #include <QComboBox> |
26 | #include <QTranslator> |
27 | #include <QScreen> |
28 | #include <QStandardPaths> |
29 | |
30 | static WindowKeeper *ins{nullptr}; |
31 | using namespace dpfservice; |
32 | class WindowKeeperPrivate |
33 | { |
34 | WindowKeeperPrivate(); |
35 | QHash<QString, QWidget *> centrals{}; |
36 | QMainWindow *window{nullptr}; |
37 | QActionGroup *navActionGroup{nullptr}; |
38 | QToolBar *toolbar{nullptr}; |
39 | |
40 | friend class WindowKeeper; |
41 | }; |
42 | |
43 | WindowKeeperPrivate::WindowKeeperPrivate() |
44 | { |
45 | |
46 | } |
47 | |
48 | void WindowKeeper::(QMenuBar *) |
49 | { |
50 | qInfo() << __FUNCTION__; |
51 | QMenu* = new QMenu(); |
52 | QAction* actionQuit = new QAction(MWMFA_QUIT); |
53 | ActionManager::getInstance()->registerAction(actionQuit, "File.Quit" , |
54 | MWMFA_QUIT, QKeySequence(Qt::Modifier::CTRL | Qt::Key::Key_Q), |
55 | ":/core/images/quit.png" ); |
56 | QAction::connect(actionQuit, &QAction::triggered, [](){ |
57 | qApp->closeAllWindows(); |
58 | }); |
59 | |
60 | QAction* actionOpenFile = new QAction(MWMFA_OPEN_FILE); |
61 | ActionManager::getInstance()->registerAction(actionOpenFile, "File.Open.File" , |
62 | MWMFA_OPEN_FILE, QKeySequence(Qt::Modifier::CTRL | Qt::Key::Key_O), |
63 | ":/core/images/open_file.png" ); |
64 | |
65 | QAction::connect(actionOpenFile, &QAction::triggered, [=](){ |
66 | QString dir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); |
67 | QString filePath = QFileDialog::getOpenFileName(nullptr, DIALOG_OPEN_DOCUMENT_TITLE, dir); |
68 | if (filePath.isEmpty() && !QFileInfo(filePath).exists()) |
69 | return; |
70 | recent.saveOpenedFile(filePath); |
71 | editor.openFile(filePath); |
72 | }); |
73 | |
74 | QMenu* = new QMenu(MWMFA_OPEN_PROJECT); |
75 | menuOpenProject->setIcon(QIcon(":/core/images/open_project.png" )); |
76 | |
77 | fileMenu->addAction(actionOpenFile); |
78 | fileMenu->addMenu(menuOpenProject); |
79 | fileMenu->addAction(actionQuit); |
80 | QAction* fileAction = menuBar->addMenu(fileMenu); |
81 | fileAction->setText(MWM_FILE); |
82 | } |
83 | |
84 | void WindowKeeper::(QMenuBar *) |
85 | { |
86 | qInfo() << __FUNCTION__; |
87 | QMenu* = new QMenu(); |
88 | QAction* buildAction = menuBar->addMenu(analyzeMenu); |
89 | buildAction->setText(MWM_ANALYZE); |
90 | } |
91 | |
92 | void WindowKeeper::(QMenuBar *) |
93 | { |
94 | qInfo() << __FUNCTION__; |
95 | QMenu* = new QMenu(); |
96 | QAction* buildAction = menuBar->addMenu(buildMenu); |
97 | buildAction->setText(MWM_BUILD); |
98 | } |
99 | |
100 | void WindowKeeper::(QMenuBar *) |
101 | { |
102 | qInfo() << __FUNCTION__; |
103 | QAction* debugAction = menuBar->addMenu(new QMenu()); |
104 | debugAction->setText(MWM_DEBUG); |
105 | } |
106 | |
107 | void WindowKeeper::(QMenuBar *) |
108 | { |
109 | qInfo() << __FUNCTION__; |
110 | auto = new QMenu(MWM_TOOLS); |
111 | menuBar->addMenu(toolsMenu); |
112 | } |
113 | |
114 | void WindowKeeper::(QMenuBar *) |
115 | { |
116 | qInfo() << __FUNCTION__; |
117 | auto = new QMenu(MWM_HELP); |
118 | menuBar->addMenu(helpMenu); |
119 | |
120 | QAction *actionReportBug = new QAction(MWM_REPORT_BUG); |
121 | ActionManager::getInstance()->registerAction(actionReportBug, "Help.Report.Bug" , |
122 | MWM_REPORT_BUG, QKeySequence(Qt::Modifier::CTRL | Qt::Modifier::SHIFT | Qt::Key::Key_R), |
123 | ":/core/images/tools-report-bug.png" ); |
124 | helpMenu->addAction(actionReportBug); |
125 | QAction *actionHelpDoc = new QAction(MWM_HELP_DOCUMENTS); |
126 | ActionManager::getInstance()->registerAction(actionHelpDoc, "Help.Help.Documents" , |
127 | MWM_HELP_DOCUMENTS, QKeySequence()); |
128 | helpMenu->addAction(actionHelpDoc); |
129 | |
130 | helpMenu->addSeparator(); |
131 | |
132 | QAction *actionAboutUnionCode = new QAction(MWM_ABOUT); |
133 | ActionManager::getInstance()->registerAction(actionAboutUnionCode, "Help.About" , |
134 | MWM_ABOUT, QKeySequence(Qt::Modifier::CTRL | Qt::Modifier::SHIFT | Qt::Key::Key_A), |
135 | ":/core/images/help-about.svg" ); |
136 | helpMenu->addAction(actionAboutUnionCode); |
137 | |
138 | QAction *actionAboutPlugin = new QAction(MWM_ABOUT_PLUGINS); |
139 | ActionManager::getInstance()->registerAction(actionAboutUnionCode, "Help.AboutPlugins" , MWM_ABOUT, QKeySequence()); |
140 | helpMenu->addAction(actionAboutPlugin); |
141 | |
142 | QAction::connect(actionReportBug, &QAction::triggered, [=](){ |
143 | QDesktopServices::openUrl(QUrl("https://pms.uniontech.com/project-bug-1039.html" )); |
144 | }); |
145 | QAction::connect(actionHelpDoc, &QAction::triggered, [=](){ |
146 | QDesktopServices::openUrl(QUrl("https://wiki.deepin.org/zh/05_HOW-TO/02_%E5%BC%80%E5%8F%91%E7%9B%B8%E5%85%B3/deepin-unioncode" )); |
147 | }); |
148 | QAction::connect(actionAboutUnionCode, &QAction::triggered, this, &WindowKeeper::showAboutDlg); |
149 | QAction::connect(actionAboutPlugin, &QAction::triggered, this, &WindowKeeper::showAboutPlugins); |
150 | } |
151 | |
152 | void WindowKeeper::createStatusBar(QMainWindow *window) |
153 | { |
154 | qInfo() << __FUNCTION__; |
155 | QStatusBar* statusBar = new WindowStatusBar(); |
156 | window->setStatusBar(statusBar); |
157 | } |
158 | |
159 | void WindowKeeper::createNavRecent(QToolBar *toolbar) |
160 | { |
161 | qInfo() << __FUNCTION__; |
162 | if (!toolbar) |
163 | return; |
164 | |
165 | QAction* navRecent = new QAction(QIcon(":/core/images/recent.png" ), MWNA_RECENT, toolbar); |
166 | navRecent->setCheckable(true); |
167 | d->navActionGroup->addAction(navRecent); |
168 | QAction::connect(navRecent, &QAction::triggered, [=](){ |
169 | WindowKeeper::switchWidgetNavigation(MWNA_RECENT); |
170 | }); |
171 | |
172 | toolbar->addAction(navRecent); |
173 | toolbar->widgetForAction(navRecent)->setObjectName("Recent" ); |
174 | } |
175 | |
176 | void WindowKeeper::createNavEdit(QToolBar *toolbar) |
177 | { |
178 | qInfo() << __FUNCTION__; |
179 | if (!toolbar) |
180 | return; |
181 | |
182 | QAction* navEdit = new QAction(QIcon(":/core/images/edit.png" ), MWNA_EDIT, toolbar); |
183 | navEdit->setCheckable(true); |
184 | d->navActionGroup->addAction(navEdit); |
185 | QAction::connect(navEdit, &QAction::triggered, [=](){ |
186 | WindowKeeper::switchWidgetNavigation(MWNA_EDIT); |
187 | }); |
188 | |
189 | toolbar->addAction(navEdit); |
190 | toolbar->widgetForAction(navEdit)->setObjectName("Edit" ); |
191 | } |
192 | |
193 | void WindowKeeper::layoutWindow(QMainWindow *window) |
194 | { |
195 | qInfo() << __FUNCTION__; |
196 | if (!d->navActionGroup) |
197 | d->navActionGroup = new QActionGroup(window); |
198 | |
199 | d->toolbar = new QToolBar(QToolBar::tr("Navigation" )); |
200 | d->toolbar->setMovable(false); |
201 | d->toolbar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); |
202 | |
203 | QWidget *titleWiget = new QWidget(); |
204 | titleWiget->setFixedSize(65, 29); |
205 | d->toolbar->addWidget(titleWiget); |
206 | |
207 | createNavRecent(d->toolbar); |
208 | createNavEdit(d->toolbar); |
209 | |
210 | QMenuBar* = new QMenuBar(); |
211 | createFileActions(menuBar); |
212 | createBuildActions(menuBar); |
213 | createDebugActions(menuBar); |
214 | createAnalyzeActions(menuBar); |
215 | createToolsActions(menuBar); |
216 | createHelpActions(menuBar); |
217 | |
218 | createStatusBar(window); |
219 | |
220 | window->addToolBar(Qt::LeftToolBarArea, d->toolbar); |
221 | window->setMinimumSize(QSize(MW_MIN_WIDTH,MW_MIN_HEIGHT)); |
222 | window->setAttribute(Qt::WA_DeleteOnClose); |
223 | window->setMenuBar(menuBar); |
224 | } |
225 | |
226 | WindowKeeper *WindowKeeper::instace() |
227 | { |
228 | if (!ins) |
229 | ins = new WindowKeeper; |
230 | return ins; |
231 | } |
232 | |
233 | WindowKeeper::WindowKeeper(QObject *parent) |
234 | : QObject (parent) |
235 | , d (new WindowKeeperPrivate) |
236 | { |
237 | auto &ctx = dpfInstance.serviceContext(); |
238 | WindowService *windowService = ctx.service<WindowService>(WindowService::name()); |
239 | |
240 | if (windowService) { |
241 | windowService->name(); |
242 | } |
243 | |
244 | if (!d->window) { |
245 | d->window = new QMainWindow(); |
246 | d->window->setWindowTitle("Deepin Union Code" ); |
247 | d->window->setWindowIcon(QIcon(":/core/images/unioncode@128.png" )); |
248 | QObject::connect(d->window, &QMainWindow::destroyed, [&](){ |
249 | d->window->takeCentralWidget(); |
250 | }); |
251 | layoutWindow(d->window); |
252 | |
253 | //CommandLine Model will not show main window |
254 | if (CommandParser::instance().getModel() != CommandParser::CommandLine) |
255 | d->window->show(); |
256 | int currentScreenIndex = qApp->desktop()->screenNumber(d->window); |
257 | QList<QScreen *> screenList = QGuiApplication::screens(); |
258 | |
259 | if (currentScreenIndex < screenList.count()) { |
260 | QRect screenRect = screenList[currentScreenIndex]->geometry(); |
261 | int screenWidth = screenRect.width(); |
262 | int screenHeight = screenRect.height(); |
263 | d->window->move((screenWidth - d->window->width()) / 2, (screenHeight - d->window->height()) / 2); |
264 | } |
265 | } |
266 | |
267 | QObject::connect(&dpf::Listener::instance(), &dpf::Listener::pluginsStarted, |
268 | this, &WindowKeeper::initUserWidget); |
269 | |
270 | using namespace std::placeholders; |
271 | if (!windowService->addMenu) { |
272 | windowService->addMenu = std::bind(&WindowKeeper::addMenu, this, _1); |
273 | } |
274 | |
275 | if (!windowService->addCentralNavigation) { |
276 | windowService->addCentralNavigation = std::bind(&WindowKeeper::addCentralNavigation, this, _1, _2); |
277 | } |
278 | |
279 | if (!windowService->addActionNavigation) { |
280 | windowService->addActionNavigation = std::bind(&WindowKeeper::addActionNavigation, this, _1, _2); |
281 | } |
282 | |
283 | if (!windowService->addAction) { |
284 | windowService->addAction = std::bind(&WindowKeeper::addAction, this, _1, _2); |
285 | } |
286 | |
287 | if (!windowService->removeActions) { |
288 | windowService->removeActions = std::bind(&WindowKeeper::removeActions, this, _1); |
289 | } |
290 | } |
291 | |
292 | WindowKeeper::~WindowKeeper() |
293 | { |
294 | if (d) { |
295 | delete d; |
296 | } |
297 | } |
298 | |
299 | QStringList WindowKeeper::navActionTexts() const |
300 | { |
301 | return d->centrals.keys(); |
302 | } |
303 | |
304 | void WindowKeeper::addActionNavigation(const QString &id, AbstractAction *action) |
305 | { |
306 | if (!action || !action->qAction() || !d->toolbar || !d->navActionGroup) |
307 | return; |
308 | |
309 | auto qAction = (QAction*)action->qAction(); |
310 | qAction->setCheckable(true); |
311 | d->navActionGroup->addAction(qAction); |
312 | d->toolbar->addAction(qAction); |
313 | d->toolbar->widgetForAction(qAction)->setObjectName(id); |
314 | QObject::connect(qAction, &QAction::triggered,[=](){ |
315 | switchWidgetNavigation(qAction->text()); |
316 | }); |
317 | } |
318 | |
319 | void WindowKeeper::addCentralNavigation(const QString &navName, AbstractCentral *central) |
320 | { |
321 | qInfo() << __FUNCTION__; |
322 | QWidget* inputWidget = static_cast<QWidget*>(central->qWidget()); |
323 | if(!central || !inputWidget || navName.isEmpty()) |
324 | return; |
325 | |
326 | if (d->centrals.values().contains(inputWidget)) |
327 | return; |
328 | |
329 | inputWidget->setParent(d->window); |
330 | d->centrals.insert(navName, inputWidget); |
331 | } |
332 | |
333 | void WindowKeeper::(AbstractMenu *) |
334 | { |
335 | qInfo() << __FUNCTION__; |
336 | |
337 | QMenu * = static_cast<QMenu*>(menu->qMenu()); |
338 | if (!d->window || !inputMenu) |
339 | return; |
340 | |
341 | //始终将Helper置末 |
342 | for (QAction *action : d->window->menuBar()->actions()) { |
343 | if (action->text() == MWM_HELP) { |
344 | d->window->menuBar()->insertMenu(action, inputMenu); |
345 | return; //提前返回 |
346 | } |
347 | } |
348 | |
349 | //直接添加到最后 |
350 | d->window->menuBar()->addMenu(inputMenu); |
351 | } |
352 | |
353 | void WindowKeeper::insertAction(const QString &, |
354 | const QString &beforActionName, |
355 | AbstractAction *action) |
356 | { |
357 | qInfo() << __FUNCTION__; |
358 | QAction *inputAction = static_cast<QAction*>(action->qAction()); |
359 | if (!action || !inputAction) |
360 | return; |
361 | |
362 | for (QAction *qAction : d->window->menuBar()->actions()) { |
363 | if (qAction->text() == menuName) { |
364 | for (auto childAction : qAction->menu()->actions()) { |
365 | if (childAction->text() == beforActionName) { |
366 | qAction->menu()->insertAction(childAction, inputAction); |
367 | break; |
368 | } else if (qAction->text() == MWM_FILE |
369 | && childAction->text() == MWMFA_QUIT) { |
370 | qAction->menu()->insertAction(qAction, inputAction); |
371 | break; |
372 | } |
373 | } |
374 | } |
375 | } |
376 | } |
377 | |
378 | void WindowKeeper::addAction(const QString &, AbstractAction *action) |
379 | { |
380 | QAction *inputAction = static_cast<QAction*>(action->qAction()); |
381 | if (!action || !inputAction) |
382 | return; |
383 | |
384 | for (QAction *qAction : d->window->menuBar()->actions()) { |
385 | if (qAction->text() == menuName) { |
386 | if (qAction->text() == MWM_FILE) { |
387 | auto endAction = *(qAction->menu()->actions().rbegin()); |
388 | if (endAction->text() == MWMFA_QUIT) { |
389 | return qAction->menu()->insertAction(endAction, inputAction); |
390 | } |
391 | } |
392 | qAction->menu()->addAction(inputAction); |
393 | } |
394 | } |
395 | } |
396 | |
397 | void WindowKeeper::removeActions(const QString &) |
398 | { |
399 | qInfo() << __FUNCTION__; |
400 | for (QAction *qAction : d->window->menuBar()->actions()) { |
401 | if (qAction->text() == menuName) { |
402 | foreach (QAction *action, qAction->menu()->actions()) { |
403 | qAction->menu()->removeAction(action); |
404 | } |
405 | |
406 | break; |
407 | } |
408 | } |
409 | } |
410 | |
411 | void WindowKeeper::addOpenProjectAction(const QString &name, AbstractAction *action) |
412 | { |
413 | if (!action || !action->qAction()) |
414 | return; |
415 | |
416 | QAction *inputAction = static_cast<QAction*>(action->qAction()); |
417 | for (QAction *qAction : d->window->menuBar()->actions()) { |
418 | if (qAction->text() == MWM_FILE) { |
419 | for(QAction *childAction : qAction->menu()->actions()) { |
420 | if (childAction->text() == MWMFA_OPEN_PROJECT) { |
421 | for (auto langAction : childAction->menu()->menuAction()->menu()->actions()) { |
422 | if (name == langAction->text()) { |
423 | langAction->menu()->addAction(inputAction); |
424 | return; |
425 | } |
426 | } |
427 | auto = new QMenu(name); |
428 | childAction->menu()->addMenu(langMenu); |
429 | langMenu->addAction(inputAction); |
430 | return; |
431 | } |
432 | } |
433 | } |
434 | } |
435 | } |
436 | |
437 | void WindowKeeper::initUserWidget() |
438 | { |
439 | qApp->processEvents(); |
440 | if (d->toolbar->actions().size() > 0) { |
441 | d->toolbar->actions().at(0)->trigger(); |
442 | } |
443 | } |
444 | |
445 | void WindowKeeper::switchWidgetNavigation(const QString &navName) |
446 | { |
447 | auto beforWidget = d->window->takeCentralWidget(); |
448 | if (beforWidget) |
449 | beforWidget->hide(); |
450 | |
451 | setNavActionChecked(navName, true); |
452 | |
453 | if (d->centrals.isEmpty() || !d->window) |
454 | return; |
455 | |
456 | auto widget = d->centrals[navName]; |
457 | if (!widget) |
458 | return; |
459 | |
460 | d->window->setCentralWidget(widget); |
461 | d->window->centralWidget()->show(); |
462 | } |
463 | |
464 | void WindowKeeper::setNavActionChecked(const QString &actionName, bool checked) |
465 | { |
466 | if (!d->navActionGroup) |
467 | return; |
468 | |
469 | for (auto action : d->navActionGroup->actions()) { |
470 | if (action->text() == actionName) { |
471 | action->setChecked(checked); |
472 | } |
473 | } |
474 | } |
475 | |
476 | void WindowKeeper::showAboutDlg() |
477 | { |
478 | AboutDialog dlg; |
479 | dlg.exec(); |
480 | } |
481 | |
482 | void WindowKeeper::showAboutPlugins() |
483 | { |
484 | PluginDialog dialog; |
485 | dialog.exec(); |
486 | } |
487 | |