1 // This file is part of Heimer.
2 // Copyright (C) 2018 Jussi Lind <jussi.lind@iki.fi>
3 //
4 // Heimer is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 // Heimer is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Heimer. If not, see <http://www.gnu.org/licenses/>.
15
16 #include "main_window.hpp"
17
18 #include "about_dlg.hpp"
19 #include "constants.hpp"
20 #include "mediator.hpp"
21 #include "node_action.hpp"
22 #include "recent_files_manager.hpp"
23 #include "recent_files_menu.hpp"
24 #include "settings.hpp"
25 #include "settings_dialog.hpp"
26 #include "simple_logger.hpp"
27 #include "whats_new_dlg.hpp"
28 #include "widget_factory.hpp"
29
30 #include <QAction>
31 #include <QApplication>
32 #include <QCheckBox>
33 #include <QDoubleSpinBox>
34 #include <QFontDialog>
35 #include <QHBoxLayout>
36 #include <QLabel>
37 #include <QLineEdit>
38 #include <QMenu>
39 #include <QMenuBar>
40 #include <QMessageBox>
41 #include <QPushButton>
42 #include <QScreen>
43 #include <QSpinBox>
44 #include <QToolBar>
45 #include <QVBoxLayout>
46 #include <QWidgetAction>
47
48 #include <cassert>
49
50 MainWindow * MainWindow::m_instance = nullptr;
51
52 namespace {
53 static const auto threeDots = "...";
54 }
55
MainWindow()56 MainWindow::MainWindow()
57 : m_aboutDlg(new AboutDlg(this))
58 , m_settingsDlg(new SettingsDialog(this))
59 , m_whatsNewDlg(new WhatsNewDlg(this))
60 , m_connectSelectedNodesAction(new QAction(tr("Connect selected nodes"), this))
61 , m_disconnectSelectedNodesAction(new QAction(tr("Disconnect selected nodes"), this))
62 , m_saveAction(new QAction(tr("&Save"), this))
63 , m_saveAsAction(new QAction(tr("&Save as") + threeDots, this))
64 , m_undoAction(new QAction(tr("Undo"), this))
65 , m_redoAction(new QAction(tr("Redo"), this))
66 , m_edgeWidthSpinBox(new QDoubleSpinBox(this))
67 , m_cornerRadiusSpinBox(new QSpinBox(this))
68 , m_fontButton(new QPushButton(this))
69 , m_gridSizeSpinBox(new QSpinBox(this))
70 , m_textSizeSpinBox(new QSpinBox(this))
71 , m_copyOnDragCheckBox(new QCheckBox(tr("Copy on drag"), this))
72 , m_showGridCheckBox(new QCheckBox(tr("Show grid"), this))
73 , m_searchLineEdit(new QLineEdit(this))
74 {
75 if (!m_instance) {
76 m_instance = this;
77 } else {
78 qFatal("MainWindow already instantiated!");
79 }
80 }
81
addConnectSelectedNodesAction(QMenu & menu)82 void MainWindow::addConnectSelectedNodesAction(QMenu & menu)
83 {
84 m_connectSelectedNodesAction->setShortcut(QKeySequence("Ctrl+Shift+C"));
85 connect(m_connectSelectedNodesAction, &QAction::triggered, [this] {
86 juzzlin::L().debug() << "Connect selected triggered";
87 m_mediator->performNodeAction({ NodeAction::Type::ConnectSelected });
88 });
89 menu.addAction(m_connectSelectedNodesAction);
90 connect(&menu, &QMenu::aboutToShow, [=] {
91 m_connectSelectedNodesAction->setEnabled(m_mediator->areSelectedNodesConnectable());
92 });
93 }
94
addDisconnectSelectedNodesAction(QMenu & menu)95 void MainWindow::addDisconnectSelectedNodesAction(QMenu & menu)
96 {
97 m_disconnectSelectedNodesAction->setShortcut(QKeySequence("Ctrl+Shift+D"));
98 connect(m_disconnectSelectedNodesAction, &QAction::triggered, [this] {
99 juzzlin::L().debug() << "Disconnect selected triggered";
100 m_mediator->performNodeAction({ NodeAction::Type::DisconnectSelected });
101 });
102 menu.addAction(m_disconnectSelectedNodesAction);
103 connect(&menu, &QMenu::aboutToShow, [=] {
104 m_disconnectSelectedNodesAction->setEnabled(m_mediator->areSelectedNodesDisconnectable());
105 });
106 }
107
addRedoAction(QMenu & menu)108 void MainWindow::addRedoAction(QMenu & menu)
109 {
110 m_redoAction->setShortcut(QKeySequence(QKeySequence::Redo));
111
112 connect(m_redoAction, &QAction::triggered, [=] {
113 m_mediator->redo();
114 });
115
116 m_redoAction->setEnabled(false);
117
118 menu.addAction(m_redoAction);
119 }
120
addUndoAction(QMenu & menu)121 void MainWindow::addUndoAction(QMenu & menu)
122 {
123 m_undoAction->setShortcut(QKeySequence(QKeySequence::Undo));
124
125 connect(m_undoAction, &QAction::triggered, [=] {
126 m_mediator->undo();
127 });
128
129 m_undoAction->setEnabled(false);
130
131 menu.addAction(m_undoAction);
132 }
133
createEditMenu()134 void MainWindow::createEditMenu()
135 {
136 const auto editMenu = menuBar()->addMenu(tr("&Edit"));
137
138 addUndoAction(*editMenu);
139
140 addRedoAction(*editMenu);
141
142 editMenu->addSeparator();
143
144 addConnectSelectedNodesAction(*editMenu);
145
146 addDisconnectSelectedNodesAction(*editMenu);
147
148 editMenu->addSeparator();
149
150 const auto colorMenu = new QMenu;
151 const auto colorMenuAction = editMenu->addMenu(colorMenu);
152 colorMenuAction->setText(tr("General &colors"));
153
154 const auto backgroundColorAction = new QAction(tr("Set background color") + threeDots, this);
155 backgroundColorAction->setShortcut(QKeySequence("Ctrl+B"));
156
157 connect(backgroundColorAction, &QAction::triggered, [=] {
158 emit actionTriggered(StateMachine::Action::BackgroundColorChangeRequested);
159 });
160
161 colorMenu->addAction(backgroundColorAction);
162
163 colorMenu->addSeparator();
164
165 const auto edgeColorAction = new QAction(tr("Set edge color") + threeDots, this);
166 edgeColorAction->setShortcut(QKeySequence("Ctrl+E"));
167
168 connect(edgeColorAction, &QAction::triggered, [=] {
169 emit actionTriggered(StateMachine::Action::EdgeColorChangeRequested);
170 });
171
172 colorMenu->addAction(edgeColorAction);
173
174 colorMenu->addSeparator();
175
176 const auto gridColorAction = new QAction(tr("Set grid color") + threeDots, this);
177 gridColorAction->setShortcut(QKeySequence("Ctrl+G"));
178
179 connect(gridColorAction, &QAction::triggered, [=] {
180 emit actionTriggered(StateMachine::Action::GridColorChangeRequested);
181 });
182
183 colorMenu->addAction(gridColorAction);
184
185 editMenu->addSeparator();
186
187 auto optimizeLayoutAction = new QAction(tr("Optimize layout") + threeDots, this);
188 optimizeLayoutAction->setShortcut(QKeySequence("Ctrl+Shift+O"));
189
190 connect(optimizeLayoutAction, &QAction::triggered, [=] {
191 emit actionTriggered(StateMachine::Action::LayoutOptimizationRequested);
192 });
193
194 editMenu->addAction(optimizeLayoutAction);
195 }
196
createCornerRadiusAction()197 QWidgetAction * MainWindow::createCornerRadiusAction()
198 {
199 m_cornerRadiusSpinBox->setMinimum(0);
200 m_cornerRadiusSpinBox->setMaximum(Constants::Node::MAX_CORNER_RADIUS);
201 m_cornerRadiusSpinBox->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
202
203 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
204 connect(m_cornerRadiusSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &MainWindow::cornerRadiusChanged);
205 #else
206 connect(m_cornerRadiusSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &MainWindow::cornerRadiusChanged);
207 #endif
208 return WidgetFactory::buildToolBarWidgetActionWithLabel(tr("Corner radius:"), *m_cornerRadiusSpinBox, *this).second;
209 }
210
createEdgeWidthAction()211 QWidgetAction * MainWindow::createEdgeWidthAction()
212 {
213 m_edgeWidthSpinBox->setSingleStep(Constants::Edge::STEP);
214 m_edgeWidthSpinBox->setMinimum(Constants::Edge::MIN_SIZE);
215 m_edgeWidthSpinBox->setMaximum(Constants::Edge::MAX_SIZE);
216 m_edgeWidthSpinBox->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
217
218 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
219 connect(m_edgeWidthSpinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &MainWindow::edgeWidthChanged);
220 #else
221 connect(m_edgeWidthSpinBox, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this, &MainWindow::edgeWidthChanged);
222 #endif
223 return WidgetFactory::buildToolBarWidgetActionWithLabel(tr("Edge width:"), *m_edgeWidthSpinBox, *this).second;
224 }
225
createTextSizeAction()226 QWidgetAction * MainWindow::createTextSizeAction()
227 {
228 m_textSizeSpinBox->setMinimum(Constants::Text::MIN_SIZE);
229 m_textSizeSpinBox->setMaximum(Constants::Text::MAX_SIZE);
230 m_textSizeSpinBox->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
231
232 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
233 connect(m_textSizeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &MainWindow::textSizeChanged);
234 #else
235 connect(m_textSizeSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &MainWindow::textSizeChanged);
236 #endif
237 return WidgetFactory::buildToolBarWidgetActionWithLabel(tr("Text size:"), *m_textSizeSpinBox, *this).second;
238 }
239
createFontAction()240 QWidgetAction * MainWindow::createFontAction()
241 {
242 m_fontButton->setText(tr("Font") + threeDots);
243 connect(m_fontButton, &QPushButton::clicked, [=] {
244 bool ok;
245 QFont defaultFont = m_fontButton->font();
246 defaultFont.setPointSize(m_textSizeSpinBox->value());
247 const auto font = QFontDialog::getFont(&ok, defaultFont, this);
248 if (ok) {
249 // Note: Support for multiple families implemented in Qt 5.13 =>
250 juzzlin::L().debug() << "Font family selected: '" << font.family().toStdString() << "'";
251 juzzlin::L().debug() << "Font weight selected: " << font.weight();
252 updateFontButtonFont(font);
253 m_textSizeSpinBox->setValue(font.pointSize());
254 emit textSizeChanged(font.pointSize());
255 emit fontChanged(font);
256 }
257 });
258 return WidgetFactory::buildToolBarWidgetAction(*m_fontButton, *this).second;
259 }
260
createGridSizeAction()261 QWidgetAction * MainWindow::createGridSizeAction()
262 {
263 m_gridSizeSpinBox->setMinimum(Constants::Grid::MIN_SIZE);
264 m_gridSizeSpinBox->setMaximum(Constants::Grid::MAX_SIZE);
265 m_gridSizeSpinBox->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
266
267 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
268 const auto signal = QOverload<int>::of(&QSpinBox::valueChanged);
269 #else
270 const auto signal = static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged);
271 #endif
272 connect(m_gridSizeSpinBox, signal, this, &MainWindow::gridSizeChanged);
273 connect(m_gridSizeSpinBox, signal, Settings::saveGridSize);
274
275 m_gridSizeSpinBox->setValue(Settings::loadGridSize());
276
277 return WidgetFactory::buildToolBarWidgetActionWithLabel(tr("Grid size:"), *m_gridSizeSpinBox, *this).second;
278 }
279
createSearchAction()280 QWidgetAction * MainWindow::createSearchAction()
281 {
282 m_searchTimer.setSingleShot(true);
283 connect(&m_searchTimer, &QTimer::timeout, [this, searchLineEdit = m_searchLineEdit]() {
284 const auto text = searchLineEdit->text();
285 juzzlin::L().debug() << "Search text changed: " << text.toStdString();
286 emit searchTextChanged(text);
287 });
288 connect(m_searchLineEdit, &QLineEdit::textChanged, [searchTimer = &m_searchTimer](const QString & text) {
289 if (text.isEmpty()) {
290 searchTimer->start(0);
291 } else {
292 searchTimer->start(Constants::View::TEXT_SEARCH_DELAY_MS);
293 }
294 });
295 connect(m_searchLineEdit, &QLineEdit::returnPressed, [searchTimer = &m_searchTimer] {
296 searchTimer->start(0);
297 });
298
299 return WidgetFactory::buildToolBarWidgetActionWithLabel(tr("Search:"), *m_searchLineEdit, *this).second;
300 }
301
createExportSubMenu(QMenu & fileMenu)302 void MainWindow::createExportSubMenu(QMenu & fileMenu)
303 {
304 const auto exportMenu = new QMenu;
305 const auto exportMenuAction = fileMenu.addMenu(exportMenu);
306 exportMenuAction->setText(tr("&Export"));
307
308 // Add "export to PNG image"-action
309 const auto exportToPngAction = new QAction(tr("&PNG"), this);
310 exportMenu->addAction(exportToPngAction);
311 connect(exportToPngAction, &QAction::triggered, [=] {
312 emit actionTriggered(StateMachine::Action::PngExportSelected);
313 });
314
315 exportMenu->addSeparator();
316
317 // Add "export to SVG file"-action
318 const auto exportToSvgAction = new QAction(tr("&SVG"), this);
319 exportMenu->addAction(exportToSvgAction);
320 connect(exportToSvgAction, &QAction::triggered, [=] {
321 emit actionTriggered(StateMachine::Action::SvgExportSelected);
322 });
323
324 connect(&fileMenu, &QMenu::aboutToShow, [=] {
325 exportToPngAction->setEnabled(m_mediator->hasNodes());
326 exportToSvgAction->setEnabled(m_mediator->hasNodes());
327 });
328 }
329
createFileMenu()330 void MainWindow::createFileMenu()
331 {
332 const auto fileMenu = menuBar()->addMenu(tr("&File"));
333
334 // Add "new"-action
335 const auto newAct = new QAction(tr("&New") + threeDots, this);
336 newAct->setShortcut(QKeySequence(QKeySequence::New));
337 fileMenu->addAction(newAct);
338 connect(newAct, &QAction::triggered, [=] {
339 emit actionTriggered(StateMachine::Action::NewSelected);
340 });
341
342 // Add "open"-action
343 const auto openAct = new QAction(tr("&Open") + threeDots, this);
344 openAct->setShortcut(QKeySequence(QKeySequence::Open));
345 fileMenu->addAction(openAct);
346 connect(openAct, &QAction::triggered, [=] {
347 emit actionTriggered(StateMachine::Action::OpenSelected);
348 });
349
350 // Add "Recent Files"-menu
351 const auto recentFilesMenu = new RecentFilesMenu;
352 const auto recentFilesMenuAction = fileMenu->addMenu(recentFilesMenu);
353 recentFilesMenuAction->setText(tr("Recent &Files"));
354 connect(recentFilesMenu, &RecentFilesMenu::fileSelected, [=] {
355 emit actionTriggered(StateMachine::Action::RecentFileSelected);
356 });
357
358 fileMenu->addSeparator();
359
360 // Add "save"-action
361 m_saveAction->setShortcut(QKeySequence(QKeySequence::Save));
362 m_saveAction->setEnabled(false);
363 fileMenu->addAction(m_saveAction);
364 connect(m_saveAction, &QAction::triggered, [=] {
365 emit actionTriggered(StateMachine::Action::SaveSelected);
366 });
367
368 // Add "save as"-action
369 m_saveAsAction->setShortcut(QKeySequence(QKeySequence::SaveAs));
370 m_saveAsAction->setEnabled(false);
371 fileMenu->addAction(m_saveAsAction);
372 connect(m_saveAsAction, &QAction::triggered, [=] {
373 emit actionTriggered(StateMachine::Action::SaveAsSelected);
374 });
375
376 fileMenu->addSeparator();
377
378 createExportSubMenu(*fileMenu);
379
380 fileMenu->addSeparator();
381
382 // Add "settings"-action
383 const auto settingsAct = new QAction(tr("Settings") + threeDots, this);
384 connect(settingsAct, &QAction::triggered, m_settingsDlg, &SettingsDialog::exec);
385 fileMenu->addAction(settingsAct);
386
387 fileMenu->addSeparator();
388
389 // Add "quit"-action
390 const auto quitAct = new QAction(tr("&Quit"), this);
391 quitAct->setShortcut(QKeySequence(QKeySequence::Quit));
392 fileMenu->addAction(quitAct);
393 connect(quitAct, &QAction::triggered, [=] {
394 emit actionTriggered(StateMachine::Action::QuitSelected);
395 });
396
397 connect(fileMenu, &QMenu::aboutToShow, [=] {
398 recentFilesMenuAction->setEnabled(RecentFilesManager::instance().hasRecentFiles());
399 });
400 }
401
createHelpMenu()402 void MainWindow::createHelpMenu()
403 {
404 const auto helpMenu = menuBar()->addMenu(tr("&Help"));
405
406 // Add "about"-action
407 const auto aboutAct = new QAction(tr("&About"), this);
408 helpMenu->addAction(aboutAct);
409 connect(aboutAct, &QAction::triggered, [=] {
410 m_aboutDlg->exec();
411 });
412
413 // Add "about Qt"-action
414 const auto aboutQtAct = new QAction(tr("About &Qt"), this);
415 helpMenu->addAction(aboutQtAct);
416 connect(aboutQtAct, &QAction::triggered, [=] {
417 QMessageBox::aboutQt(this, tr("About Qt"));
418 });
419
420 helpMenu->addSeparator();
421
422 // Add "What's new"-action
423 const auto whatsNewAct = new QAction(tr("What's New"), this);
424 helpMenu->addAction(whatsNewAct);
425 connect(whatsNewAct, &QAction::triggered, [=] {
426 m_whatsNewDlg->resize(3 * width() / 5, 3 * height() / 5);
427 m_whatsNewDlg->exec();
428 });
429 }
430
createToolBar()431 void MainWindow::createToolBar()
432 {
433 const auto toolBar = new QToolBar(this);
434 addToolBar(Qt::BottomToolBarArea, toolBar);
435 toolBar->addAction(createEdgeWidthAction());
436 toolBar->addSeparator();
437 toolBar->addAction(createTextSizeAction());
438 toolBar->addSeparator();
439 toolBar->addAction(createFontAction());
440 toolBar->addSeparator();
441 toolBar->addAction(createCornerRadiusAction());
442 toolBar->addSeparator();
443 toolBar->addAction(createGridSizeAction());
444
445 const auto spacer = new QWidget;
446 spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
447 toolBar->addWidget(spacer);
448 toolBar->addAction(createSearchAction());
449 toolBar->addSeparator();
450 toolBar->addWidget(m_showGridCheckBox);
451 toolBar->addWidget(m_copyOnDragCheckBox);
452
453 connect(m_showGridCheckBox, &QCheckBox::stateChanged, this, &MainWindow::gridVisibleChanged);
454 connect(m_showGridCheckBox, &QCheckBox::stateChanged, Settings::saveGridVisibleState);
455
456 m_showGridCheckBox->setCheckState(Settings::loadGridVisibleState());
457 }
458
createViewMenu()459 void MainWindow::createViewMenu()
460 {
461 const auto viewMenu = menuBar()->addMenu(tr("&View"));
462
463 // Add "fullScreen"-action
464 m_fullScreenAction = new QAction(tr("Full Screen"), this);
465 m_fullScreenAction->setCheckable(true);
466 m_fullScreenAction->setChecked(false);
467 m_fullScreenAction->setShortcut(QKeySequence("F11"));
468 viewMenu->addAction(m_fullScreenAction);
469 connect(m_fullScreenAction, &QAction::triggered, [=](bool checked) {
470 if (checked) {
471 Settings::saveFullScreen(true);
472 showFullScreen();
473 } else {
474 Settings::saveFullScreen(false);
475 showNormal();
476 resize(Settings::loadWindowSize(calculateDefaultWindowSize().second));
477 }
478 });
479
480 viewMenu->addSeparator();
481
482 // Add "zoom in"-action
483 const auto zoomIn = new QAction(tr("Zoom In"), this);
484 viewMenu->addAction(zoomIn);
485 zoomIn->setShortcut(QKeySequence(QKeySequence::ZoomIn));
486 connect(zoomIn, &QAction::triggered, this, &MainWindow::zoomInTriggered);
487
488 // Add "zoom out"-action
489 const auto zoomOut = new QAction(tr("Zoom Out"), this);
490 viewMenu->addAction(zoomOut);
491 connect(zoomOut, &QAction::triggered, this, &MainWindow::zoomOutTriggered);
492 zoomOut->setShortcut(QKeySequence(QKeySequence::ZoomOut));
493
494 viewMenu->addSeparator();
495
496 // Add "zoom to fit"-action
497 const auto zoomToFit = new QAction(tr("&Zoom To Fit"), this);
498 zoomToFit->setShortcut(QKeySequence("Ctrl+0"));
499 viewMenu->addAction(zoomToFit);
500 connect(zoomToFit, &QAction::triggered, this, &MainWindow::zoomToFitTriggered);
501
502 connect(viewMenu, &QMenu::aboutToShow, [=] {
503 zoomToFit->setEnabled(m_mediator->hasNodes());
504 });
505 }
506
calculateDefaultWindowSize() const507 std::pair<QSize, QSize> MainWindow::calculateDefaultWindowSize() const
508 {
509 // Detect screen dimensions
510 const auto screen = QGuiApplication::primaryScreen();
511 const auto screenGeometry = screen->geometry();
512 const int height = screenGeometry.height();
513 const int width = screenGeometry.width();
514 const double defaultScale = 0.8;
515 return { QSize(width, height), QSize(width, height) * defaultScale };
516 }
517
initialize()518 void MainWindow::initialize()
519 {
520 // Read dialog size data
521 const auto screenAndWindow = calculateDefaultWindowSize();
522 const auto lastSize = Settings::loadWindowSize(screenAndWindow.second);
523 if (screenAndWindow.first.width() < lastSize.width()
524 || screenAndWindow.first.height() < lastSize.height()) {
525 resize(screenAndWindow.second);
526 Settings::saveWindowSize(size());
527 } else {
528 resize(lastSize);
529 }
530
531 // Try to center the window.
532 move(screenAndWindow.first.width() / 2 - this->width() / 2, screenAndWindow.first.height() / 2 - this->height() / 2);
533
534 populateMenuBar();
535
536 createToolBar();
537
538 setWindowIcon(QIcon(":/heimer.png"));
539
540 emit actionTriggered(StateMachine::Action::MainWindowInitialized);
541 }
542
setMediator(std::shared_ptr<Mediator> mediator)543 void MainWindow::setMediator(std::shared_ptr<Mediator> mediator)
544 {
545 m_mediator = mediator;
546 }
547
setTitle()548 void MainWindow::setTitle()
549 {
550 const auto appInfo = QString(Constants::Application::APPLICATION_NAME) + " " + Constants::Application::APPLICATION_VERSION;
551 const auto displayFileName = m_mediator->fileName().isEmpty() ? tr("New File") : m_mediator->fileName();
552 if (m_mediator->isModified()) {
553 setWindowTitle(appInfo + " - " + displayFileName + " - " + tr("Not Saved"));
554 } else {
555 setWindowTitle(appInfo + " - " + displayFileName);
556 }
557 }
558
instance()559 MainWindow * MainWindow::instance()
560 {
561 return MainWindow::m_instance;
562 }
563
closeEvent(QCloseEvent * event)564 void MainWindow::closeEvent(QCloseEvent * event)
565 {
566 event->ignore();
567 emit actionTriggered(StateMachine::Action::QuitSelected);
568 }
569
populateMenuBar()570 void MainWindow::populateMenuBar()
571 {
572 createFileMenu();
573
574 createEditMenu();
575
576 createViewMenu();
577
578 createHelpMenu();
579 }
580
updateFontButtonFont(const QFont & font)581 void MainWindow::updateFontButtonFont(const QFont & font)
582 {
583 QFont newFont(font);
584 newFont.setPointSize(m_fontButton->font().pointSize());
585 m_fontButton->setFont(newFont);
586 }
587
appear()588 void MainWindow::appear()
589 {
590 if (Settings::loadFullScreen()) {
591 m_fullScreenAction->trigger();
592 } else {
593 show();
594 }
595 }
596
copyOnDragEnabled() const597 bool MainWindow::copyOnDragEnabled() const
598 {
599 return m_copyOnDragCheckBox->isChecked();
600 }
601
disableUndoAndRedo()602 void MainWindow::disableUndoAndRedo()
603 {
604 m_undoAction->setEnabled(false);
605 m_redoAction->setEnabled(false);
606 }
607
enableConnectSelectedNodesAction(bool enable)608 void MainWindow::enableConnectSelectedNodesAction(bool enable)
609 {
610 m_connectSelectedNodesAction->setEnabled(enable);
611 }
612
enableDisconnectSelectedNodesAction(bool enable)613 void MainWindow::enableDisconnectSelectedNodesAction(bool enable)
614 {
615 m_disconnectSelectedNodesAction->setEnabled(enable);
616 }
617
enableWidgetSignals(bool enable)618 void MainWindow::enableWidgetSignals(bool enable)
619 {
620 m_cornerRadiusSpinBox->blockSignals(!enable);
621 m_edgeWidthSpinBox->blockSignals(!enable);
622 m_textSizeSpinBox->blockSignals(!enable);
623 m_gridSizeSpinBox->blockSignals(!enable);
624 }
625
setCornerRadius(int value)626 void MainWindow::setCornerRadius(int value)
627 {
628 if (m_cornerRadiusSpinBox->value() != value) {
629 m_cornerRadiusSpinBox->setValue(value);
630 }
631 }
632
setEdgeWidth(double value)633 void MainWindow::setEdgeWidth(double value)
634 {
635 if (!qFuzzyCompare(m_edgeWidthSpinBox->value(), value)) {
636 m_edgeWidthSpinBox->setValue(value);
637 }
638 }
639
setFont(const QFont & font)640 void MainWindow::setFont(const QFont & font)
641 {
642 updateFontButtonFont(font);
643 }
644
setTextSize(int textSize)645 void MainWindow::setTextSize(int textSize)
646 {
647 if (m_textSizeSpinBox->value() != textSize) {
648 m_textSizeSpinBox->setValue(textSize);
649 }
650 }
651
enableUndo(bool enable)652 void MainWindow::enableUndo(bool enable)
653 {
654 m_undoAction->setEnabled(enable);
655 }
656
enableRedo(bool enable)657 void MainWindow::enableRedo(bool enable)
658 {
659 m_redoAction->setEnabled(enable);
660 }
661
enableSave(bool enable)662 void MainWindow::enableSave(bool enable)
663 {
664 setTitle();
665
666 m_saveAction->setEnabled(enable);
667 }
668
saveWindowSize()669 void MainWindow::saveWindowSize()
670 {
671 if (!m_fullScreenAction->isChecked()) {
672 Settings::saveWindowSize(size());
673 }
674 }
675
showErrorDialog(QString message)676 void MainWindow::showErrorDialog(QString message)
677 {
678 QMessageBox::critical(this,
679 Constants::Application::APPLICATION_NAME,
680 message,
681 "");
682 }
683
initializeNewMindMap()684 void MainWindow::initializeNewMindMap()
685 {
686 disableUndoAndRedo();
687 setSaveActionStatesOnNewMindMap();
688
689 emit actionTriggered(StateMachine::Action::NewMindMapInitialized);
690 }
691
setSaveActionStatesOnNewMindMap()692 void MainWindow::setSaveActionStatesOnNewMindMap()
693 {
694 m_saveAction->setEnabled(true);
695 m_saveAsAction->setEnabled(true);
696 }
697
setSaveActionStatesOnOpenedMindMap()698 void MainWindow::setSaveActionStatesOnOpenedMindMap()
699 {
700 m_saveAction->setEnabled(false);
701 m_saveAsAction->setEnabled(true);
702 }
703
704 MainWindow::~MainWindow() = default;
705