src/main.cpp

changeset 214
8e1fe64ce4e3
parent 212
27259810da6d
child 215
34c6e7bc4ee1
equal deleted inserted replaced
213:ee5758ddb6d2 214:8e1fe64ce4e3
1 #include <QApplication> 1 #include <QApplication>
2 #include <QFileDialog> 2 #include <QFileDialog>
3 #include <QMessageBox> 3 #include <QMessageBox>
4 #include <QMdiSubWindow> 4 #include <QMdiSubWindow>
5 #include <QStackedWidget> 5 #include <QStackedWidget>
6 #include <QCloseEvent>
6 #include "mainwindow.h" 7 #include "mainwindow.h"
7 #include "ui_mainwindow.h" 8 #include "ui_mainwindow.h"
8 #include "version.h" 9 #include "version.h"
10 #include "ui/canvas.h"
9 #include "document.h" 11 #include "document.h"
10 #include "settingseditor/settingseditor.h" 12 #include "settingseditor/settingseditor.h"
11 #include "widgets/colorselectdialog.h" 13 #include "widgets/colorselectdialog.h"
12 14
13 static const QDir LOCALE_DIR {":/locale"}; 15 static const QDir LOCALE_DIR {":/locale"};
16
17 class ModelSubWindow : public QMdiSubWindow
18 {
19 Q_OBJECT
20 public:
21 const ModelId modelId;
22 ModelSubWindow(ModelId modelId, QWidget* widget) :
23 QMdiSubWindow{widget},
24 modelId{modelId}
25 {
26 }
27 protected:
28 void closeEvent(QCloseEvent* event) override
29 {
30 event->ignore();
31 }
32 };
14 33
15 static void doQtRegistrations() 34 static void doQtRegistrations()
16 { 35 {
17 QCoreApplication::setApplicationName(::appName); 36 QCoreApplication::setApplicationName(::appName);
18 QCoreApplication::setOrganizationName("hecknology.net"); 37 QCoreApplication::setOrganizationName("hecknology.net");
29 constexpr MemberType memberInstance(BaseType* instance) const 48 constexpr MemberType memberInstance(BaseType* instance) const
30 { 49 {
31 return *reinterpret_cast<MemberType*>(reinterpret_cast<char*>(instance) + this->member); 50 return *reinterpret_cast<MemberType*>(reinterpret_cast<char*>(instance) + this->member);
32 } 51 }
33 }; 52 };
53
54 class ModelData : public QObject
55 {
56 Q_OBJECT
57 public:
58 ModelData(QObject* parent) : QObject {parent} {}
59 std::unique_ptr<Canvas> canvas;
60 std::unique_ptr<QItemSelectionModel> itemSelectionModel;
61 std::unique_ptr<EditTools> tools;
62 Model* model;
63 };
64 #include "main.moc"
34 65
35 static constexpr MemberData<Ui_MainWindow, QAction*, gl::RenderStyle> renderStyleButtons[] = { 66 static constexpr MemberData<Ui_MainWindow, QAction*, gl::RenderStyle> renderStyleButtons[] = {
36 { offsetof(Ui_MainWindow, actionRenderStyleNormal), gl::RenderStyle::Normal }, 67 { offsetof(Ui_MainWindow, actionRenderStyleNormal), gl::RenderStyle::Normal },
37 { offsetof(Ui_MainWindow, actionRenderStyleBfc), gl::RenderStyle::BfcRedGreen }, 68 { offsetof(Ui_MainWindow, actionRenderStyleBfc), gl::RenderStyle::BfcRedGreen },
38 { offsetof(Ui_MainWindow, actionRenderStyleRandom), gl::RenderStyle::RandomColors }, 69 { offsetof(Ui_MainWindow, actionRenderStyleRandom), gl::RenderStyle::RandomColors },
105 qApp->installTranslator(translator); 136 qApp->installTranslator(translator);
106 } 137 }
107 } 138 }
108 } 139 }
109 140
110 /* 141 ModelData* findModelData(DocumentManager* documents, ModelId modelId)
111 void MainWindow::handleDocumentSplitterChange() 142 {
112 { 143 return documents->findPayload<ModelData>(modelId);
113 EditorTabWidget* currentDocument = this->currentDocument(); 144 }
114 if (currentDocument != nullptr) 145
115 { 146 static ModelSubWindow* currentModelSubWindow(Ui_MainWindow* ui)
116 this->documentSplitterState = currentDocument->saveSplitterState(); 147 {
117 for (int i = 0; i < this->ui->tabs->count(); i += 1) 148 return qobject_cast<ModelSubWindow*>(ui->mdiArea->activeSubWindow());
118 { 149 }
119 EditorTabWidget* document = qobject_cast<EditorTabWidget*>(this->ui->tabs->widget(i)); 150
120 if (document != nullptr and document != currentDocument) 151 static ModelData* currentModelData(Ui_MainWindow* ui, DocumentManager* documents)
121 { 152 {
122 document->restoreSplitterState(this->documentSplitterState); 153 if (auto* const activeSubWindow = currentModelSubWindow(ui)) {
123 } 154 return findModelData(documents, activeSubWindow->modelId);
124 } 155 }
125 this->settings.setMainSplitterState(this->documentSplitterState); 156 else {
126 }
127 }
128 */
129
130 static EditorTabWidget* currentTabWidget(Ui_MainWindow* ui)
131 {
132 QMdiSubWindow* activeSubWindow = ui->mdiArea->activeSubWindow();
133 if (activeSubWindow == nullptr) {
134 return nullptr; 157 return nullptr;
135 } 158 }
159 }
160
161 static Model* currentModelBody(Ui_MainWindow* ui, DocumentManager* documents)
162 {
163 if (auto* const activeSubWindow = currentModelSubWindow(ui)) {
164 return documents->getModelById(activeSubWindow->modelId);
165 }
136 else { 166 else {
137 return qobject_cast<EditorTabWidget*>(activeSubWindow->widget()); 167 return nullptr;
138 } 168 }
139 }; 169 }
140 170
141 171 static std::optional<ModelId> findCurrentModelId(Ui_MainWindow* ui)
142 static void closeDocument(DocumentManager* documents, EditorTabWidget *document) 172 {
143 { 173 ModelSubWindow* activeSubWindow = qobject_cast<ModelSubWindow*>(ui->mdiArea->activeSubWindow());
144 std::optional<ModelId> modelId = documents->findIdForModel(document->model); 174 if (activeSubWindow != nullptr) {
145 if (modelId.has_value()) { 175 return activeSubWindow->modelId;
146 documents->closeDocument(modelId.value());
147 delete document;
148 }
149 }
150
151 static std::optional<ModelId> findCurrentModelId(Ui_MainWindow* ui, DocumentManager* documents)
152 {
153 const EditorTabWidget* tab = currentTabWidget(ui);
154 if (tab != nullptr) {
155 return documents->findIdForModel(tab->model);
156 } 176 }
157 else { 177 else {
158 return {}; 178 return {};
159 } 179 }
160 } 180 }
194 action->setData(path); 214 action->setData(path);
195 menu->addAction(action); 215 menu->addAction(action);
196 } 216 }
197 } 217 }
198 218
219 template<typename Fn>
220 static void forModel(const DocumentManager* documents, Fn&& fn)
221 {
222 forValueInMap(*documents, [&fn](const DocumentManager::ModelInfo& info)
223 {
224 ModelData* modelSpecificData = qobject_cast<ModelData*>(info.payload);
225 if (modelSpecificData != nullptr) {
226 fn(&info, modelSpecificData);
227 }
228 });
229 }
230
199 static void updateRenderPreferences( 231 static void updateRenderPreferences(
200 Ui_MainWindow* ui, 232 Ui_MainWindow* ui,
201 const gl::RenderPreferences* renderPreferences) 233 const gl::RenderPreferences* renderPreferences,
202 { 234 const DocumentManager* documents)
203 for (QMdiSubWindow* subWindow : ui->mdiArea->subWindowList()) { 235 {
204 EditorTabWidget* tab = qobject_cast<EditorTabWidget*>(subWindow->widget()); 236 forModel(documents, [&renderPreferences](const void*, const ModelData* data){
205 if (tab != nullptr) { 237 if (data->canvas != nullptr) {
206 tab->canvas->setRenderPreferences(*renderPreferences); 238 data->canvas->setRenderPreferences(*renderPreferences);
207 } 239 }
208 } 240 });
209 for (auto data : ::renderStyleButtons) { 241 for (auto data : ::renderStyleButtons) {
210 QAction* action = data.memberInstance(ui); 242 QAction* action = data.memberInstance(ui);
211 action->setChecked(renderPreferences->style == data.payload); 243 action->setChecked(renderPreferences->style == data.payload);
212 } 244 }
213 ui->actionDrawAxes->setChecked(renderPreferences->drawAxes); 245 ui->actionDrawAxes->setChecked(renderPreferences->drawAxes);
293 LibraryManager libraries{&mainWindow}; 325 LibraryManager libraries{&mainWindow};
294 QByteArray documentSplitterState; 326 QByteArray documentSplitterState;
295 QStringList recentlyOpenedFiles; 327 QStringList recentlyOpenedFiles;
296 ColorTable colorTable; 328 ColorTable colorTable;
297 gl::RenderPreferences renderPreferences; 329 gl::RenderPreferences renderPreferences;
298 QMap<Model*, QItemSelectionModel*> itemSelectionModels;
299 ui.setupUi(&mainWindow); 330 ui.setupUi(&mainWindow);
300 const uiutilities::KeySequenceMap defaultKeyboardShortcuts = 331 const uiutilities::KeySequenceMap defaultKeyboardShortcuts =
301 uiutilities::makeKeySequenceMap(uiutilities::collectActions(&mainWindow)); 332 uiutilities::makeKeySequenceMap(uiutilities::collectActions(&mainWindow));
302 const auto saveSettings = [&]{ 333 const auto saveSettings = [&]{
303 settings.setMainWindowGeometry(mainWindow.saveGeometry()); 334 settings.setMainWindowGeometry(mainWindow.saveGeometry());
338 renderPreferences = loadRenderPreferences(&settings); 369 renderPreferences = loadRenderPreferences(&settings);
339 changeLanguage(settings.locale(), &translator); 370 changeLanguage(settings.locale(), &translator);
340 libraries.restoreFromSettings(&settings); 371 libraries.restoreFromSettings(&settings);
341 updateRecentlyOpenedDocumentsMenu(); 372 updateRecentlyOpenedDocumentsMenu();
342 colorTable = loadColors(&libraries); 373 colorTable = loadColors(&libraries);
343 updateRenderPreferences(&ui, &renderPreferences); 374 updateRenderPreferences(&ui, &renderPreferences, &documents);
344 ui.mdiArea->setViewMode(static_cast<QMdiArea::ViewMode>(settings.viewMode())); 375 ui.mdiArea->setViewMode(static_cast<QMdiArea::ViewMode>(settings.viewMode()));
345 ui.retranslateUi(&mainWindow); 376 ui.retranslateUi(&mainWindow);
346 }; 377 };
347 const auto addRecentlyOpenedFile = [&](const QString& path){ 378 const auto addRecentlyOpenedFile = [&](const QString& path){
348 constexpr int maxRecentlyOpenedFiles = 10; 379 constexpr int maxRecentlyOpenedFiles = 10;
355 saveSettings(); 386 saveSettings();
356 updateRecentlyOpenedDocumentsMenu(); 387 updateRecentlyOpenedDocumentsMenu();
357 }; 388 };
358 const auto openModelForEditing = [&](const ModelId modelId){ 389 const auto openModelForEditing = [&](const ModelId modelId){
359 Model* model = documents.getModelById(modelId); 390 Model* model = documents.getModelById(modelId);
360 EditorTabWidget* document = new EditorTabWidget{model, &documents, colorTable}; 391 if (model != nullptr) {
361 QObject::connect( 392 ModelData* data = new ModelData(&documents);
362 document, 393 data->tools = std::make_unique<EditTools>(model, colorTable);
363 &EditorTabWidget::modelAction, 394 data->canvas = std::make_unique<Canvas>(model, data->tools.get(), &documents, colorTable);
364 std::bind(executeAction, model, std::placeholders::_1)); 395 data->itemSelectionModel = std::make_unique<QItemSelectionModel>(model);
365 QItemSelectionModel* selectionModel = new QItemSelectionModel{model}; 396 data->model = model;
366 itemSelectionModels[model] = selectionModel; 397 QObject::connect(
367 QObject::connect(selectionModel, &QItemSelectionModel::selectionChanged, 398 data->tools.get(),
368 [model, document](const QItemSelection& selected, const QItemSelection& deselected) 399 &EditTools::modelAction,
369 { 400 std::bind(executeAction, model, std::placeholders::_1));
370 auto resolveIndex = [&model](const QModelIndex& index){ 401 QObject::connect(data->itemSelectionModel.get(), &QItemSelectionModel::selectionChanged,
371 return model->idAt(index.row()); 402 [modelId, &documents](
372 }; 403 const QItemSelection& selected,
373 auto resolve = [&resolveIndex](const QItemSelection& selection) 404 const QItemSelection& deselected)
374 { 405 {
375 return fn::map<QSet<ModelId>>(selection.indexes(), resolveIndex); 406 ModelData* data = findModelData(&documents, modelId);
376 }; 407 if (data != nullptr) {
377 document->canvas->handleSelectionChange(resolve(selected), resolve(deselected)); 408 auto resolveIndex = [&data](const QModelIndex& index){
378 }); 409 return data->model->idAt(index.row());
379 document->canvas->setRenderPreferences(renderPreferences); 410 };
380 QObject::connect( 411 auto resolve = [&resolveIndex](const QItemSelection& selection)
381 document, 412 {
382 &EditorTabWidget::newStatusText, 413 return fn::map<QSet<ModelId>>(selection.indexes(), resolveIndex);
383 [&](const QString& newStatusText) { 414 };
384 mainWindow.statusBar()->showMessage(newStatusText); 415 data->canvas->handleSelectionChange(resolve(selected), resolve(deselected));
416 }
385 }); 417 });
386 const QFileInfo fileInfo{*documents.modelPath(modelId)}; 418 data->canvas->setRenderPreferences(renderPreferences);
387 QMdiSubWindow* subWindow = ui.mdiArea->addSubWindow(document); 419 QObject::connect(
388 subWindow->setWindowTitle(tabName(fileInfo)); 420 data->tools.get(),
389 subWindow->show(); 421 &EditTools::newStatusText,
422 [&](const QString& newStatusText) {
423 mainWindow.statusBar()->showMessage(newStatusText);
424 });
425 const QFileInfo fileInfo{*documents.modelPath(modelId)};
426 QMdiSubWindow* subWindow = ui.mdiArea->addSubWindow(data->canvas.get());
427 subWindow->setWindowTitle(tabName(fileInfo));
428 subWindow->show();
429 }
390 }; 430 };
391 QObject::connect(ui.actionNew, &QAction::triggered, [&]{ 431 QObject::connect(ui.actionNew, &QAction::triggered, [&]{
392 openModelForEditing(documents.newModel()); 432 openModelForEditing(documents.newModel());
393 }); 433 });
394 QObject::connect(ui.actionOpen, &QAction::triggered, [&]{ 434 QObject::connect(ui.actionOpen, &QAction::triggered, [&]{
409 { 449 {
410 restoreSettings(); 450 restoreSettings();
411 } 451 }
412 }); 452 });
413 QObject::connect(ui.actionQuit, &QAction::triggered, &mainWindow, &QMainWindow::close); 453 QObject::connect(ui.actionQuit, &QAction::triggered, &mainWindow, &QMainWindow::close);
414 QObject::connect(ui.actionAdjustGridToView, &QAction::triggered, [&ui]{ 454 QObject::connect(ui.actionAdjustGridToView, &QAction::triggered, [&]{
415 EditorTabWidget* tab = currentTabWidget(&ui); 455 if (ModelData* data = currentModelData(&ui, &documents)) {
416 if (tab != nullptr) 456 adjustGridToView(data->canvas.get());
417 {
418 adjustGridToView(tab->canvas);
419 } 457 }
420 }); 458 });
421 QObject::connect(ui.actionClose, &QAction::triggered, [&ui, &documents]{ 459 QObject::connect(ui.actionClose, &QAction::triggered, [&ui, &documents]{
422 EditorTabWidget* tab = currentTabWidget(&ui); 460 if (ModelData* data = currentModelData(&ui, &documents)) {
423 if (tab != nullptr) 461 // TODO
424 {
425 closeDocument(&documents, tab);
426 } 462 }
427 }); 463 });
428 const auto save = [&](ModelId modelId){ 464 const auto save = [&](ModelId modelId){
429 QString error; 465 QString error;
430 QTextStream errorStream{&error}; 466 QTextStream errorStream{&error};
440 addRecentlyOpenedFile(*pathPtr); 476 addRecentlyOpenedFile(*pathPtr);
441 } 477 }
442 } 478 }
443 }; 479 };
444 const auto actionSaveAs = [&]{ 480 const auto actionSaveAs = [&]{
445 const std::optional<ModelId> modelId = findCurrentModelId(&ui, &documents); 481 const std::optional<ModelId> modelId = findCurrentModelId(&ui);
446 if (modelId.has_value()) 482 if (modelId.has_value())
447 { 483 {
448 const QString* pathPtr = documents.modelPath(*modelId); 484 const QString* pathPtr = documents.modelPath(*modelId);
449 QString defaultPath = (pathPtr != nullptr) ? *pathPtr : ""; 485 QString defaultPath = (pathPtr != nullptr) ? *pathPtr : "";
450 const QString newPath = QFileDialog::getSaveFileName( 486 const QString newPath = QFileDialog::getSaveFileName(
465 } 501 }
466 } 502 }
467 }; 503 };
468 QObject::connect(ui.actionSaveAs, &QAction::triggered, actionSaveAs); 504 QObject::connect(ui.actionSaveAs, &QAction::triggered, actionSaveAs);
469 QObject::connect(ui.actionSave, &QAction::triggered, [&]{ 505 QObject::connect(ui.actionSave, &QAction::triggered, [&]{
470 if (currentTabWidget(&ui) != nullptr) { 506 const std::optional<ModelId> modelId = findCurrentModelId(&ui);
471 const std::optional<ModelId> modelId = findCurrentModelId(&ui, &documents); 507 if (modelId.has_value()) {
472 if (modelId.has_value()) { 508 const QString* path = documents.modelPath(*modelId);
473 const QString* path = documents.modelPath(*modelId); 509 if (path == nullptr or path->isEmpty()) {
474 if (path == nullptr or path->isEmpty()) { 510 actionSaveAs();
475 actionSaveAs(); 511 }
476 } 512 else {
477 else { 513 save(*modelId);
478 save(*modelId);
479 }
480 } 514 }
481 } 515 }
482 }); 516 });
483 QObject::connect(ui.actionDelete, &QAction::triggered, [&]{ 517 QObject::connect(ui.actionDelete, &QAction::triggered, [&]{
484 EditorTabWidget* tab = currentTabWidget(&ui); 518 if (Model* model = currentModelBody(&ui, &documents)) {
485 if (tab != nullptr) {
486 std::vector<int> selectedRows = rows(ui.modelListView->selectionModel()->selectedRows()); 519 std::vector<int> selectedRows = rows(ui.modelListView->selectionModel()->selectedRows());
487 std::sort(selectedRows.begin(), selectedRows.end(), std::greater<int>{}); 520 std::sort(selectedRows.begin(), selectedRows.end(), std::greater<int>{});
488 for (int row : selectedRows) { 521 for (int row : selectedRows) {
489 executeAction(tab->model, DeleteFromModel{.position = row}); 522 executeAction(model, DeleteFromModel{.position = row});
490 } 523 }
491 } 524 }
492 }); 525 });
493 QObject::connect(ui.actionDrawAxes, &QAction::triggered, [&](bool drawAxes){ 526 QObject::connect(ui.actionDrawAxes, &QAction::triggered, [&](bool drawAxes){
494 renderPreferences.drawAxes = drawAxes; 527 renderPreferences.drawAxes = drawAxes;
495 saveSettings(); 528 saveSettings();
496 updateRenderPreferences(&ui, &renderPreferences); 529 updateRenderPreferences(&ui, &renderPreferences, &documents);
497 }); 530 });
498 for (auto data : ::renderStyleButtons) { 531 for (auto data : ::renderStyleButtons) {
499 QAction* action = data.memberInstance(&ui); 532 QAction* action = data.memberInstance(&ui);
500 QObject::connect(action, &QAction::triggered, [&, data]{ 533 QObject::connect(action, &QAction::triggered, [&, data]{
501 renderPreferences.style = data.payload; 534 renderPreferences.style = data.payload;
502 saveSettings(); 535 saveSettings();
503 updateRenderPreferences(&ui, &renderPreferences); 536 updateRenderPreferences(&ui, &renderPreferences, &documents);
504 }); 537 });
505 } 538 }
506 const auto checkEditingModeAction = [&ui](EditingMode mode) { 539 const auto checkEditingModeAction = [&ui](EditingMode mode) {
507 for (QAction* action : ui.editingModesToolBar->actions()) { 540 for (QAction* action : ui.editingModesToolBar->actions()) {
508 action->setChecked(action->data().value<EditingMode>() == mode); 541 action->setChecked(action->data().value<EditingMode>() == mode);
509 } 542 }
510 }; 543 };
511 initializeTools(&ui, &mainWindow); 544 initializeTools(&ui, &mainWindow);
512 for (QAction* action : ui.editingModesToolBar->actions()) { 545 for (QAction* action : ui.editingModesToolBar->actions()) {
513 QObject::connect(action, &QAction::triggered, [&, action]{ 546 QObject::connect(action, &QAction::triggered, [&, action]{
514 EditorTabWidget* tab = currentTabWidget(&ui); 547 if (ModelData* data = currentModelData(&ui, &documents)) {
515 if (tab != nullptr) {
516 const EditingMode mode = action->data().value<EditingMode>(); 548 const EditingMode mode = action->data().value<EditingMode>();
517 tab->setEditMode(mode); 549 data->tools->setEditMode(mode);
518 checkEditingModeAction(mode); 550 checkEditingModeAction(mode);
519 } 551 }
520 }); 552 });
521 } 553 }
522 QObject::connect(ui.mdiArea, &QMdiArea::subWindowActivated, 554 QObject::connect(ui.mdiArea, &QMdiArea::subWindowActivated,
523 [&](QMdiSubWindow* subWindow) { 555 [&](QMdiSubWindow* subWindow) {
524 if (subWindow != nullptr) { 556 ModelSubWindow* modelSubWindow = qobject_cast<ModelSubWindow*>(subWindow);
525 EditorTabWidget* tab = qobject_cast<EditorTabWidget*>(subWindow->widget()); 557 if (modelSubWindow != nullptr) {
526 if (tab != nullptr) { 558 if (ModelData* data = documents.findPayload<ModelData>(modelSubWindow->modelId)) {
527 checkEditingModeAction(tab->currentEditingMode()); 559 checkEditingModeAction(data->tools->currentEditingMode());
528 QItemSelectionModel* selectionModel = itemSelectionModels.value(tab->model); 560 if (data->itemSelectionModel != nullptr) {
529 if (selectionModel != nullptr) { 561 ui.modelListView->setModel(data->model);
530 ui.modelListView->setModel(tab->model); 562 ui.modelListView->setSelectionModel(data->itemSelectionModel.get());
531 ui.modelListView->setSelectionModel(selectionModel);
532 } 563 }
533 } 564 }
534 } 565 }
535 }); 566 });
536 mainWindow.setWindowTitle(title()); 567 mainWindow.setWindowTitle(title());
537 mainWindow.restoreGeometry(settings.mainWindowGeometry()); 568 mainWindow.restoreGeometry(settings.mainWindowGeometry());
538 restoreSettings(); 569 restoreSettings();
539 updateRenderPreferences(&ui, &renderPreferences); 570 updateRenderPreferences(&ui, &renderPreferences, &documents);
540 mainWindow.show(); 571 mainWindow.show();
541 const int result = app.exec(); 572 const int result = app.exec();
542 saveSettings(); 573 saveSettings();
543 return result; 574 return result;
544 } 575 }

mercurial