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
30static WindowKeeper *ins{nullptr};
31using namespace dpfservice;
32class 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
43WindowKeeperPrivate::WindowKeeperPrivate()
44{
45
46}
47
48void WindowKeeper::createFileActions(QMenuBar *menuBar)
49{
50 qInfo() << __FUNCTION__;
51 QMenu* fileMenu = 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* menuOpenProject = 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
84void WindowKeeper::createAnalyzeActions(QMenuBar *menuBar)
85{
86 qInfo() << __FUNCTION__;
87 QMenu* analyzeMenu = new QMenu();
88 QAction* buildAction = menuBar->addMenu(analyzeMenu);
89 buildAction->setText(MWM_ANALYZE);
90}
91
92void WindowKeeper::createBuildActions(QMenuBar *menuBar)
93{
94 qInfo() << __FUNCTION__;
95 QMenu* buildMenu = new QMenu();
96 QAction* buildAction = menuBar->addMenu(buildMenu);
97 buildAction->setText(MWM_BUILD);
98}
99
100void WindowKeeper::createDebugActions(QMenuBar *menuBar)
101{
102 qInfo() << __FUNCTION__;
103 QAction* debugAction = menuBar->addMenu(new QMenu());
104 debugAction->setText(MWM_DEBUG);
105}
106
107void WindowKeeper::createToolsActions(QMenuBar *menuBar)
108{
109 qInfo() << __FUNCTION__;
110 auto toolsMenu = new QMenu(MWM_TOOLS);
111 menuBar->addMenu(toolsMenu);
112}
113
114void WindowKeeper::createHelpActions(QMenuBar *menuBar)
115{
116 qInfo() << __FUNCTION__;
117 auto helpMenu = 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
152void WindowKeeper::createStatusBar(QMainWindow *window)
153{
154 qInfo() << __FUNCTION__;
155 QStatusBar* statusBar = new WindowStatusBar();
156 window->setStatusBar(statusBar);
157}
158
159void 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
176void 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
193void 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* menuBar = 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
226WindowKeeper *WindowKeeper::instace()
227{
228 if (!ins)
229 ins = new WindowKeeper;
230 return ins;
231}
232
233WindowKeeper::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
292WindowKeeper::~WindowKeeper()
293{
294 if (d) {
295 delete d;
296 }
297}
298
299QStringList WindowKeeper::navActionTexts() const
300{
301 return d->centrals.keys();
302}
303
304void 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
319void 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
333void WindowKeeper::addMenu(AbstractMenu *menu)
334{
335 qInfo() << __FUNCTION__;
336
337 QMenu *inputMenu = 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
353void WindowKeeper::insertAction(const QString &menuName,
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
378void WindowKeeper::addAction(const QString &menuName, 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
397void WindowKeeper::removeActions(const QString &menuName)
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
411void 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 langMenu = new QMenu(name);
428 childAction->menu()->addMenu(langMenu);
429 langMenu->addAction(inputAction);
430 return;
431 }
432 }
433 }
434 }
435}
436
437void WindowKeeper::initUserWidget()
438{
439 qApp->processEvents();
440 if (d->toolbar->actions().size() > 0) {
441 d->toolbar->actions().at(0)->trigger();
442 }
443}
444
445void 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
464void 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
476void WindowKeeper::showAboutDlg()
477{
478 AboutDialog dlg;
479 dlg.exec();
480}
481
482void WindowKeeper::showAboutPlugins()
483{
484 PluginDialog dialog;
485 dialog.exec();
486}
487