diff -r ca23936b455b -r 5d201ee4a9c3 src/mainwindow.cpp --- a/src/mainwindow.cpp Mon Jun 06 22:01:22 2022 +0300 +++ b/src/mainwindow.cpp Tue Jun 07 01:37:26 2022 +0300 @@ -15,564 +15,3 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - -#include -#include -#include -#include -#include -#include "mainwindow.h" -#include "ui_mainwindow.h" -#include "settingseditor/settingseditor.h" -#include "version.h" -#include "document.h" -#include "uiutilities.h" -#include "widgets/colorselectdialog.h" - -template -struct MemberData -{ - std::size_t member; - DataType payload; - constexpr MemberType memberInstance(BaseType* instance) const - { - return *reinterpret_cast(reinterpret_cast(instance) + this->member); - } -}; - -static constexpr MemberData renderStyleButtons[] = { - { offsetof(Ui_MainWindow, actionRenderStyleNormal), gl::RenderStyle::Normal }, - { offsetof(Ui_MainWindow, actionRenderStyleBfc), gl::RenderStyle::BfcRedGreen }, - { offsetof(Ui_MainWindow, actionRenderStyleRandom), gl::RenderStyle::RandomColors }, - { offsetof(Ui_MainWindow, actionRenderStylePickScene), gl::RenderStyle::PickScene }, -}; - -class A : public QSettings -{ - using QSettings::QSettings; -}; - -MainWindow::MainWindow(QWidget *parent) : - QMainWindow{parent}, - ui{std::make_unique()}, - documents{this}, - settings{}, - libraries{this} -{ - this->ui->setupUi(this); - defaultKeyboardShortcuts = uiutilities::makeKeySequenceMap(uiutilities::collectActions(this)); - connect(ui->actionNew, &QAction::triggered, this, &MainWindow::newModel); - connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::openModel); - connect(ui->actionQuit, &QAction::triggered, this, &QMainWindow::close); - connect(ui->actionSettingsEditor, &QAction::triggered, this, &MainWindow::runSettingsEditor); - connect(ui->actionAdjustGridToView, &QAction::triggered, [&]() - { - if (this->currentDocument() != nullptr) - { - adjustGridToView(this->currentDocument()->canvas); - } - }); - connect(this->ui->actionSave, &QAction::triggered, - this, &MainWindow::actionSave); - connect(this->ui->actionSaveAs, &QAction::triggered, - this, &MainWindow::actionSaveAs); - connect(this->ui->actionClose, &QAction::triggered, this, &MainWindow::actionClose); - connect(this->ui->actionDelete, &QAction::triggered, this, &MainWindow::actionDelete); - connect(this->ui->actionInvert, &QAction::triggered, this, &MainWindow::actionInvert); - connect(this->ui->tabs, &QTabWidget::tabCloseRequested, this, &MainWindow::handleTabCloseButton); - for (auto data : ::renderStyleButtons) - { - QAction* action = data.memberInstance(this->ui.get()); - connect(action, &QAction::triggered, [this, data]() - { - this->setRenderStyle(data.payload); - }); - } - connect(this->ui->actionDrawAxes, &QAction::triggered, this, &MainWindow::setDrawAxes); - this->updateTitle(); - this->restoreStartupSettings(); - this->restoreSettings(); - this->updateRenderPreferences(); - this->newModel(); -} - -// MainWindow needs a destructor even if it is empty because otherwise the destructor of the -// std::unique_ptr is resolved in the header file, where it will complain about Ui_MainWindow -// being incomplete. -MainWindow::~MainWindow() -{ -} - -void MainWindow::newModel() -{ - this->openModelForEditing(documents.newModel()); -} - -void MainWindow::openModel() -{ - const QString path = QFileDialog::getOpenFileName( - this, - tr("Open model"), - "", - tr("LDraw models (*.ldr *.dat)")); - if (not path.isEmpty()) - { - this->openModelFromPath(path); - } -} - -void MainWindow::openModelFromPath(const QString& path) -{ - QString errorString; - QTextStream errorStream{&errorString}; - std::optional modelIdOpt = this->documents.openModel( - path, - errorStream, - DocumentManager::OpenType::ManuallyOpened); - if (modelIdOpt.has_value()) - { - const ModelId modelId = modelIdOpt.value(); - this->documents.loadDependenciesForModel(modelId, path, this->libraries, errorStream); - if (not errorString.isEmpty()) - { - QMessageBox::warning( - this, - tr("Problem loading references"), - errorString); - } - this->openModelForEditing(modelId); - this->addRecentlyOpenedFile(path); - } - else - { - QMessageBox::critical( - this, - tr("Problem opening file"), - utility::format( - tr("Could not open %1: %2"), - path, - errorString)); - } -} - -/** - * @brief Changes the application language to the specified language - * @param localeCode Code of the locale to translate to - */ -void MainWindow::changeLanguage(QString localeCode) -{ - if (not localeCode.isEmpty() and localeCode != this->currentLanguage) - { - this->currentLanguage = localeCode; - if (localeCode == "system") - { - localeCode = QLocale::system().name(); - } - QLocale::setDefault({localeCode}); - qApp->removeTranslator(&this->translator); - const bool loadSuccessful = this->translator.load(pathToTranslation(localeCode)); - if (loadSuccessful) - { - qApp->installTranslator(&this->translator); - } - } -} - -void MainWindow::addRecentlyOpenedFile(const QString& path) -{ - this->recentlyOpenedFiles.removeAll(path); - this->recentlyOpenedFiles.insert(0, path); - while (this->recentlyOpenedFiles.size() > maxRecentlyOpenedFiles) - { - this->recentlyOpenedFiles.removeLast(); - } - this->saveSettings(); - this->updateRecentlyOpenedDocumentsMenu(); -} - -void MainWindow::openModelForEditing(const ModelId modelId) -{ - EditorTabWidget* document = new EditorTabWidget{ - this->documents.getModelById(modelId), - &this->documents, - this->colorTable, - }; - document->canvas->setRenderPreferences(this->renderPreferences); - connect(document, &EditorTabWidget::newStatusText, [&](const QString& newStatusText) - { - this->statusBar()->showMessage(newStatusText); - }); - const QFileInfo fileInfo{*this->documents.modelPath(modelId)}; - QString tabName = fileInfo.baseName(); - if (tabName.isEmpty()) - { - tabName = tr(""); - } - this->ui->tabs->addTab(document, tabName); - this->ui->tabs->setCurrentWidget(document); - document->restoreSplitterState(this->documentSplitterState); -} - -void MainWindow::runSettingsEditor() -{ - SettingsEditor settingsEditor{&this->settings, this->defaultKeyboardShortcuts, this}; - const int result = settingsEditor.exec(); - if (result == QDialog::Accepted) - { - this->restoreSettings(); - } -} - -EditorTabWidget* MainWindow::currentDocument() -{ - return qobject_cast(this->ui->tabs->currentWidget()); -} - -const EditorTabWidget* MainWindow::currentDocument() const -{ - return qobject_cast(this->ui->tabs->currentWidget()); -} - -void MainWindow::handleDocumentSplitterChange() -{ - EditorTabWidget* currentDocument = this->currentDocument(); - if (currentDocument != nullptr) - { - this->documentSplitterState = currentDocument->saveSplitterState(); - for (int i = 0; i < this->ui->tabs->count(); i += 1) - { - EditorTabWidget* document = qobject_cast(this->ui->tabs->widget(i)); - if (document != nullptr and document != currentDocument) - { - document->restoreSplitterState(this->documentSplitterState); - } - } - this->settings.setMainSplitterState(this->documentSplitterState); - } -} - -void MainWindow::updateRecentlyOpenedDocumentsMenu() -{ - this->ui->menuRecentFiles->clear(); - for (const QString& path : this->recentlyOpenedFiles) - { - QAction* action = new QAction{path, this}; - action->setData(path); - this->ui->menuRecentFiles->addAction(action); - connect(action, &QAction::triggered, this, &MainWindow::openRecentFile); - } -} - -void MainWindow::openRecentFile() -{ - QAction* action = qobject_cast(this->sender()); - if (action != nullptr) - { - const QString path = action->data().toString(); - this->openModelFromPath(path); - } -} - -void MainWindow::setRenderStyle(gl::RenderStyle renderStyle) -{ - this->renderPreferences.style = renderStyle; - this->saveSettings(); - this->updateRenderPreferences(); -} - -void MainWindow::setDrawAxes(bool drawAxes) -{ - this->renderPreferences.drawAxes = drawAxes; - this->saveSettings(); - this->updateRenderPreferences(); -} - -/** - * @brief Handles the "Save" (Ctrl+S) action - */ -void MainWindow::actionSave() -{ - if (this->currentDocument() != nullptr) - { - const std::optional modelId = this->findCurrentModelId(); - if (modelId.has_value()) - { - const QString* path = this->documents.modelPath(*modelId); - if (path == nullptr or path->isEmpty()) - { - this->actionSaveAs(); - } - else - { - QString error; - QTextStream errorStream{&error}; - const bool succeeded = this->documents.saveModel(*modelId, errorStream); - if (not succeeded) - { - QMessageBox::critical(this, tr("Save error"), error); - } - else - { - this->addRecentlyOpenedFile(*path); - } - } - } - } -} - -/** - * @brief Handles the "Save as…" (Ctrl+Shift+S) action - */ -void MainWindow::actionSaveAs() -{ - if (this->currentDocument() != nullptr) - { - const std::optional modelId = this->findCurrentModelId(); - if (modelId.has_value()) - { - const QString* pathPtr = this->documents.modelPath(*modelId); - QString defaultPath = (pathPtr != nullptr) ? *pathPtr : ""; - const QString newPath = QFileDialog::getSaveFileName( - this, - tr("Save as…"), - QFileInfo{defaultPath}.absoluteDir().path(), - tr("LDraw files (*.ldr *dat);;All files (*)") - ); - if (not newPath.isEmpty()) - { - QString error; - QTextStream errorStream{&error}; - this->documents.setModelPath(*modelId, newPath, this->libraries, errorStream); - this->ui->tabs->setTabText(this->ui->tabs->currentIndex(), QFileInfo{newPath}.fileName()); - this->actionSave(); - } - } - } -} - -/** - * @brief Handles the "Close" (Ctrl+W) action - */ -void MainWindow::actionClose() -{ - if (this->currentDocument() != nullptr) - { - this->closeDocument(this->currentDocument()); - } -} - -/** - * @brief Handles the "Delete" (Del) action - */ -void MainWindow::actionDelete() -{ - /* - EditorTabWidget* document = this->currentDocument(); - if (document != nullptr) - { - std::unique_ptr modelEditor = document->editModel(); - QSet ids = document->selectedObjects(); // copy - for (const ldraw::id_t id : ids) - { - const QModelIndex index = modelEditor->model().find(id); - if (index.isValid()) - { - modelEditor->remove(index.row()); - } - } - } - */ -} - -/** - * @brief Handles the "Invert" action - */ -void MainWindow::actionInvert() -{ - /* - EditorTabWidget* document = this->currentDocument(); - if (document != nullptr) - { - // TODO: simplify - std::unique_ptr modelEditor = document->editModel(); - const std::optional modelId = this->documents.findIdForModel(&modelEditor->model()); - if (modelId.has_value()) - { - ldraw::GetPolygonsContext context = { - .modelId = modelId.value(), - .documents = &this->documents, - }; - for (const ldraw::id_t id : document->selectedObjects()) - { - modelEditor->modifyObject(id, [&context](ldraw::Object* object) - { - object->invert(&context); - }); - } - } - } - */ -} - -/** - * @brief Removes the document at the specified tab index - * @param index - */ -void MainWindow::handleTabCloseButton(int tabIndex) -{ - if (tabIndex >= 0 and tabIndex < this->ui->tabs->count()) - { - EditorTabWidget* document = qobject_cast(this->ui->tabs->widget(tabIndex)); - if (document != nullptr) - { - this->closeDocument(document); - } - } -} - -/** - * @brief Closes the specified document - * @param document - */ -void MainWindow::closeDocument(EditorTabWidget *document) -{ - std::optional modelId = this->documents.findIdForModel(document->model); - if (modelId.has_value()) - { - this->documents.closeDocument(modelId.value()); - delete document; - } -} - -std::optional MainWindow::findCurrentModelId() const -{ - const EditorTabWidget* document = this->currentDocument(); - if (document != nullptr) - { - return this->documents.findIdForModel(document->model); - } - else - { - return {}; - } -} - -void MainWindow::changeEvent(QEvent* event) -{ - if (event != nullptr) - { - switch (event->type()) - { - case QEvent::LanguageChange: - this->ui->retranslateUi(this); - break; - default: - break; - } - } - QMainWindow::changeEvent(event); -} - -/** - * @brief Handles closing the main window - * @param event Event information - */ -void MainWindow::closeEvent(QCloseEvent* event) -{ - saveSettings(); - event->accept(); -} - -/** - * @brief Updates the title of the main window so to contain the app's name - * and version as well as the open document name. - */ -void MainWindow::updateTitle() -{ - QString title = ::appName; - title += " "; - title += fullVersionString(); - setWindowTitle(title); -} - -void MainWindow::updateRenderPreferences() -{ - for (int i = 0; i < this->ui->tabs->count(); i += 1) - { - EditorTabWidget* document = qobject_cast(this->ui->tabs->widget(i)); - if (document != nullptr) - { - document->canvas->setRenderPreferences(this->renderPreferences); - } - } - for (auto data : ::renderStyleButtons) - { - QAction* action = data.memberInstance(this->ui.get()); - action->setChecked(this->renderPreferences.style == data.payload); - } - this->ui->actionDrawAxes->setChecked(this->renderPreferences.drawAxes); -} - -/** - * @brief Stores the settings of the main window, storing geometry, etc - */ -void MainWindow::saveSettings() -{ - this->settings.setMainWindowGeometry(this->saveGeometry()); - this->settings.setRecentFiles(this->recentlyOpenedFiles); - this->settings.setMainSplitterState(this->documentSplitterState); - this->settings.setRenderStyle(static_cast(this->renderPreferences.style)); - this->settings.setDrawAxes(this->renderPreferences.drawAxes); - this->libraries.storeToSettings(&this->settings); -} - -void MainWindow::restoreStartupSettings() -{ - this->restoreGeometry(this->settings.mainWindowGeometry()); -} - -/** - * @brief Restores saved settings relating to the main window - */ -void MainWindow::restoreSettings() -{ - this->recentlyOpenedFiles = this->settings.recentFiles(); - this->documentSplitterState = this->settings.mainSplitterState(); - this->renderPreferences.style = static_cast(this->settings.renderStyle()); - this->renderPreferences.mainColor = this->settings.mainColor(); - this->renderPreferences.backgroundColor = this->settings.backgroundColor(); - this->renderPreferences.lineThickness = this->settings.lineThickness(); - this->renderPreferences.lineAntiAliasing = this->settings.lineAntiAliasing(); - this->renderPreferences.selectedColor = this->settings.selectedColor(); - this->renderPreferences.drawAxes = this->settings.drawAxes(); - const QString systemLocale = QLocale::system().name(); - const QVariant defaultLocale = this->settings.locale(); - this->changeLanguage(defaultLocale.toString()); - this->libraries.restoreFromSettings(&this->settings); - this->updateRecentlyOpenedDocumentsMenu(); - this->loadColors(); - this->updateRenderPreferences(); -} - -QString MainWindow::pathToTranslation(const QString& localeCode) -{ - QDir dir {":/locale"}; - return dir.filePath(localeCode + ".qm"); -} - -void MainWindow::loadColors() -{ - QTextStream errors; - this->colorTable = this->libraries.loadColorTable(errors); -} - -void MainWindow::keyReleaseEvent(QKeyEvent* /*event*/) -{ - /* - Document* document = this->currentDocument(); - if (document != nullptr) - { - document->handleKeyPress(event); - } - */ -}