Download src/Mod/Start/Gui/StartView.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 21 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Start/Gui/StartView.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Start/Gui/StartView.cpp
-
curl -L -o StartView.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Start/Gui/StartView.cpp
21 kB
| // SPDX-License-Identifier: LGPL-2.1-or-later | |
| /**************************************************************************** | |
| * * | |
| * Copyright (c) 2024 The FreeCAD Project Association AISBL * | |
| * * | |
| * This file is part of FreeCAD. * | |
| * * | |
| * FreeCAD is free software: you can redistribute it and/or modify it * | |
| * under the terms of the GNU Lesser General Public License as * | |
| * published by the Free Software Foundation, either version 2.1 of the * | |
| * License, or (at your option) any later version. * | |
| * * | |
| * FreeCAD is distributed in the hope that it will be useful, but * | |
| * WITHOUT ANY WARRANTY; without even the implied warranty of * | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * | |
| * Lesser General Public License for more details. * | |
| * * | |
| * You should have received a copy of the GNU Lesser General Public * | |
| * License along with FreeCAD. If not, see * | |
| * <https://www.gnu.org/licenses/>. * | |
| * * | |
| ***************************************************************************/ | |
| using namespace StartGui; | |
| TYPESYSTEM_SOURCE_ABSTRACT(StartGui::StartView, Gui::MDIView) // NOLINT | |
| StartView::StartView(QWidget* parent) | |
| : Gui::MDIView(nullptr, parent) | |
| , _contents(new QStackedWidget(parent)) | |
| , _newFileLabel {nullptr} | |
| , _examplesLabel {nullptr} | |
| , _recentFilesLabel {nullptr} | |
| , _customFolderLabel {nullptr} | |
| , _showOnStartupCheckBox {nullptr} | |
| { | |
| setObjectName(QLatin1String("StartView")); | |
| auto hGrp = App::GetApplication().GetParameterGroupByPath( | |
| "User parameter:BaseApp/Preferences/Mod/Start" | |
| ); | |
| auto cardSpacing = hGrp->GetInt("FileCardSpacing", 15); // NOLINT | |
| auto showExamples = hGrp->GetBool("ShowExamples", true); // NOLINT | |
| // Verify that the folder specified in preferences is available before showing it | |
| std::string customFolder(hGrp->GetASCII("CustomFolder", "")); | |
| bool showCustomFolder = false; | |
| if (!customFolder.empty()) { | |
| showCustomFolder = true; | |
| } | |
| // First start page | |
| auto firstStartScrollArea = gsl::owner<QScrollArea*>(new QScrollArea()); | |
| auto firstStartScrollWidget = gsl::owner<QWidget*>(new QWidget(firstStartScrollArea)); | |
| firstStartScrollArea->setWidget(firstStartScrollWidget); | |
| firstStartScrollArea->setWidgetResizable(true); | |
| auto firstStartRegion = gsl::owner<QHBoxLayout*>(new QHBoxLayout(firstStartScrollWidget)); | |
| firstStartRegion->setAlignment(Qt::AlignCenter); | |
| auto firstStartWidget = gsl::owner<FirstStartWidget*>(new FirstStartWidget(this)); | |
| connect(firstStartWidget, &FirstStartWidget::dismissed, this, &StartView::firstStartWidgetDismissed); | |
| firstStartRegion->addWidget(firstStartWidget); | |
| _contents->addWidget(firstStartScrollArea); | |
| // Documents page | |
| auto documentsWidget = gsl::owner<QWidget*>(new QWidget()); | |
| _contents->addWidget(documentsWidget); | |
| auto documentsMainLayout = gsl::owner<QVBoxLayout*>(new QVBoxLayout()); | |
| documentsWidget->setLayout(documentsMainLayout); | |
| auto documentsScrollArea = gsl::owner<QScrollArea*>(new QScrollArea()); | |
| documentsScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); | |
| documentsMainLayout->addWidget(documentsScrollArea); | |
| auto documentsScrollWidget = gsl::owner<QWidget*>(new QWidget(documentsScrollArea)); | |
| documentsScrollArea->setWidget(documentsScrollWidget); | |
| documentsScrollArea->setWidgetResizable(true); | |
| auto documentsContentLayout = gsl::owner<QVBoxLayout*>(new QVBoxLayout(documentsScrollWidget)); | |
| documentsContentLayout->setSizeConstraint(QLayout::SizeConstraint::SetMinAndMaxSize); | |
| _newFileLabel = gsl::owner<QLabel*>(new QLabel()); | |
| documentsContentLayout->addWidget(_newFileLabel); | |
| auto createNewRow = gsl::owner<QWidget*>(new QWidget); | |
| auto flowLayout = gsl::owner<FlowLayout*>(new FlowLayout); | |
| // Reset margins of layout to provide consistent spacing | |
| flowLayout->setContentsMargins({}); | |
| // This allows new file widgets to be targeted via QSS | |
| createNewRow->setObjectName(QStringLiteral("CreateNewRow")); | |
| createNewRow->setLayout(flowLayout); | |
| documentsContentLayout->addWidget(createNewRow); | |
| configureNewFileButtons(flowLayout); | |
| _recentFilesLabel = gsl::owner<QLabel*>(new QLabel()); | |
| documentsContentLayout->addWidget(_recentFilesLabel); | |
| auto recentFilesListWidget = gsl::owner<FileCardView*>(new FileCardView(_contents)); | |
| connect(recentFilesListWidget, &QListView::clicked, this, &StartView::fileCardSelected); | |
| documentsContentLayout->addWidget(recentFilesListWidget); | |
| auto customFolderListWidget = gsl::owner<FileCardView*>(new FileCardView(_contents)); | |
| customFolderListWidget->setVisible(showCustomFolder); | |
| _customFolderLabel = gsl::owner<QLabel*>(new QLabel()); | |
| _customFolderLabel->setVisible(showCustomFolder); | |
| documentsContentLayout->addWidget(_customFolderLabel); | |
| connect(customFolderListWidget, &QListView::clicked, this, &StartView::fileCardSelected); | |
| documentsContentLayout->addWidget(customFolderListWidget); | |
| auto examplesListWidget = gsl::owner<FileCardView*>(new FileCardView(_contents)); | |
| examplesListWidget->setVisible(showExamples); | |
| _examplesLabel = gsl::owner<QLabel*>(new QLabel()); | |
| _examplesLabel->setVisible(showExamples); | |
| documentsContentLayout->addWidget(_examplesLabel); | |
| connect(examplesListWidget, &QListView::clicked, this, &StartView::fileCardSelected); | |
| documentsContentLayout->addWidget(examplesListWidget); | |
| documentsContentLayout->setSpacing(static_cast<int>(cardSpacing)); | |
| documentsContentLayout->addStretch(); | |
| // Documents page footer | |
| auto footerLayout = gsl::owner<QHBoxLayout*>(new QHBoxLayout()); | |
| documentsMainLayout->addLayout(footerLayout); | |
| _openFirstStart = gsl::owner<QPushButton*>(new QPushButton()); | |
| _openFirstStart->setIcon(QIcon(QLatin1String(":/icons/preferences-general.svg"))); | |
| connect(_openFirstStart, &QPushButton::clicked, this, &StartView::openFirstStartClicked); | |
| _showOnStartupCheckBox = gsl::owner<QCheckBox*>(new QCheckBox()); | |
| bool showOnStartup = hGrp->GetBool("ShowOnStartup", true); | |
| _showOnStartupCheckBox->setCheckState( | |
| showOnStartup ? Qt::CheckState::Unchecked : Qt::CheckState::Checked | |
| ); | |
| connect(_showOnStartupCheckBox, &QCheckBox::toggled, this, &StartView::showOnStartupChanged); | |
| footerLayout->addWidget(_openFirstStart); | |
| footerLayout->addStretch(); | |
| footerLayout->addWidget(_showOnStartupCheckBox); | |
| setCentralWidget(_contents); | |
| // Set startup widget according to the first start parameter | |
| auto firstStart = hGrp->GetBool("FirstStart2024", true); // NOLINT | |
| _contents->setCurrentWidget(firstStart ? firstStartScrollArea : documentsWidget); | |
| configureCustomFolderListWidget(customFolderListWidget); | |
| configureExamplesListWidget(examplesListWidget); | |
| configureRecentFilesListWidget(recentFilesListWidget, _recentFilesLabel); | |
| QTimer::singleShot(2000, [this, recentFilesListWidget]() { | |
| auto updateFun = [this, recentFilesListWidget]() { | |
| configureRecentFilesListWidget(recentFilesListWidget, _recentFilesLabel); | |
| }; | |
| auto recentFiles = Gui::getMainWindow()->findChild<Gui::RecentFilesAction*>(); | |
| if (recentFiles != nullptr) { | |
| connect(recentFiles, &Gui::RecentFilesAction::recentFilesListModified, this, updateFun); | |
| } | |
| }); | |
| isInitialized = true; | |
| retranslateUi(); | |
| } | |
| void StartView::configureNewFileButtons(QLayout* layout) const | |
| { | |
| auto newEmptyFile = gsl::owner<NewFileButton*>(new NewFileButton( | |
| {tr("Empty File"), | |
| tr("Creates a new empty FreeCAD file"), | |
| QLatin1String(":/icons/document-new.svg")} | |
| )); | |
| auto openFile = gsl::owner<NewFileButton*>(new NewFileButton( | |
| {tr("Open File"), | |
| tr("Opens an existing CAD file or 3D model"), | |
| QLatin1String(":/icons/document-open.svg")} | |
| )); | |
| auto partDesign = gsl::owner<NewFileButton*>(new NewFileButton( | |
| {tr("Parametric Body"), | |
| tr("Creates a body with the Part Design workbench"), | |
| QLatin1String(":/icons/PartDesignWorkbench.svg")} | |
| )); | |
| auto assembly = gsl::owner<NewFileButton*>(new NewFileButton( | |
| {tr("Assembly"), | |
| tr("Creates an assembly project"), | |
| QLatin1String(":/icons/AssemblyWorkbench.svg")} | |
| )); | |
| auto draft = gsl::owner<NewFileButton*>(new NewFileButton( | |
| {tr("2D Draft"), tr("Creates a 2D Draft document"), QLatin1String(":/icons/DraftWorkbench.svg")} | |
| )); | |
| auto arch = gsl::owner<NewFileButton*>(new NewFileButton( | |
| {tr("BIM/Architecture"), | |
| tr("Creates an architectural project"), | |
| QLatin1String(":/icons/BIMWorkbench.svg")} | |
| )); | |
| // TODO: Ensure all of the required WBs are actually available | |
| layout->addWidget(partDesign); | |
| layout->addWidget(assembly); | |
| layout->addWidget(draft); | |
| layout->addWidget(arch); | |
| layout->addWidget(newEmptyFile); | |
| layout->addWidget(openFile); | |
| connect(newEmptyFile, &QPushButton::clicked, this, &StartView::newEmptyFile); | |
| connect(openFile, &QPushButton::clicked, this, &StartView::openExistingFile); | |
| connect(partDesign, &QPushButton::clicked, this, &StartView::newPartDesignFile); | |
| connect(assembly, &QPushButton::clicked, this, &StartView::newAssemblyFile); | |
| connect(draft, &QPushButton::clicked, this, &StartView::newDraftFile); | |
| connect(arch, &QPushButton::clicked, this, &StartView::newArchFile); | |
| } | |
| void StartView::configureFileCardWidget(QListView* fileCardWidget) | |
| { | |
| auto delegate = gsl::owner<FileCardDelegate*>(new FileCardDelegate(fileCardWidget)); | |
| fileCardWidget->setItemDelegate(delegate); | |
| fileCardWidget->setMinimumWidth(fileCardWidget->parentWidget()->width()); | |
| // fileCardWidget->setGridSize( | |
| // fileCardWidget->itemDelegate()->sizeHint(QStyleOptionViewItem(), | |
| // fileCardWidget->model()->index(0, 0))); | |
| } | |
| void StartView::configureRecentFilesListWidget(QListView* recentFilesListWidget, QLabel* recentFilesLabel) | |
| { | |
| _recentFilesModel.loadRecentFiles(); | |
| recentFilesListWidget->setModel(&_recentFilesModel); | |
| configureFileCardWidget(recentFilesListWidget); | |
| auto recentFilesGroup = App::GetApplication().GetParameterGroupByPath( | |
| "User parameter:BaseApp/Preferences/RecentFiles" | |
| ); | |
| auto numRecentFiles {recentFilesGroup->GetInt("RecentFiles", 0)}; | |
| if (numRecentFiles == 0) { | |
| recentFilesListWidget->hide(); | |
| recentFilesLabel->hide(); | |
| } | |
| else { | |
| recentFilesListWidget->show(); | |
| recentFilesLabel->show(); | |
| } | |
| } | |
| void StartView::configureExamplesListWidget(QListView* examplesListWidget) | |
| { | |
| _examplesModel.loadExamples(); | |
| examplesListWidget->setModel(&_examplesModel); | |
| configureFileCardWidget(examplesListWidget); | |
| } | |
| void StartView::configureCustomFolderListWidget(QListView* customFolderListWidget) | |
| { | |
| _customFolderModel.loadCustomFolder(); | |
| customFolderListWidget->setModel(&_customFolderModel); | |
| configureFileCardWidget(customFolderListWidget); | |
| } | |
| void StartView::newEmptyFile() | |
| { | |
| Gui::Application::Instance->commandManager().runCommandByName("Std_New"); | |
| postStart(PostStartBehavior::switchWorkbench); | |
| } | |
| void StartView::newPartDesignFile() | |
| { | |
| Gui::Application::Instance->commandManager().runCommandByName("Std_New"); | |
| Gui::Application::Instance->activateWorkbench("PartDesignWorkbench"); | |
| Gui::Application::Instance->commandManager().runCommandByName("PartDesign_Body"); | |
| postStart(PostStartBehavior::doNotSwitchWorkbench); | |
| } | |
| void StartView::openExistingFile() | |
| { | |
| auto originalDocument = Gui::Application::Instance->activeDocument(); | |
| Gui::Application::Instance->commandManager().runCommandByName("Std_Open"); | |
| Gui::Application::checkForRecomputes(); | |
| if (Gui::Application::Instance->activeDocument() != originalDocument) { | |
| // Only run this if the user chose a new document to open (that is, they didn't cancel the | |
| // open file dialog) | |
| postStart(PostStartBehavior::switchWorkbench); | |
| } | |
| } | |
| void StartView::newAssemblyFile() | |
| { | |
| Gui::Application::Instance->commandManager().runCommandByName("Std_New"); | |
| Gui::Application::Instance->activateWorkbench("AssemblyWorkbench"); | |
| Gui::Application::Instance->commandManager().runCommandByName("Assembly_CreateAssembly"); | |
| Gui::Application::Instance->commandManager().runCommandByName("Std_Refresh"); | |
| postStart(PostStartBehavior::doNotSwitchWorkbench); | |
| } | |
| void StartView::newDraftFile() | |
| { | |
| Gui::Application::Instance->commandManager().runCommandByName("Std_New"); | |
| Gui::Application::Instance->activateWorkbench("DraftWorkbench"); | |
| Gui::Application::Instance->commandManager().runCommandByName("Std_ViewTop"); | |
| postStart(PostStartBehavior::doNotSwitchWorkbench); | |
| } | |
| void StartView::newArchFile() | |
| { | |
| Gui::Application::Instance->commandManager().runCommandByName("Std_New"); | |
| try { | |
| Gui::Application::Instance->activateWorkbench("BIMWorkbench"); | |
| } | |
| catch (...) { | |
| Gui::Application::Instance->activateWorkbench("ArchWorkbench"); | |
| } | |
| // Set the camera zoom level to 10 m, which is more appropriate for architectural projects | |
| Gui::Command::doCommand( | |
| Gui::Command::Gui, | |
| "Gui.activeDocument().activeView().viewDefaultOrientation(None, 10000.0)" | |
| ); | |
| postStart(PostStartBehavior::doNotSwitchWorkbench); | |
| } | |
| bool StartView::onHasMsg(const char* pMsg) const | |
| { | |
| if (strcmp("AllowsOverlayOnHover", pMsg) == 0) { | |
| return false; | |
| } | |
| return MDIView::onHasMsg(pMsg); | |
| } | |
| void StartView::postStart(PostStartBehavior behavior) | |
| { | |
| auto hGrp = App::GetApplication().GetParameterGroupByPath( | |
| "User parameter:BaseApp/Preferences/Mod/Start" | |
| ); | |
| if (behavior == PostStartBehavior::switchWorkbench) { | |
| auto wb = hGrp->GetASCII("AutoloadModule", ""); | |
| if (wb == "$LastModule") { | |
| wb = App::GetApplication() | |
| .GetParameterGroupByPath("User parameter:BaseApp/Preferences/General") | |
| ->GetASCII("LastModule", ""); | |
| } | |
| if (!wb.empty()) { | |
| Gui::Application::Instance->activateWorkbench(wb.c_str()); | |
| } | |
| } | |
| if (auto closeStart = hGrp->GetBool("closeStart", false)) { | |
| for (QWidget* w = this; w != nullptr; w = w->parentWidget()) { | |
| if (auto mdiSub = qobject_cast<QMdiSubWindow*>(w)) { | |
| mdiSub->close(); | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| void StartView::fileCardSelected(const QModelIndex& index) | |
| { | |
| try { | |
| auto filename = index.data(static_cast<int>(Start::DisplayedFilesModelRoles::path)).toString(); | |
| Gui::ModuleIO::verifyAndOpenFile(filename); | |
| } | |
| catch (Base::PyException& e) { | |
| Base::Console().error(e.getMessage().c_str()); | |
| } | |
| catch (Base::Exception& e) { | |
| Base::Console().error(e.getMessage().c_str()); | |
| } | |
| catch (...) { | |
| Base::Console().error("An unknown error occurred"); | |
| } | |
| } | |
| void StartView::showOnStartupChanged(bool checked) | |
| { | |
| auto hGrp = App::GetApplication().GetParameterGroupByPath( | |
| "User parameter:BaseApp/Preferences/Mod/Start" | |
| ); | |
| hGrp->SetBool( | |
| "ShowOnStartup", | |
| !checked | |
| ); // The sense of this option has been reversed: the checkbox actually says | |
| // "*Don't* show on startup" now, but the option is preserved in its | |
| // original sense, so is stored inverted. | |
| } | |
| void StartView::openFirstStartClicked() | |
| { | |
| _contents->setCurrentIndex(0); | |
| } | |
| void StartView::firstStartWidgetDismissed() | |
| { | |
| auto hGrp = App::GetApplication().GetParameterGroupByPath( | |
| "User parameter:BaseApp/Preferences/Mod/Start" | |
| ); | |
| hGrp->SetBool("FirstStart2024", false); | |
| _contents->setCurrentIndex(1); | |
| } | |
| void StartView::changeEvent(QEvent* event) | |
| { | |
| if (!isInitialized) { | |
| return; | |
| } | |
| _openFirstStart->setEnabled(true); | |
| Gui::Document* doc = Gui::Application::Instance->activeDocument(); | |
| if (doc) { | |
| if (auto view = dynamic_cast<Gui::View3DInventor*>(doc->getActiveView())) { | |
| Gui::View3DInventorViewer* viewer = view->getViewer(); | |
| if (viewer->isEditing()) { | |
| _openFirstStart->setEnabled(false); | |
| } | |
| } | |
| } | |
| if (event->type() == QEvent::LanguageChange) { | |
| this->retranslateUi(); | |
| } | |
| Gui::MDIView::changeEvent(event); | |
| } | |
| void StartView::showEvent(QShowEvent* event) | |
| { | |
| if (auto mainWindow = Gui::getMainWindow()) { | |
| if (auto mdiArea = mainWindow->findChild<QMdiArea*>()) { | |
| connect( | |
| mdiArea, | |
| &QMdiArea::subWindowActivated, | |
| this, | |
| &StartView::onMdiSubWindowActivated, | |
| Qt::UniqueConnection | |
| ); | |
| } | |
| } | |
| Gui::MDIView::showEvent(event); | |
| } | |
| void StartView::onMdiSubWindowActivated(QMdiSubWindow* subWindow) | |
| { | |
| // check if start view is activated subwindow if yes, then enable updates | |
| // so we can once again receive paint events | |
| bool isOurWindow = subWindow && subWindow->isAncestorOf(this); | |
| setListViewUpdatesEnabled(isOurWindow); | |
| } | |
| void StartView::setListViewUpdatesEnabled(bool enabled) | |
| { | |
| // disable updates on all QListView widgets when inactive to prevent unnecessary paint events | |
| QList<QListView*> listViews = findChildren<QListView*>(); | |
| for (QListView* listView : listViews) { | |
| listView->setUpdatesEnabled(enabled); | |
| if (listView->viewport()) { | |
| listView->viewport()->setUpdatesEnabled(enabled); | |
| } | |
| } | |
| } | |
| void StartView::retranslateUi() | |
| { | |
| QString title = QCoreApplication::translate("Workbench", "Start"); | |
| setWindowTitle(title); | |
| const QLatin1String h1Start("<h1>"); | |
| const QLatin1String h1End("</h1>"); | |
| _newFileLabel->setText(h1Start + tr("New File") + h1End); | |
| _examplesLabel->setText(h1Start + tr("Examples") + h1End); | |
| _recentFilesLabel->setText(h1Start + tr("Recent Files") + h1End); | |
| auto hGrp = App::GetApplication().GetParameterGroupByPath( | |
| "User parameter:BaseApp/Preferences/Mod/Start" | |
| ); | |
| std::string customFolder(hGrp->GetASCII("CustomFolder", "")); | |
| bool shortCustomFolder = hGrp->GetBool("ShortCustomFolder", true); // false shows full path | |
| if (!customFolder.empty()) { | |
| if (shortCustomFolder) { | |
| _customFolderLabel->setToolTip(QString::fromUtf8(customFolder.c_str())); | |
| customFolder = customFolder.substr(customFolder.find_last_of("/\\") + 1); | |
| } | |
| _customFolderLabel->setText(h1Start + QString::fromUtf8(customFolder.c_str()) + h1End); | |
| } | |
| QString application = QString::fromUtf8(App::Application::Config()["ExeName"].c_str()); | |
| _openFirstStart->setText(tr("Open First Start Setup")); | |
| _showOnStartupCheckBox->setText(tr("Do not show this Start page again (start with blank screen)")); | |
| } | |