372 SubWindow* subWindow = new SubWindow{args..., nullptr}; |
372 SubWindow* subWindow = new SubWindow{args..., nullptr}; |
373 mdiArea->addSubWindow(subWindow); |
373 mdiArea->addSubWindow(subWindow); |
374 return subWindow; |
374 return subWindow; |
375 } |
375 } |
376 |
376 |
|
377 static void executeAction(QTextDocument* model, const ModelAction& action) |
|
378 { |
|
379 std::visit(overloaded{ |
|
380 [model](const AppendToModel& action){ |
|
381 QTextCursor cursor{model}; |
|
382 cursor.movePosition(QTextCursor::End); |
|
383 const QString newText = modelElementToString(action.newElement); |
|
384 // Make sure we have an empty line |
|
385 if (not model->lastBlock().text().isEmpty()) { |
|
386 cursor.insertBlock(); |
|
387 } |
|
388 cursor.insertText(newText); |
|
389 }, |
|
390 [](const DeleteFromModel&){}, |
|
391 [model](const ModifyModel& action){ |
|
392 QTextBlock block = model->findBlockByLineNumber((int) action.position); |
|
393 if (block.isValid()) { |
|
394 QTextCursor cursor{block}; |
|
395 cursor.select(QTextCursor::LineUnderCursor); |
|
396 cursor.insertText(modelElementToString(action.newElement)); |
|
397 } |
|
398 //model->assignAt(action.position, action.newElement); |
|
399 }, |
|
400 }, action); |
|
401 } |
|
402 |
377 int main(int argc, char *argv[]) |
403 int main(int argc, char *argv[]) |
378 { |
404 { |
379 doQtRegistrations(); |
405 doQtRegistrations(); |
380 QApplication app{argc, argv}; |
406 QApplication app{argc, argv}; |
381 QApplication::setWindowIcon(QIcon{":/icons/appicon.png"}); |
407 QApplication::setWindowIcon(QIcon{":/icons/appicon.png"}); |
397 const auto updateTitle = [&ui, &mainWindow]{ |
423 const auto updateTitle = [&ui, &mainWindow]{ |
398 mainWindow.setWindowTitle(title(&ui)); |
424 mainWindow.setWindowTitle(title(&ui)); |
399 }; |
425 }; |
400 const uiutilities::KeySequenceMap defaultKeyboardShortcuts = |
426 const uiutilities::KeySequenceMap defaultKeyboardShortcuts = |
401 uiutilities::makeKeySequenceMap(uiutilities::collectActions(&mainWindow)); |
427 uiutilities::makeKeySequenceMap(uiutilities::collectActions(&mainWindow)); |
402 const auto saveSettings = [&]{ |
428 const auto saveSettings = [ |
|
429 &libraries, |
|
430 &mainWindow, |
|
431 &recentlyOpenedFiles, |
|
432 &renderPreferences, |
|
433 &settingsChanged] |
|
434 { |
403 setSetting<Setting::MainWindowGeometry>(mainWindow.saveGeometry()); |
435 setSetting<Setting::MainWindowGeometry>(mainWindow.saveGeometry()); |
404 setSetting<Setting::MainWindowState>(mainWindow.saveState()); |
436 setSetting<Setting::MainWindowState>(mainWindow.saveState()); |
405 setSetting<Setting::RecentFiles>(recentlyOpenedFiles); |
437 setSetting<Setting::RecentFiles>(recentlyOpenedFiles); |
406 setSetting<Setting::RenderStyle>(renderPreferences.style); |
438 setSetting<Setting::RenderStyle>(renderPreferences.style); |
407 setSetting<Setting::DrawAxes>(renderPreferences.drawAxes); |
439 setSetting<Setting::DrawAxes>(renderPreferences.drawAxes); |
408 setSetting<Setting::Wireframe>(renderPreferences.wireframe); |
440 setSetting<Setting::Wireframe>(renderPreferences.wireframe); |
409 libraries.storeToSettings(); |
441 libraries.storeToSettings(); |
410 settingsChanged.emit(); |
442 settingsChanged.emit(); |
411 }; |
443 }; |
412 const auto executeAction = [&]( |
444 const auto openModelForEditing = [ |
413 QTextDocument* model, const ModelAction& action |
445 &colorTable, |
414 ) { |
446 &documents, |
415 std::visit(overloaded{ |
447 &mainWindow, |
416 [model](const AppendToModel& action){ |
448 &messageLog, |
417 QTextCursor cursor{model}; |
449 &renderPreferences, |
418 cursor.movePosition(QTextCursor::End); |
450 &settingsChanged, |
419 const QString newText = modelElementToString(action.newElement); |
451 &ui] |
420 // Make sure we have an empty line |
452 (const ModelId modelId) |
421 if (not model->lastBlock().text().isEmpty()) { |
453 { |
422 cursor.insertBlock(); |
|
423 } |
|
424 cursor.insertText(newText); |
|
425 }, |
|
426 [](const DeleteFromModel&){}, |
|
427 [model](const ModifyModel& action){ |
|
428 QTextBlock block = model->findBlockByLineNumber((int) action.position); |
|
429 if (block.isValid()) { |
|
430 QTextCursor cursor{block}; |
|
431 cursor.select(QTextCursor::LineUnderCursor); |
|
432 cursor.insertText(modelElementToString(action.newElement)); |
|
433 } |
|
434 //model->assignAt(action.position, actio邉󠄌n.newElement); |
|
435 }, |
|
436 }, action); |
|
437 |
|
438 }; |
|
439 const auto openModelForEditing = [&](const ModelId modelId){ |
|
440 QTextDocument* model = documents.getModelById(modelId); |
454 QTextDocument* model = documents.getModelById(modelId); |
441 if (model != nullptr) { |
455 if (model != nullptr) { |
442 ModelData* data = new ModelData(&documents); |
456 ModelData* data = new ModelData(&documents); |
443 data->tools = std::make_unique<EditTools>(); |
457 data->tools = std::make_unique<EditTools>(); |
444 data->canvas = std::make_unique<PartRenderer>(model, &documents, colorTable); |
458 data->canvas = std::make_unique<PartRenderer>(model, &documents, colorTable); |
506 subWindow->setWidget(data->canvas.get()); |
520 subWindow->setWidget(data->canvas.get()); |
507 subWindow->setWindowTitle(tabName(fileInfo)); |
521 subWindow->setWindowTitle(tabName(fileInfo)); |
508 subWindow->show(); |
522 subWindow->show(); |
509 } |
523 } |
510 }; |
524 }; |
511 const auto updateRecentlyOpenedDocumentsMenu = [&]{ |
525 const auto updateRecentlyOpenedDocumentsMenu = [ |
|
526 &documents, |
|
527 &libraries, |
|
528 &mainWindow, |
|
529 &openModelForEditing, |
|
530 &recentlyOpenedFiles, |
|
531 &ui] |
|
532 { |
512 rebuildRecentFilesMenu(ui.menuRecentFiles, recentlyOpenedFiles, &mainWindow); |
533 rebuildRecentFilesMenu(ui.menuRecentFiles, recentlyOpenedFiles, &mainWindow); |
513 for (QAction* action : ui.menuRecentFiles->actions()) { |
534 for (QAction* action : ui.menuRecentFiles->actions()) { |
514 QString path = action->data().toString(); |
535 QString path = action->data().toString(); |
515 QObject::connect( |
536 QObject::connect( |
516 action, |
537 action, |
535 ui.mdiArea->setViewMode(setting<Setting::ViewMode>()); |
566 ui.mdiArea->setViewMode(setting<Setting::ViewMode>()); |
536 ui.retranslateUi(&mainWindow); |
567 ui.retranslateUi(&mainWindow); |
537 mainWindow.setToolButtonStyle(setting<Setting::ToolButtonStyle>()); |
568 mainWindow.setToolButtonStyle(setting<Setting::ToolButtonStyle>()); |
538 settingsChanged.emit(); |
569 settingsChanged.emit(); |
539 }; |
570 }; |
540 const auto addRecentlyOpenedFile = [&](const QString& path){ |
571 const auto addRecentlyOpenedFile = [ |
|
572 &recentlyOpenedFiles, |
|
573 &saveSettings, |
|
574 &updateRecentlyOpenedDocumentsMenu] |
|
575 (const QString& path) |
|
576 { |
541 constexpr int maxRecentlyOpenedFiles = 10; |
577 constexpr int maxRecentlyOpenedFiles = 10; |
542 recentlyOpenedFiles.removeAll(path); |
578 recentlyOpenedFiles.removeAll(path); |
543 recentlyOpenedFiles.insert(0, path); |
579 recentlyOpenedFiles.insert(0, path); |
544 while (recentlyOpenedFiles.size() > maxRecentlyOpenedFiles) |
580 while (recentlyOpenedFiles.size() > maxRecentlyOpenedFiles) |
545 { |
581 { |
546 recentlyOpenedFiles.removeLast(); |
582 recentlyOpenedFiles.removeLast(); |
547 } |
583 } |
548 saveSettings(); |
584 saveSettings(); |
549 updateRecentlyOpenedDocumentsMenu(); |
585 updateRecentlyOpenedDocumentsMenu(); |
550 }; |
586 }; |
551 QObject::connect(ui.actionNew, &QAction::triggered, [&]{ |
587 QObject::connect(ui.actionNew, &QAction::triggered, |
552 openModelForEditing(documents.newModel()); |
588 [&documents, &openModelForEditing]{ |
553 }); |
589 openModelForEditing(documents.newModel()); |
554 QObject::connect(ui.actionOpen, &QAction::triggered, [&]{ |
590 } |
555 const QString path = getOpenModelPath(&mainWindow); |
591 ); |
556 if (not path.isEmpty()) |
592 QObject::connect(ui.actionOpen, &QAction::triggered, |
|
593 [&addRecentlyOpenedFile, &documents, &libraries, &mainWindow, &openModelForEditing] |
557 { |
594 { |
558 const std::optional<ModelId> id = openModelFromPath(path, &libraries, &documents, &mainWindow); |
595 const QString path = getOpenModelPath(&mainWindow); |
559 if (id.has_value()) { |
596 if (not path.isEmpty()) |
560 openModelForEditing(id.value()); |
597 { |
561 addRecentlyOpenedFile(path); |
598 const std::optional<ModelId> id = openModelFromPath(path, &libraries, &documents, &mainWindow); |
562 } |
599 if (id.has_value()) { |
563 } |
600 openModelForEditing(id.value()); |
564 }); |
601 addRecentlyOpenedFile(path); |
565 QObject::connect(ui.actionSettingsEditor, &QAction::triggered, [&]{ |
602 } |
|
603 } |
|
604 } |
|
605 ); |
|
606 QObject::connect(ui.actionSettingsEditor, &QAction::triggered, [&defaultKeyboardShortcuts, &restoreSettings, &settingsChanged, &ui]{ |
566 if (ui.mdiArea->findChildren<SettingsEditor*>().isEmpty()) { |
607 if (ui.mdiArea->findChildren<SettingsEditor*>().isEmpty()) { |
567 auto* const settingsEditor = createSubWindow<SettingsEditor>(ui.mdiArea, defaultKeyboardShortcuts); |
608 auto* const settingsEditor = createSubWindow<SettingsEditor>(ui.mdiArea, defaultKeyboardShortcuts); |
568 QObject::connect(&settingsChanged, &Signal::triggered, settingsEditor, &SettingsEditor::loadSettings); |
609 QObject::connect(&settingsChanged, &Signal::triggered, settingsEditor, &SettingsEditor::loadSettings); |
569 QObject::connect(settingsEditor, &SettingsEditor::settingsChanged, restoreSettings); |
610 QObject::connect(settingsEditor, &SettingsEditor::settingsChanged, restoreSettings); |
570 settingsEditor->setAttribute(Qt::WA_DeleteOnClose); |
611 settingsEditor->setAttribute(Qt::WA_DeleteOnClose); |
582 QObject::connect(ui.actionClose, &QAction::triggered, [&ui, &documents]{ |
623 QObject::connect(ui.actionClose, &QAction::triggered, [&ui, &documents]{ |
583 if (ModelData* data = currentModelData(&ui, &documents)) { |
624 if (ModelData* data = currentModelData(&ui, &documents)) { |
584 // TODO |
625 // TODO |
585 } |
626 } |
586 }); |
627 }); |
587 const auto save = [&](ModelId modelId){ |
628 const auto save = [&addRecentlyOpenedFile, &documents, &mainWindow](ModelId modelId){ |
588 QString error; |
629 QString error; |
589 QTextStream errorStream{&error}; |
630 QTextStream errorStream{&error}; |
590 const bool succeeded = documents.saveModel(modelId, errorStream); |
631 const bool succeeded = documents.saveModel(modelId, errorStream); |
591 if (not succeeded) |
632 if (not succeeded) |
592 { |
633 { |
598 if (pathPtr != nullptr) { |
639 if (pathPtr != nullptr) { |
599 addRecentlyOpenedFile(*pathPtr); |
640 addRecentlyOpenedFile(*pathPtr); |
600 } |
641 } |
601 } |
642 } |
602 };; |
643 };; |
603 const auto actionSaveAs = [&]{ |
644 const auto actionSaveAs = [&documents, &libraries, &mainWindow, &save, &ui]{ |
604 const std::optional<ModelId> modelId = findCurrentModelId(&ui); |
645 const std::optional<ModelId> modelId = findCurrentModelId(&ui); |
605 if (modelId.has_value()) |
646 if (modelId.has_value()) |
606 { |
647 { |
607 const QString* pathPtr = documents.modelPath(*modelId); |
648 const QString* pathPtr = documents.modelPath(*modelId); |
608 QString defaultPath = (pathPtr != nullptr) ? *pathPtr : ""; |
649 QString defaultPath = (pathPtr != nullptr) ? *pathPtr : ""; |
623 save(*modelId); |
664 save(*modelId); |
624 } |
665 } |
625 } |
666 } |
626 }; |
667 }; |
627 QObject::connect(ui.actionSaveAs, &QAction::triggered, actionSaveAs); |
668 QObject::connect(ui.actionSaveAs, &QAction::triggered, actionSaveAs); |
628 QObject::connect(ui.actionSave, &QAction::triggered, [&]{ |
669 QObject::connect(ui.actionSave, &QAction::triggered, [ |
|
670 &actionSaveAs, |
|
671 &documents, |
|
672 &save, |
|
673 &ui] |
|
674 { |
629 const std::optional<ModelId> modelId = findCurrentModelId(&ui); |
675 const std::optional<ModelId> modelId = findCurrentModelId(&ui); |
630 if (modelId.has_value()) { |
676 if (modelId.has_value()) { |
631 const QString* path = documents.modelPath(*modelId); |
677 const QString* path = documents.modelPath(*modelId); |
632 if (path == nullptr or path->isEmpty()) { |
678 if (path == nullptr or path->isEmpty()) { |
633 actionSaveAs(); |
679 actionSaveAs(); |
635 else { |
681 else { |
636 save(*modelId); |
682 save(*modelId); |
637 } |
683 } |
638 } |
684 } |
639 }); |
685 }); |
640 QObject::connect(ui.actionDrawAxes, &QAction::triggered, [&](bool drawAxes){ |
686 QObject::connect(ui.actionDrawAxes, &QAction::triggered, [ |
|
687 &documents, |
|
688 &renderPreferences, |
|
689 &saveSettings, |
|
690 &ui] |
|
691 (bool drawAxes) |
|
692 { |
641 renderPreferences.drawAxes = drawAxes; |
693 renderPreferences.drawAxes = drawAxes; |
642 saveSettings(); |
694 saveSettings(); |
643 updateRenderPreferences(&ui, &renderPreferences, &documents); |
695 updateRenderPreferences(&ui, &renderPreferences, &documents); |
644 }); |
696 }); |
645 QObject::connect(ui.actionWireframe, &QAction::triggered, [&](bool enabled){ |
697 QObject::connect(ui.actionWireframe, &QAction::triggered, [ |
|
698 &documents, |
|
699 &renderPreferences, |
|
700 &saveSettings, |
|
701 &ui] |
|
702 (bool enabled) |
|
703 { |
646 renderPreferences.wireframe = enabled; |
704 renderPreferences.wireframe = enabled; |
647 saveSettings(); |
705 saveSettings(); |
648 updateRenderPreferences(&ui, &renderPreferences, &documents); |
706 updateRenderPreferences(&ui, &renderPreferences, &documents); |
649 }); |
707 }); |
650 for (auto data : ::renderStyleButtons) { |
708 for (auto data : ::renderStyleButtons) { |
662 action->setChecked(hasDocument and action->data().value<EditingMode>() == mode); |
720 action->setChecked(hasDocument and action->data().value<EditingMode>() == mode); |
663 } |
721 } |
664 }; |
722 }; |
665 initializeTools(&ui, &toolWidgets, &mainWindow); |
723 initializeTools(&ui, &toolWidgets, &mainWindow); |
666 for (QAction* action : ui.editingModesToolBar->actions()) { |
724 for (QAction* action : ui.editingModesToolBar->actions()) { |
667 QObject::connect(action, &QAction::triggered, [&, action]{ |
725 QObject::connect(action, &QAction::triggered, [ |
|
726 action, |
|
727 &checkEditingModeAction, |
|
728 &documents, |
|
729 &ui] |
|
730 { |
668 if (ModelData* data = currentModelData(&ui, &documents)) { |
731 if (ModelData* data = currentModelData(&ui, &documents)) { |
669 const EditingMode mode = action->data().value<EditingMode>(); |
732 const EditingMode mode = action->data().value<EditingMode>(); |
670 data->tools->setEditMode(mode); |
733 data->tools->setEditMode(mode); |
671 checkEditingModeAction(mode); |
734 checkEditingModeAction(mode); |
672 } |
735 } |
673 }); |
736 }); |
674 } |
737 } |
675 QObject::connect(ui.mdiArea, &QMdiArea::subWindowActivated, |
738 QObject::connect(ui.mdiArea, &QMdiArea::subWindowActivated, [ |
676 [&](QMdiSubWindow* subWindow) { |
739 &checkEditingModeAction, |
|
740 &documents, |
|
741 &ui, |
|
742 &updateTitle] |
|
743 (QMdiSubWindow* subWindow) |
|
744 { |
677 ModelSubWindow* modelSubWindow = qobject_cast<ModelSubWindow*>(subWindow); |
745 ModelSubWindow* modelSubWindow = qobject_cast<ModelSubWindow*>(subWindow); |
678 if (modelSubWindow != nullptr) { |
746 if (modelSubWindow != nullptr) { |
679 if (ModelData* data = documents.findPayload<ModelData>(modelSubWindow->modelId)) { |
747 if (ModelData* data = documents.findPayload<ModelData>(modelSubWindow->modelId)) { |
680 checkEditingModeAction(data->tools->currentEditingMode()); |
748 checkEditingModeAction(data->tools->currentEditingMode()); |
681 ui.modelEdit->setDocument(data->model); |
749 ui.modelEdit->setDocument(data->model); |
689 updateTitle(); |
757 updateTitle(); |
690 }); |
758 }); |
691 ui.messageLog->setModel(&messageLog); |
759 ui.messageLog->setModel(&messageLog); |
692 QObject::connect(ui.actionAboutQt, &QAction::triggered, &app, &QApplication::aboutQt); |
760 QObject::connect(ui.actionAboutQt, &QAction::triggered, &app, &QApplication::aboutQt); |
693 QObject::connect(&documents, &DocumentManager::message, &messageLog, &MessageLog::addMessage); |
761 QObject::connect(&documents, &DocumentManager::message, &messageLog, &MessageLog::addMessage); |
694 QObject::connect(&messageLog, &MessageLog::rowsAboutToBeInserted, [&]{ |
762 QObject::connect(&messageLog, &MessageLog::rowsAboutToBeInserted, [&ui]{ |
695 const auto bar = ui.messageLog->verticalScrollBar(); |
763 const auto bar = ui.messageLog->verticalScrollBar(); |
696 ui.messageLog->setProperty("shouldAutoScroll", bar->value() == bar->maximum()); |
764 ui.messageLog->setProperty("shouldAutoScroll", bar->value() == bar->maximum()); |
697 }); |
765 }); |
698 QObject::connect(&messageLog, &MessageLog::rowsInserted, [&]{ |
766 QObject::connect(&messageLog, &MessageLog::rowsInserted, [&ui]{ |
699 ui.messageLog->resizeRowsToContents(); |
767 ui.messageLog->resizeRowsToContents(); |
700 if (ui.messageLog->property("shouldAutoScroll").toBool()) { |
768 if (ui.messageLog->property("shouldAutoScroll").toBool()) { |
701 ui.messageLog->scrollToBottom(); |
769 ui.messageLog->scrollToBottom(); |
702 } |
770 } |
703 }); |
771 }); |