/***************************************************************************
 *   Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de>              *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  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 Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include <sstream>
#include <Inventor/events/SoMouseButtonEvent.h>
#include <Inventor/nodes/SoOrthographicCamera.h>
#include <Inventor/nodes/SoPerspectiveCamera.h>
#include <Inventor/SoPickedPoint.h>
#include <QApplication>
#include <QDialog>
#include <QDomDocument>
#include <QDomElement>
#include <QFile>
#include <QFileInfo>
#include <QFont>
#include <QFontMetrics>
#include <QImageReader>
#include <QMessageBox>
#include <QPainter>
#include <QPointer>
#include <QTextStream>

#include <App/ComplexGeoDataPy.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <App/DocumentObjectGroup.h>
#include <App/DocumentObserver.h>
#include <App/GeoFeature.h>
#include <App/GeoFeatureGroupExtension.h>
#include <App/Part.h>
#include <App/Link.h>
#include <Base/Console.h>
#include <Base/Parameter.h>

#include "Command.h"
#include "Action.h"
#include "Application.h"
#include "BitmapFactory.h"
#include "Control.h"
#include "Clipping.h"
#include "DemoMode.h"
#include "Dialogs/DlgSettingsImageImp.h"
#include "Document.h"
#include "FileDialog.h"
#include "ImageView.h"
#include "Inventor/SoAxisCrossKit.h"
#include "Macro.h"
#include "MainWindow.h"
#include "Navigation/NavigationStyle.h"
#include "OverlayParams.h"
#include "OverlayManager.h"
#include "SceneInspector.h"
#include "Selection.h"
#include "Selection/SelectionView.h"
#include "SelectionObject.h"
#include "SoFCOffscreenRenderer.h"
#include "TextureMapping.h"
#include "Tools.h"
#include "Tree.h"
#include "TreeParams.h"
#include "Utilities.h"
#include "View3DInventor.h"
#include "View3DInventorViewer.h"
#include "ViewParams.h"
#include "ViewProviderGeometryObject.h"
#include "WaitCursor.h"

using namespace Gui;
using Gui::Dialog::DlgSettingsImageImp;
namespace sp = std::placeholders;

namespace
{
// A helper class to open a transaction when changing properties of view providers.
// It uses the same parameter key as the PropertyView to control the behaviour.
class TransactionView
{
    Gui::Document* document;

public:
    static bool getDefault()
    {
        auto hGrp = App::GetApplication().GetParameterGroupByPath(
            "User parameter:BaseApp/Preferences/PropertyView"
        );
        return hGrp->GetBool("AutoTransactionView", false);
    }
    TransactionView(Gui::Document* doc, const char* name, bool enable = getDefault())
        : document(doc)
    {
        if (document && enable) {
            document->openCommand(name);
        }
        else {
            document = nullptr;
        }
    }

    ~TransactionView()
    {
        if (document) {
            document->commitCommand();
        }
    }
};
}  // namespace

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

DEF_STD_CMD_AC(StdOrthographicCamera)

StdOrthographicCamera::StdOrthographicCamera()
    : Command("Std_OrthographicCamera")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Orthographic View");
    sToolTipText = QT_TR_NOOP("Switches to orthographic view mode");
    sWhatsThis = "Std_OrthographicCamera";
    sStatusTip = sToolTipText;
    sPixmap = "view-isometric";
    sAccel = "V, O";
    eType = Alter3DView;
}

void StdOrthographicCamera::activated(int iMsg)
{
    if (iMsg == 1) {
        auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
        SoCamera* cam = view->getViewer()->getSoRenderManager()->getCamera();
        if (!cam || cam->getTypeId() != SoOrthographicCamera::getClassTypeId()) {

            doCommand(Command::Gui, "Gui.activeDocument().activeView().setCameraType(\"Orthographic\")");
        }
    }
}

bool StdOrthographicCamera::isActive()
{
    auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
    if (view) {
        // update the action group if needed
        bool check = _pcAction->isChecked();
        SoCamera* cam = view->getViewer()->getSoRenderManager()->getCamera();
        bool mode = cam ? cam->getTypeId() == SoOrthographicCamera::getClassTypeId() : false;

        if (mode != check) {
            _pcAction->setChecked(mode);
        }
        return true;
    }

    return false;
}

Action* StdOrthographicCamera::createAction()
{
    Action* pcAction = Command::createAction();
    pcAction->setCheckable(true);
    return pcAction;
}

DEF_STD_CMD_AC(StdPerspectiveCamera)

StdPerspectiveCamera::StdPerspectiveCamera()
    : Command("Std_PerspectiveCamera")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Perspective View");
    sToolTipText = QT_TR_NOOP("Switches to perspective view mode");
    sWhatsThis = "Std_PerspectiveCamera";
    sStatusTip = sToolTipText;
    sPixmap = "view-perspective";
    sAccel = "V, P";
    eType = Alter3DView;
}

void StdPerspectiveCamera::activated(int iMsg)
{
    if (iMsg == 1) {
        auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
        SoCamera* cam = view->getViewer()->getSoRenderManager()->getCamera();
        if (!cam || cam->getTypeId() != SoPerspectiveCamera::getClassTypeId()) {

            doCommand(Command::Gui, "Gui.activeDocument().activeView().setCameraType(\"Perspective\")");
        }
    }
}

bool StdPerspectiveCamera::isActive()
{
    auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
    if (view) {
        // update the action group if needed
        bool check = _pcAction->isChecked();
        SoCamera* cam = view->getViewer()->getSoRenderManager()->getCamera();
        bool mode = cam ? cam->getTypeId() == SoPerspectiveCamera::getClassTypeId() : false;

        if (mode != check) {
            _pcAction->setChecked(mode);
        }

        return true;
    }

    return false;
}

Action* StdPerspectiveCamera::createAction()
{
    Action* pcAction = Command::createAction();
    pcAction->setCheckable(true);
    return pcAction;
}

//===========================================================================

// The two commands below are provided for convenience so that they can be bound
// to a button of a spacemouse

//===========================================================================
// Std_ViewSaveCamera
//===========================================================================

DEF_3DV_CMD(StdCmdViewSaveCamera)

StdCmdViewSaveCamera::StdCmdViewSaveCamera()
    : Command("Std_ViewSaveCamera")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Save Current Camera");
    sToolTipText = QT_TR_NOOP("Saves the current camera settings");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_ViewSaveCamera";
    eType = Alter3DView;
}

void StdCmdViewSaveCamera::activated(int iMsg)
{
    Q_UNUSED(iMsg);

    auto view = qobject_cast<Gui::View3DInventor*>(Gui::getMainWindow()->activeWindow());
    if (view) {
        view->getViewer()->saveHomePosition();
    }
}

//===========================================================================
// Std_ViewRestoreCamera
//===========================================================================
DEF_3DV_CMD(StdCmdViewRestoreCamera)

StdCmdViewRestoreCamera::StdCmdViewRestoreCamera()
    : Command("Std_ViewRestoreCamera")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Restore Saved Camera");
    sToolTipText = QT_TR_NOOP("Restores the saved camera settings");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_ViewRestoreCamera";
    eType = Alter3DView;
}

void StdCmdViewRestoreCamera::activated(int iMsg)
{
    Q_UNUSED(iMsg);

    auto view = qobject_cast<Gui::View3DInventor*>(Gui::getMainWindow()->activeWindow());
    if (view) {
        view->getViewer()->resetToHomePosition();
    }
}

//===========================================================================
// Std_FreezeViews
//===========================================================================
class StdCmdFreezeViews: public Gui::Command
{
public:
    StdCmdFreezeViews();
    ~StdCmdFreezeViews() override = default;
    const char* className() const override
    {
        return "StdCmdFreezeViews";
    }

    void setShortcut(const QString&) override;
    QString getShortcut() const override;

protected:
    void activated(int iMsg) override;
    bool isActive() override;
    Action* createAction() override;
    void languageChange() override;

private:
    void onSaveViews();
    void onRestoreViews();

private:
    const int maxViews {50};
    int savedViews {0};
    int offset {0};
    QAction* saveView {nullptr};
    QAction* freezeView {nullptr};
    QAction* clearView {nullptr};
    QAction* separator {nullptr};
};

StdCmdFreezeViews::StdCmdFreezeViews()
    : Command("Std_FreezeViews")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("F&reeze Display");
    sToolTipText = QT_TR_NOOP("Freezes the current view position");
    sWhatsThis = "Std_FreezeViews";
    sStatusTip = sToolTipText;
    sAccel = "Shift+F";
    eType = Alter3DView;
}

Action* StdCmdFreezeViews::createAction()
{
    auto pcAction = new ActionGroup(this, getMainWindow());
    pcAction->setDropDownMenu(true);
    applyCommandData(this->className(), pcAction);

    // add the action items
    saveView = pcAction->addAction(QObject::tr("&Save Views…"));
    saveView->setWhatsThis(QString::fromLatin1(getWhatsThis()));
    QAction* loadView = pcAction->addAction(QObject::tr("&Load Views…"));
    loadView->setWhatsThis(QString::fromLatin1(getWhatsThis()));
    pcAction->addAction(QStringLiteral(""))->setSeparator(true);
    freezeView = pcAction->addAction(QObject::tr("F&reeze View"));
    freezeView->setShortcut(QString::fromLatin1(getAccel()));
    freezeView->setWhatsThis(QString::fromLatin1(getWhatsThis()));
    clearView = pcAction->addAction(QObject::tr("&Clear Views"));
    clearView->setWhatsThis(QString::fromLatin1(getWhatsThis()));
    separator = pcAction->addAction(QStringLiteral(""));
    separator->setSeparator(true);
    offset = pcAction->actions().count();

    // allow up to 50 views
    for (int i = 0; i < maxViews; i++) {
        pcAction->addAction(QStringLiteral(""))->setVisible(false);
    }

    return pcAction;
}

void StdCmdFreezeViews::setShortcut(const QString& shortcut)
{
    if (freezeView) {
        freezeView->setShortcut(shortcut);
    }
}

QString StdCmdFreezeViews::getShortcut() const
{
    if (freezeView) {
        return freezeView->shortcut().toString();
    }
    return Command::getShortcut();
}

void StdCmdFreezeViews::activated(int iMsg)
{
    auto pcAction = qobject_cast<ActionGroup*>(_pcAction);

    if (iMsg == 0) {
        onSaveViews();
    }
    else if (iMsg == 1) {
        onRestoreViews();
    }
    else if (iMsg == 3) {
        // Create a new view
        const char* ppReturn = nullptr;
        getGuiApplication()->sendMsgToActiveView("GetCamera", &ppReturn);

        QList<QAction*> acts = pcAction->actions();
        int index = 1;
        for (QList<QAction*>::Iterator it = acts.begin() + offset; it != acts.end(); ++it, index++) {
            if (!(*it)->isVisible()) {
                savedViews++;
                QString viewnr = QString(QObject::tr("Restore view &%1")).arg(index);
                (*it)->setText(viewnr);
                (*it)->setToolTip(QString::fromLatin1(ppReturn));
                (*it)->setVisible(true);
                if (index < 10) {
                    (*it)->setShortcut(QKeySequence(QStringLiteral("CTRL+%1").arg(index)));
                }
                break;
            }
        }
    }
    else if (iMsg == 4) {
        savedViews = 0;
        QList<QAction*> acts = pcAction->actions();
        for (QList<QAction*>::Iterator it = acts.begin() + offset; it != acts.end(); ++it) {
            (*it)->setVisible(false);
        }
    }
    else if (iMsg >= offset) {
        // Activate a view
        QList<QAction*> acts = pcAction->actions();
        QString data = acts[iMsg]->toolTip();
        QString send = QStringLiteral("SetCamera %1").arg(data);
        getGuiApplication()->sendMsgToActiveView(send.toLatin1());
    }
}

void StdCmdFreezeViews::onSaveViews()
{
    // Save the views to an XML file
    QString fn = FileDialog::getSaveFileName(
        getMainWindow(),
        QObject::tr("Save frozen views"),
        QString(),
        QStringLiteral("%1 (*.cam)").arg(QObject::tr("Frozen views"))
    );
    if (fn.isEmpty()) {
        return;
    }
    QFile file(fn);
    if (file.open(QFile::WriteOnly)) {
        QTextStream str(&file);
        auto pcAction = qobject_cast<ActionGroup*>(_pcAction);
        QList<QAction*> acts = pcAction->actions();
        str << "<?xml version='1.0' encoding='utf-8'?>\n"
            << "<FrozenViews SchemaVersion=\"1\">\n";
        str << "  <Views Count=\"" << savedViews << "\">\n";

        for (QList<QAction*>::Iterator it = acts.begin() + offset; it != acts.end(); ++it) {
            if (!(*it)->isVisible()) {
                break;
            }
            QString data = (*it)->toolTip();

            // remove the first line because it's a comment like '#Inventor V2.1 ascii'
            QString viewPos;
            if (!data.isEmpty()) {
                QStringList lines = data.split(QStringLiteral("\n"));
                if (lines.size() > 1) {
                    lines.pop_front();
                }
                viewPos = lines.join(QStringLiteral(" "));
            }

            str << "    <Camera settings=\"" << viewPos.toLatin1().constData() << "\"/>\n";
        }

        str << "  </Views>\n";
        str << "</FrozenViews>\n";
    }
}

void StdCmdFreezeViews::onRestoreViews()
{
    // Should we clear the already saved views
    if (savedViews > 0) {
        auto ret = QMessageBox::question(
            getMainWindow(),
            QObject::tr("Restore views"),
            QObject::tr(
                "Importing the restored views would clear the already stored views.\n"
                "Continue?"
            ),
            QMessageBox::Yes | QMessageBox::No,
            QMessageBox::Yes
        );
        if (ret != QMessageBox::Yes) {
            return;
        }
    }

    // Restore the views from an XML file
    QString fn = FileDialog::getOpenFileName(
        getMainWindow(),
        QObject::tr("Restore frozen views"),
        QString(),
        QStringLiteral("%1 (*.cam)").arg(QObject::tr("Frozen views"))
    );
    if (fn.isEmpty()) {
        return;
    }
    QFile file(fn);
    if (!file.open(QFile::ReadOnly)) {
        QMessageBox::critical(
            getMainWindow(),
            QObject::tr("Restore views"),
            QObject::tr("Cannot open file '%1'.").arg(fn)
        );
        return;
    }

    QDomDocument xmlDocument;

#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
    if (const auto result
        = xmlDocument.setContent(&file, QDomDocument::ParseOption::UseNamespaceProcessing);
        !result) {
        std::cerr << "Parse error in XML content at line " << result.errorLine << ", column "
                  << result.errorColumn << ": " << qPrintable(result.errorMessage) << std::endl;
        return;
    }
#else
    QString errorStr;
    int errorLine;
    int errorColumn;

    // evaluate the XML content
    if (!xmlDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) {
        std::cerr << "Parse error in XML content at line " << errorLine << ", column "
                  << errorColumn << ": " << (const char*)errorStr.toLatin1() << std::endl;
        return;
    }
#endif

    // get the root element
    QDomElement root = xmlDocument.documentElement();
    if (root.tagName() != QLatin1String("FrozenViews")) {
        std::cerr << "Unexpected XML structure" << std::endl;
        return;
    }

    bool ok;
    int scheme = root.attribute(QStringLiteral("SchemaVersion")).toInt(&ok);
    if (!ok) {
        return;
    }
    // SchemeVersion "1"
    if (scheme == 1) {
        // read the views, ignore the attribute 'Count'
        QDomElement child = root.firstChildElement(QStringLiteral("Views"));
        QDomElement views = child.firstChildElement(QStringLiteral("Camera"));
        QStringList cameras;
        while (!views.isNull()) {
            QString setting = views.attribute(QStringLiteral("settings"));
            cameras << setting;
            views = views.nextSiblingElement(QStringLiteral("Camera"));
        }

        // use this rather than the attribute 'Count' because it could be
        // changed from outside
        int ct = cameras.count();
        auto pcAction = qobject_cast<ActionGroup*>(_pcAction);
        QList<QAction*> acts = pcAction->actions();

        int numRestoredViews = std::min<int>(ct, acts.size() - offset);
        savedViews = numRestoredViews;

        if (numRestoredViews > 0) {
            separator->setVisible(true);
        }
        for (int i = 0; i < numRestoredViews; i++) {
            QString setting = cameras[i];
            QString viewnr = QString(QObject::tr("Restore view &%1")).arg(i + 1);
            acts[i + offset]->setText(viewnr);
            acts[i + offset]->setToolTip(setting);
            acts[i + offset]->setVisible(true);
            if (i < 9) {
                acts[i + offset]->setShortcut(QKeySequence(QStringLiteral("CTRL+%1").arg(i + 1)));
            }
        }

        // if less views than actions
        for (int index = numRestoredViews + offset; index < acts.count(); index++) {
            acts[index]->setVisible(false);
        }
    }
}

bool StdCmdFreezeViews::isActive()
{
    auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
    if (view) {
        saveView->setEnabled(savedViews > 0);
        freezeView->setEnabled(savedViews < maxViews);
        clearView->setEnabled(savedViews > 0);
        separator->setVisible(savedViews > 0);
        return true;
    }
    else {
        separator->setVisible(savedViews > 0);
    }

    return false;
}

void StdCmdFreezeViews::languageChange()
{
    Command::languageChange();

    if (!_pcAction) {
        return;
    }
    auto pcAction = qobject_cast<ActionGroup*>(_pcAction);
    QList<QAction*> acts = pcAction->actions();
    acts[0]->setText(QObject::tr("&Save Views…"));
    acts[1]->setText(QObject::tr("&Load Views…"));
    acts[3]->setText(QObject::tr("F&reeze View"));
    acts[4]->setText(QObject::tr("&Clear Views"));
    int index = 1;
    for (QList<QAction*>::Iterator it = acts.begin() + 5; it != acts.end(); ++it, index++) {
        if ((*it)->isVisible()) {
            QString viewnr = QString(QObject::tr("Restore View &%1")).arg(index);
            (*it)->setText(viewnr);
        }
    }
}


//===========================================================================
// Std_ToggleClipPlane
//===========================================================================

DEF_STD_CMD_AC(StdCmdToggleClipPlane)

StdCmdToggleClipPlane::StdCmdToggleClipPlane()
    : Command("Std_ToggleClipPlane")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Clippin&g View");
    sToolTipText = QT_TR_NOOP("Toggles clipping of the active view");
    sWhatsThis = "Std_ToggleClipPlane";
    sStatusTip = sToolTipText;
    sPixmap = "Std_ToggleClipPlane";
    eType = Alter3DView;
}

Action* StdCmdToggleClipPlane::createAction()
{
    Action* pcAction = Command::createAction();
    return pcAction;
}

void StdCmdToggleClipPlane::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    static QPointer<Gui::Dialog::Clipping> clipping = nullptr;
    if (!clipping) {
        auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
        if (view) {
            clipping = Gui::Dialog::Clipping::makeDockWidget(view);
        }
    }
}

bool StdCmdToggleClipPlane::isActive()
{
    auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
    return view ? true : false;
}

//===========================================================================
// StdCmdDrawStyle
//===========================================================================
class StdCmdDrawStyle: public Gui::Command
{
public:
    StdCmdDrawStyle();
    ~StdCmdDrawStyle() override = default;
    void languageChange() override;
    const char* className() const override
    {
        return "StdCmdDrawStyle";
    }
    void updateIcon(const Gui::MDIView* view);

protected:
    void activated(int iMsg) override;
    bool isActive() override;
    Gui::Action* createAction() override;
};

StdCmdDrawStyle::StdCmdDrawStyle()
    : Command("Std_DrawStyle")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&Draw Style");
    sToolTipText = QT_TR_NOOP("Changes the draw style of the objects");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_DrawStyle";
    sPixmap = "DrawStyleAsIs";
    eType = Alter3DView;

    this->getGuiApplication()->signalActivateView.connect([this](auto view) {
        this->updateIcon(view);
    });
}

Gui::Action* StdCmdDrawStyle::createAction()
{
    auto pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
    pcAction->setDropDownMenu(true);
    pcAction->setIsMode(true);
    applyCommandData(this->className(), pcAction);

    QAction* a0 = pcAction->addAction(QString());
    a0->setCheckable(true);
    a0->setIcon(BitmapFactory().iconFromTheme("DrawStyleAsIs"));
    a0->setChecked(true);
    a0->setObjectName(QStringLiteral("Std_DrawStyleAsIs"));
    a0->setShortcut(QKeySequence(QStringLiteral("V,1")));
    a0->setWhatsThis(QString::fromLatin1(getWhatsThis()));
    QAction* a1 = pcAction->addAction(QString());
    a1->setCheckable(true);
    a1->setIcon(BitmapFactory().iconFromTheme("DrawStylePoints"));
    a1->setObjectName(QStringLiteral("Std_DrawStylePoints"));
    a1->setShortcut(QKeySequence(QStringLiteral("V,2")));
    a1->setWhatsThis(QString::fromLatin1(getWhatsThis()));
    QAction* a2 = pcAction->addAction(QString());
    a2->setCheckable(true);
    a2->setIcon(BitmapFactory().iconFromTheme("DrawStyleWireFrame"));
    a2->setObjectName(QStringLiteral("Std_DrawStyleWireframe"));
    a2->setShortcut(QKeySequence(QStringLiteral("V,3")));
    a2->setWhatsThis(QString::fromLatin1(getWhatsThis()));
    QAction* a3 = pcAction->addAction(QString());
    a3->setCheckable(true);
    a3->setIcon(BitmapFactory().iconFromTheme("DrawStyleHiddenLine"));
    a3->setObjectName(QStringLiteral("Std_DrawStyleHiddenLine"));
    a3->setShortcut(QKeySequence(QStringLiteral("V,4")));
    a3->setWhatsThis(QString::fromLatin1(getWhatsThis()));
    QAction* a4 = pcAction->addAction(QString());
    a4->setCheckable(true);
    a4->setIcon(BitmapFactory().iconFromTheme("DrawStyleNoShading"));
    a4->setObjectName(QStringLiteral("Std_DrawStyleNoShading"));
    a4->setShortcut(QKeySequence(QStringLiteral("V,5")));
    a4->setWhatsThis(QString::fromLatin1(getWhatsThis()));
    QAction* a5 = pcAction->addAction(QString());
    a5->setCheckable(true);
    a5->setIcon(BitmapFactory().iconFromTheme("DrawStyleShaded"));
    a5->setObjectName(QStringLiteral("Std_DrawStyleShaded"));
    a5->setShortcut(QKeySequence(QStringLiteral("V,6")));
    a5->setWhatsThis(QString::fromLatin1(getWhatsThis()));
    QAction* a6 = pcAction->addAction(QString());
    a6->setCheckable(true);
    a6->setIcon(BitmapFactory().iconFromTheme("DrawStyleFlatLines"));
    a6->setObjectName(QStringLiteral("Std_DrawStyleFlatLines"));
    a6->setShortcut(QKeySequence(QStringLiteral("V,7")));
    a6->setWhatsThis(QString::fromLatin1(getWhatsThis()));

    pcAction->setIcon(a0->icon());

    _pcAction = pcAction;
    languageChange();
    return pcAction;
}

void StdCmdDrawStyle::languageChange()
{
    Command::languageChange();

    if (!_pcAction) {
        return;
    }
    auto pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
    QList<QAction*> a = pcAction->actions();

    a[0]->setText(QCoreApplication::translate("Std_DrawStyle", "&1 As is"));
    a[0]->setToolTip(QCoreApplication::translate("Std_DrawStyle", "Normal mode"));

    a[1]->setText(QCoreApplication::translate("Std_DrawStyle", "&2 Points"));
    a[1]->setToolTip(QCoreApplication::translate("Std_DrawStyle", "Points mode"));

    a[2]->setText(QCoreApplication::translate("Std_DrawStyle", "&3 Wireframe"));
    a[2]->setToolTip(QCoreApplication::translate("Std_DrawStyle", "Wireframe mode"));

    a[3]->setText(QCoreApplication::translate("Std_DrawStyle", "&4 Hidden line"));
    a[3]->setToolTip(QCoreApplication::translate("Std_DrawStyle", "Hidden line mode"));

    a[4]->setText(QCoreApplication::translate("Std_DrawStyle", "&5 No shading"));
    a[4]->setToolTip(QCoreApplication::translate("Std_DrawStyle", "No shading mode"));

    a[5]->setText(QCoreApplication::translate("Std_DrawStyle", "&6 Shaded"));
    a[5]->setToolTip(QCoreApplication::translate("Std_DrawStyle", "Shaded mode"));

    a[6]->setText(QCoreApplication::translate("Std_DrawStyle", "&7 Flat lines"));
    a[6]->setToolTip(QCoreApplication::translate("Std_DrawStyle", "Flat lines mode"));
}

void StdCmdDrawStyle::updateIcon(const MDIView* view)
{
    const auto view3d = dynamic_cast<const Gui::View3DInventor*>(view);
    if (!view3d) {
        return;
    }
    Gui::View3DInventorViewer* viewer = view3d->getViewer();
    if (!viewer) {
        return;
    }
    std::string mode(viewer->getOverrideMode());
    auto actionGroup = dynamic_cast<Gui::ActionGroup*>(_pcAction);
    if (!actionGroup) {
        return;
    }

    if (mode == "Point") {
        actionGroup->setCheckedAction(1);
        return;
    }
    if (mode == "Wireframe") {
        actionGroup->setCheckedAction(2);
        return;
    }
    if (mode == "Hidden Line") {
        actionGroup->setCheckedAction(3);
        return;
    }
    if (mode == "No shading") {
        actionGroup->setCheckedAction(4);
        return;
    }
    if (mode == "Shaded") {
        actionGroup->setCheckedAction(5);
        return;
    }
    if (mode == "Flat Lines") {
        actionGroup->setCheckedAction(6);
        return;
    }
    actionGroup->setCheckedAction(0);
}

void StdCmdDrawStyle::activated(int iMsg)
{
    Gui::Document* doc = this->getActiveGuiDocument();
    std::list<MDIView*> views = doc->getMDIViews();
    std::list<MDIView*>::iterator viewIt;
    bool oneChangedSignal(false);
    for (viewIt = views.begin(); viewIt != views.end(); ++viewIt) {
        auto view = qobject_cast<View3DInventor*>(*viewIt);
        if (view) {
            View3DInventorViewer* viewer;
            viewer = view->getViewer();
            if (viewer) {
                switch (iMsg) {
                    case 1:
                        (oneChangedSignal) ? viewer->updateOverrideMode("Point")
                                           : viewer->setOverrideMode("Point");
                        break;
                    case 2:
                        (oneChangedSignal) ? viewer->updateOverrideMode("Wireframe")
                                           : viewer->setOverrideMode("Wireframe");
                        break;
                    case 3:
                        (oneChangedSignal) ? viewer->updateOverrideMode("Hidden Line")
                                           : viewer->setOverrideMode("Hidden Line");
                        break;
                    case 4:
                        (oneChangedSignal) ? viewer->updateOverrideMode("No Shading")
                                           : viewer->setOverrideMode("No Shading");
                        break;
                    case 5:
                        (oneChangedSignal) ? viewer->updateOverrideMode("Shaded")
                                           : viewer->setOverrideMode("Shaded");
                        break;
                    case 6:
                        (oneChangedSignal) ? viewer->updateOverrideMode("Flat Lines")
                                           : viewer->setOverrideMode("Flat Lines");
                        break;
                    default:
                        (oneChangedSignal) ? viewer->updateOverrideMode("As Is")
                                           : viewer->setOverrideMode("As Is");
                        break;
                }
                oneChangedSignal = true;
            }
        }
    }
}

bool StdCmdDrawStyle::isActive()
{
    return Gui::Application::Instance->activeDocument();
}

//===========================================================================
// Std_ToggleVisibility
//===========================================================================
DEF_STD_CMD_A(StdCmdToggleVisibility)

StdCmdToggleVisibility::StdCmdToggleVisibility()
    : Command("Std_ToggleVisibility")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Toggle &Visibility");
    sToolTipText = QT_TR_NOOP("Toggles the visibility of the selection");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_ToggleVisibility";
    sPixmap = "Std_ToggleVisibility";
    sAccel = "Space";
    eType = Alter3DView;
}


void StdCmdToggleVisibility::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    TransactionView transaction(
        getActiveGuiDocument(),
        QT_TRANSLATE_NOOP("Command", "Toggle Visibility")
    );
    Selection().setVisible(SelectionSingleton::VisToggle);
}

bool StdCmdToggleVisibility::isActive()
{
    return (Gui::Selection().size() != 0);
}

//===========================================================================
// Std_ToggleTransparency
//===========================================================================
DEF_STD_CMD_A(StdCmdToggleTransparency)

StdCmdToggleTransparency::StdCmdToggleTransparency()
    : Command("Std_ToggleTransparency")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Toggle Transparenc&y");
    static std::string toolTip = std::string("<p>")
        + QT_TR_NOOP("Toggles the transparency of the selected objects. Transparency "
                     "can be fine-tuned in the appearance task dialog")
        + "</p>";
    sToolTipText = toolTip.c_str();
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_ToggleTransparency";
    sPixmap = "Std_ToggleTransparency";
    sAccel = "V,T";
    eType = Alter3DView;
}

void StdCmdToggleTransparency::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    getActiveGuiDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Toggle Transparency"));

    std::vector<Gui::SelectionSingleton::SelObj> sels = Gui::Selection().getCompleteSelection();

    std::vector<Gui::ViewProvider*> viewsToToggle = {};

    for (Gui::SelectionSingleton::SelObj& sel : sels) {
        App::DocumentObject* obj = sel.pObject;
        if (!obj) {
            continue;
        }

        bool isGroup = dynamic_cast<App::Part*>(obj) || dynamic_cast<App::LinkGroup*>(obj)
            || dynamic_cast<App::DocumentObjectGroup*>(obj);

        auto addObjects = [](App::DocumentObject* obj, std::vector<Gui::ViewProvider*>& views) {
            App::Document* doc = obj->getDocument();
            Gui::ViewProvider* view = Application::Instance->getDocument(doc)->getViewProvider(obj);
            App::Property* prop = view->getPropertyByName("Transparency");
            if (prop && prop->isDerivedFrom<App::PropertyInteger>()) {
                // To prevent toggling the tip of a PD body (see #11353), we check if the parent has
                // a Tip prop.
                const std::vector<App::DocumentObject*> parents = obj->getInList();
                if (!parents.empty()) {
                    App::Document* parentDoc = parents[0]->getDocument();
                    Gui::ViewProvider* parentView
                        = Application::Instance->getDocument(parentDoc)->getViewProvider(parents[0]);
                    App::Property* parentProp = parents[0]->getPropertyByName("Tip");
                    if (parentProp) {
                        // Make sure it has a transparency prop too
                        parentProp = parentView->getPropertyByName("Transparency");
                        if (parentProp && parentProp->isDerivedFrom<App::PropertyInteger>()) {
                            view = parentView;
                        }
                    }
                }

                if (std::ranges::find(views, view) == views.end()) {
                    views.push_back(view);
                }
            }
        };

        if (isGroup) {
            for (App::DocumentObject* subobj : obj->getOutListRecursive()) {
                addObjects(subobj, viewsToToggle);
            }
        }
        else {
            addObjects(obj, viewsToToggle);
        }
    }

    bool oneTransparent = false;
    for (auto* view : viewsToToggle) {
        App::Property* prop = view->getPropertyByName("Transparency");
        if (prop && prop->isDerivedFrom<App::PropertyInteger>()) {
            auto* transparencyProp = dynamic_cast<App::PropertyInteger*>(prop);
            int transparency = transparencyProp->getValue();
            if (transparency != 0) {
                oneTransparent = true;
            }
        }
    }

    auto hGrp = App::GetApplication().GetParameterGroupByPath(
        "User parameter:BaseApp/Preferences/View"
    );
    int userTransparency = hGrp->GetInt("ToggleTransparency", 70);

    int transparency = oneTransparent ? 0 : userTransparency;

    for (auto* view : viewsToToggle) {
        App::Property* prop = view->getPropertyByName("Transparency");
        if (prop && prop->isDerivedFrom<App::PropertyInteger>()) {
            auto* transparencyProp = dynamic_cast<App::PropertyInteger*>(prop);
            transparencyProp->setValue(transparency);
        }
    }

    getActiveGuiDocument()->commitCommand();
}

bool StdCmdToggleTransparency::isActive()
{
    return (Gui::Selection().size() != 0);
}

//===========================================================================
// Std_ToggleSelectability
//===========================================================================
DEF_STD_CMD_A(StdCmdToggleSelectability)

StdCmdToggleSelectability::StdCmdToggleSelectability()
    : Command("Std_ToggleSelectability")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Toggle Se&lectability");
    sToolTipText = QT_TR_NOOP("Toggles the property of the objects to get selected in the 3D view");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_ToggleSelectability";
    sPixmap = "view-unselectable";
    eType = Alter3DView;
}

void StdCmdToggleSelectability::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    // go through all documents
    const std::vector<App::Document*> docs = App::GetApplication().getDocuments();
    for (const auto& doc : docs) {
        Document* pcDoc = Application::Instance->getDocument(doc);
        std::vector<App::DocumentObject*> sel
            = Selection().getObjectsOfType(App::DocumentObject::getClassTypeId(), doc->getName());

        if (sel.empty()) {
            continue;
        }

        TransactionView transaction(pcDoc, QT_TRANSLATE_NOOP("Command", "Toggle Selectability"));

        for (const auto& ft : sel) {
            ViewProvider* pr = pcDoc->getViewProviderByName(ft->getNameInDocument());
            if (pr && pr->isDerivedFrom<ViewProviderGeometryObject>()) {
                if (static_cast<ViewProviderGeometryObject*>(pr)->Selectable.getValue()) {
                    doCommand(
                        Gui,
                        "Gui.getDocument(\"%s\").getObject(\"%s\").Selectable=False",
                        doc->getName(),
                        ft->getNameInDocument()
                    );
                }
                else {
                    doCommand(
                        Gui,
                        "Gui.getDocument(\"%s\").getObject(\"%s\").Selectable=True",
                        doc->getName(),
                        ft->getNameInDocument()
                    );
                }
            }
        }
    }
}

bool StdCmdToggleSelectability::isActive()
{
    return (Gui::Selection().size() != 0);
}

//===========================================================================
// Std_ShowSelection
//===========================================================================
DEF_STD_CMD_A(StdCmdShowSelection)

StdCmdShowSelection::StdCmdShowSelection()
    : Command("Std_ShowSelection")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Sho&w Selection");
    sToolTipText = QT_TR_NOOP("Shows all selected objects");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_ShowSelection";
    sPixmap = "Std_ShowSelection";
    eType = Alter3DView;
}

void StdCmdShowSelection::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    Selection().setVisible(SelectionSingleton::VisShow);
}

bool StdCmdShowSelection::isActive()
{
    return (Gui::Selection().size() != 0);
}

//===========================================================================
// Std_HideSelection
//===========================================================================
DEF_STD_CMD_A(StdCmdHideSelection)

StdCmdHideSelection::StdCmdHideSelection()
    : Command("Std_HideSelection")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&Hide Selection");
    sToolTipText = QT_TR_NOOP("Hides all selected objects");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_HideSelection";
    sPixmap = "Std_HideSelection";
    eType = Alter3DView;
}

void StdCmdHideSelection::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    Selection().setVisible(SelectionSingleton::VisHide);
}

bool StdCmdHideSelection::isActive()
{
    return (Gui::Selection().size() != 0);
}

//===========================================================================
// Std_SelectVisibleObjects
//===========================================================================
DEF_STD_CMD_A(StdCmdSelectVisibleObjects)

StdCmdSelectVisibleObjects::StdCmdSelectVisibleObjects()
    : Command("Std_SelectVisibleObjects")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&Select Visible Objects");
    sToolTipText = QT_TR_NOOP("Selects all visible objects in the active document");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_SelectVisibleObjects";
    sPixmap = "Std_SelectVisibleObjects";
    eType = Alter3DView;
}

void StdCmdSelectVisibleObjects::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    // go through active document
    Gui::Document* doc = Application::Instance->activeDocument();
    App::Document* app = doc->getDocument();
    const std::vector<App::DocumentObject*> obj = app->getObjectsOfType(
        App::DocumentObject::getClassTypeId()
    );

    std::vector<App::DocumentObject*> visible;
    visible.reserve(obj.size());
    for (const auto& it : obj) {
        if (doc->isShow(it->getNameInDocument())) {
            visible.push_back(it);
        }
    }

    SelectionSingleton& rSel = Selection();
    rSel.setSelection(app->getName(), visible);
}

bool StdCmdSelectVisibleObjects::isActive()
{
    return App::GetApplication().getActiveDocument();
}

//===========================================================================
// Std_ToggleObjects
//===========================================================================
DEF_STD_CMD_A(StdCmdToggleObjects)

StdCmdToggleObjects::StdCmdToggleObjects()
    : Command("Std_ToggleObjects")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("To&ggle All Objects");
    sToolTipText = QT_TR_NOOP("Toggles the visibility of all objects in the active document");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_ToggleObjects";
    sPixmap = "Std_ToggleObjects";
    eType = Alter3DView;
}

void StdCmdToggleObjects::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    // go through active document
    Gui::Document* doc = Application::Instance->activeDocument();
    App::Document* app = doc->getDocument();
    const std::vector<App::DocumentObject*> obj = app->getObjectsOfType(
        App::DocumentObject::getClassTypeId()
    );

    for (const auto& it : obj) {
        if (doc->isShow(it->getNameInDocument())) {
            doCommand(
                Gui,
                "Gui.getDocument(\"%s\").getObject(\"%s\").Visibility=False",
                app->getName(),
                it->getNameInDocument()
            );
        }
        else {
            doCommand(
                Gui,
                "Gui.getDocument(\"%s\").getObject(\"%s\").Visibility=True",
                app->getName(),
                it->getNameInDocument()
            );
        }
    }
}

bool StdCmdToggleObjects::isActive()
{
    return App::GetApplication().getActiveDocument();
}

//===========================================================================
// Std_ShowObjects
//===========================================================================
DEF_STD_CMD_A(StdCmdShowObjects)

StdCmdShowObjects::StdCmdShowObjects()
    : Command("Std_ShowObjects")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Show &All Objects");
    sToolTipText = QT_TR_NOOP("Shows all objects in the document");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_ShowObjects";
    sPixmap = "Std_ShowObjects";
    eType = Alter3DView;
}

void StdCmdShowObjects::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    // go through active document
    Gui::Document* doc = Application::Instance->activeDocument();
    App::Document* app = doc->getDocument();
    const std::vector<App::DocumentObject*> obj = app->getObjectsOfType(
        App::DocumentObject::getClassTypeId()
    );

    for (const auto& it : obj) {
        doCommand(
            Gui,
            "Gui.getDocument(\"%s\").getObject(\"%s\").Visibility=True",
            app->getName(),
            it->getNameInDocument()
        );
    }
}

bool StdCmdShowObjects::isActive()
{
    return App::GetApplication().getActiveDocument();
}

//===========================================================================
// Std_HideObjects
//===========================================================================
DEF_STD_CMD_A(StdCmdHideObjects)

StdCmdHideObjects::StdCmdHideObjects()
    : Command("Std_HideObjects")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Hide All &Objects");
    sToolTipText = QT_TR_NOOP("Hides all objects in the document");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_HideObjects";
    sPixmap = "Std_HideObjects";
    eType = Alter3DView;
}

void StdCmdHideObjects::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    // go through active document
    Gui::Document* doc = Application::Instance->activeDocument();
    App::Document* app = doc->getDocument();
    const std::vector<App::DocumentObject*> obj = app->getObjectsOfType(
        App::DocumentObject::getClassTypeId()
    );

    for (const auto& it : obj) {
        doCommand(
            Gui,
            "Gui.getDocument(\"%s\").getObject(\"%s\").Visibility=False",
            app->getName(),
            it->getNameInDocument()
        );
    }
}

bool StdCmdHideObjects::isActive()
{
    return App::GetApplication().getActiveDocument();
}

//===========================================================================
// Std_ViewHome
//===========================================================================
DEF_3DV_CMD(StdCmdViewHome)

StdCmdViewHome::StdCmdViewHome()
    : Command("Std_ViewHome")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&Home");
    sToolTipText = QT_TR_NOOP("Sets the camera to the default home view");
    sWhatsThis = "Std_ViewHome";
    sStatusTip = sToolTipText;
    sPixmap = "Std_ViewHome";
    sAccel = "Home";
    eType = Alter3DView;
}

void StdCmdViewHome::activated(int iMsg)
{
    Q_UNUSED(iMsg);

    auto hGrp = App::GetApplication().GetParameterGroupByPath(
        "User parameter:BaseApp/Preferences/View"
    );
    std::string default_view = hGrp->GetASCII("NewDocumentCameraOrientation", "Top");
    doCommand(
        Command::Gui,
        "Gui.activeDocument().activeView().viewDefaultOrientation('%s',0)",
        default_view.c_str()
    );
    doCommand(Command::Gui, "Gui.SendMsgToActiveView(\"ViewFit\")");
}

//===========================================================================
// Std_ViewBottom
//===========================================================================
DEF_3DV_CMD(StdCmdViewBottom)

StdCmdViewBottom::StdCmdViewBottom()
    : Command("Std_ViewBottom")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&5 Bottom");
    sToolTipText = QT_TR_NOOP("Sets the camera to the bottom view");
    sWhatsThis = "Std_ViewBottom";
    sStatusTip = sToolTipText;
    sPixmap = "view-bottom";
    sAccel = "5";
    eType = Alter3DView;
}

void StdCmdViewBottom::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().viewBottom()");
}

//===========================================================================
// Std_ViewFront
//===========================================================================
DEF_3DV_CMD(StdCmdViewFront)

StdCmdViewFront::StdCmdViewFront()
    : Command("Std_ViewFront")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&1 Front");
    sToolTipText = QT_TR_NOOP("Sets the camera to the front view");
    sWhatsThis = "Std_ViewFront";
    sStatusTip = sToolTipText;
    sPixmap = "view-front";
    sAccel = "1";
    eType = Alter3DView;
}

void StdCmdViewFront::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().viewFront()");
}

//===========================================================================
// Std_ViewLeft
//===========================================================================
DEF_3DV_CMD(StdCmdViewLeft)

StdCmdViewLeft::StdCmdViewLeft()
    : Command("Std_ViewLeft")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&6 Left");
    sToolTipText = QT_TR_NOOP("Sets the camera to the left view");
    sWhatsThis = "Std_ViewLeft";
    sStatusTip = sToolTipText;
    sPixmap = "view-left";
    sAccel = "6";
    eType = Alter3DView;
}

void StdCmdViewLeft::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().viewLeft()");
}

//===========================================================================
// Std_ViewRear
//===========================================================================
DEF_3DV_CMD(StdCmdViewRear)

StdCmdViewRear::StdCmdViewRear()
    : Command("Std_ViewRear")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&4 Rear");
    sToolTipText = QT_TR_NOOP("Sets the camera to the rear view");
    sWhatsThis = "Std_ViewRear";
    sStatusTip = sToolTipText;
    sPixmap = "view-rear";
    sAccel = "4";
    eType = Alter3DView;
}

void StdCmdViewRear::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().viewRear()");
}

//===========================================================================
// Std_ViewRight
//===========================================================================
DEF_3DV_CMD(StdCmdViewRight)

StdCmdViewRight::StdCmdViewRight()
    : Command("Std_ViewRight")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&3 Right");
    sToolTipText = QT_TR_NOOP("Sets the camera to the right view");
    sWhatsThis = "Std_ViewRight";
    sStatusTip = sToolTipText;
    sPixmap = "view-right";
    sAccel = "3";
    eType = Alter3DView;
}

void StdCmdViewRight::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().viewRight()");
}

//===========================================================================
// Std_ViewTop
//===========================================================================
DEF_3DV_CMD(StdCmdViewTop)

StdCmdViewTop::StdCmdViewTop()
    : Command("Std_ViewTop")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&2 Top");
    sToolTipText = QT_TR_NOOP("Sets the camera to the top view");
    sWhatsThis = "Std_ViewTop";
    sStatusTip = sToolTipText;
    sPixmap = "view-top";
    sAccel = "2";
    eType = Alter3DView;
}

void StdCmdViewTop::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().viewTop()");
}


//===========================================================================
// Std_ViewIsometric
//===========================================================================
DEF_3DV_CMD(StdCmdViewIsometric)

StdCmdViewIsometric::StdCmdViewIsometric()
    : Command("Std_ViewIsometric")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&Isometric");
    sToolTipText = QT_TR_NOOP("Sets the camera to the isometric view");
    sWhatsThis = "Std_ViewIsometric";
    sStatusTip = sToolTipText;
    sPixmap = "view-axonometric";
    sAccel = "0";
    eType = Alter3DView;
}

void StdCmdViewIsometric::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().viewIsometric()");
}

//===========================================================================
// Std_ViewDimetric
//===========================================================================
DEF_3DV_CMD(StdCmdViewDimetric)

StdCmdViewDimetric::StdCmdViewDimetric()
    : Command("Std_ViewDimetric")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&Dimetric");
    sToolTipText = QT_TR_NOOP("Sets the camera to the dimetric view");
    sWhatsThis = "Std_ViewDimetric";
    sStatusTip = sToolTipText;
    sPixmap = "Std_ViewDimetric";
    eType = Alter3DView;
}

void StdCmdViewDimetric::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().viewDimetric()");
}

//===========================================================================
// Std_ViewTrimetric
//===========================================================================
DEF_3DV_CMD(StdCmdViewTrimetric)

StdCmdViewTrimetric::StdCmdViewTrimetric()
    : Command("Std_ViewTrimetric")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&Trimetric");
    sToolTipText = QT_TR_NOOP("Sets the camera to the trimetric view");
    sWhatsThis = "Std_ViewTrimetric";
    sStatusTip = sToolTipText;
    sPixmap = "Std_ViewTrimetric";
    eType = Alter3DView;
}

void StdCmdViewTrimetric::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().viewTrimetric()");
}

//===========================================================================
// Std_ViewRotateLeft
//===========================================================================
DEF_3DV_CMD(StdCmdViewRotateLeft)

StdCmdViewRotateLeft::StdCmdViewRotateLeft()
    : Command("Std_ViewRotateLeft")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Rotate &Left");
    sToolTipText = QT_TR_NOOP("Rotates the view by 90\xc2\xb0 counter-clockwise");
    sWhatsThis = "Std_ViewRotateLeft";
    sStatusTip = sToolTipText;
    sPixmap = "view-rotate-left";
    sAccel = "Shift+Left";
    eType = Alter3DView;
}

void StdCmdViewRotateLeft::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().viewRotateLeft()");
}


//===========================================================================
// Std_ViewRotateRight
//===========================================================================
DEF_3DV_CMD(StdCmdViewRotateRight)

StdCmdViewRotateRight::StdCmdViewRotateRight()
    : Command("Std_ViewRotateRight")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Rotates &Right");
    sToolTipText = QT_TR_NOOP("Rotates the view by 90\xc2\xb0 clockwise");
    sWhatsThis = "Std_ViewRotateRight";
    sStatusTip = sToolTipText;
    sPixmap = "view-rotate-right";
    sAccel = "Shift+Right";
    eType = Alter3DView;
}

void StdCmdViewRotateRight::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().viewRotateRight()");
}


//===========================================================================
// Std_ViewFitAll
//===========================================================================
DEF_STD_CMD_A(StdCmdViewFitAll)

StdCmdViewFitAll::StdCmdViewFitAll()
    : Command("Std_ViewFitAll")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&Fit All");
    sToolTipText = QT_TR_NOOP("Fits all content into the 3D view");
    sWhatsThis = "Std_ViewFitAll";
    sStatusTip = sToolTipText;
    sPixmap = "zoom-all";
    sAccel = "V, F";
    eType = Alter3DView;
}

void StdCmdViewFitAll::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    // doCommand(Command::Gui,"Gui.activeDocument().activeView().fitAll()");
    doCommand(Command::Gui, "Gui.SendMsgToActiveView(\"ViewFit\")");
}

bool StdCmdViewFitAll::isActive()
{
    // return isViewOfType(Gui::View3DInventor::getClassTypeId());
    return getGuiApplication()->sendHasMsgToActiveView("ViewFit");
}

//===========================================================================
// Std_ViewFitSelection
//===========================================================================
DEF_STD_CMD_A(StdCmdViewFitSelection)

StdCmdViewFitSelection::StdCmdViewFitSelection()
    : Command("Std_ViewFitSelection")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Fit &Selection");
    sToolTipText = QT_TR_NOOP("Fits the selected content into the 3D view");
    sWhatsThis = "Std_ViewFitSelection";
    sStatusTip = sToolTipText;
    sAccel = "V, S";
    sPixmap = "zoom-selection";
    eType = Alter3DView;
}

void StdCmdViewFitSelection::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.SendMsgToActiveView(\"ViewSelection\")");
}

bool StdCmdViewFitSelection::isActive()
{
    return getGuiApplication()->sendHasMsgToActiveView("ViewSelection");
}

//===========================================================================
// Std_ViewCommandGroup
//===========================================================================
class StdCmdViewGroup: public Gui::GroupCommand
{
public:
    StdCmdViewGroup()
        : GroupCommand("Std_ViewGroup")
    {
        sGroup = "Standard-View";
        sMenuText = QT_TR_NOOP("Standard &Views");
        sToolTipText = QT_TR_NOOP("Changes to a standard view");
        sStatusTip = sToolTipText;
        sWhatsThis = "Std_ViewGroup";
        sPixmap = "view-isometric";
        eType = Alter3DView;

        setCheckable(false);
        setRememberLast(true);

        addCommand("Std_ViewIsometric");
        addCommand("Std_ViewFront");
        addCommand("Std_ViewTop");
        addCommand("Std_ViewRight");
        addCommand("Std_ViewRear");
        addCommand("Std_ViewBottom");
        addCommand("Std_ViewLeft");
    }

    const char* className() const override
    {
        return "StdCmdViewGroup";
    }

    bool isActive() override
    {
        return hasActiveDocument();
    }
};

//===========================================================================
// Std_ViewDock
//===========================================================================
DEF_STD_CMD_A(StdViewDock)

StdViewDock::StdViewDock()
    : Command("Std_ViewDock")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&Docked");
    sToolTipText = QT_TR_NOOP(
        "Displays the active view either in fullscreen, undocked, or docked mode"
    );
    sWhatsThis = "Std_ViewDock";
    sStatusTip = sToolTipText;
    sAccel = "V, D";
    eType = Alter3DView;
    bCanLog = false;
}

void StdViewDock::activated(int iMsg)
{
    Q_UNUSED(iMsg);
}

bool StdViewDock::isActive()
{
    MDIView* view = getMainWindow()->activeWindow();
    return view != nullptr;
}

//===========================================================================
// Std_ViewUndock
//===========================================================================
DEF_STD_CMD_A(StdViewUndock)

StdViewUndock::StdViewUndock()
    : Command("Std_ViewUndock")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&Undocked");
    sToolTipText = QT_TR_NOOP(
        "Displays the active view either in fullscreen, undocked, or docked mode"
    );
    sWhatsThis = "Std_ViewUndock";
    sStatusTip = sToolTipText;
    sAccel = "V, U";
    eType = Alter3DView;
    bCanLog = false;
}

void StdViewUndock::activated(int iMsg)
{
    Q_UNUSED(iMsg);
}

bool StdViewUndock::isActive()
{
    MDIView* view = getMainWindow()->activeWindow();
    return view != nullptr;
}

//===========================================================================
// Std_MainFullscreen
//===========================================================================
DEF_STD_CMD(StdMainFullscreen)

StdMainFullscreen::StdMainFullscreen()
    : Command("Std_MainFullscreen")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Fullscreen");
    sToolTipText = QT_TR_NOOP("Displays the main window in fullscreen mode");
    sWhatsThis = "Std_MainFullscreen";
    sStatusTip = sToolTipText;
    sPixmap = "view-fullscreen";
    sAccel = "Alt+F11";
    eType = Alter3DView;
}

void StdMainFullscreen::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    MDIView* view = getMainWindow()->activeWindow();

    if (view) {
        view->setCurrentViewMode(MDIView::Child);
    }

    if (getMainWindow()->isFullScreen()) {
        getMainWindow()->showNormal();
    }
    else {
        getMainWindow()->showFullScreen();
    }
}

//===========================================================================
// Std_ViewFullscreen
//===========================================================================
DEF_STD_CMD_A(StdViewFullscreen)

StdViewFullscreen::StdViewFullscreen()
    : Command("Std_ViewFullscreen")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&Fullscreen");
    sToolTipText = QT_TR_NOOP(
        "Displays the active view either in fullscreen, undocked, or docked mode"
    );
    sWhatsThis = "Std_ViewFullscreen";
    sStatusTip = sToolTipText;
    sPixmap = "view-fullscreen";
    sAccel = "F11";
    eType = Alter3DView;
    bCanLog = false;
}

void StdViewFullscreen::activated(int iMsg)
{
    Q_UNUSED(iMsg);
}

bool StdViewFullscreen::isActive()
{
    MDIView* view = getMainWindow()->activeWindow();
    return view != nullptr;
}

//===========================================================================
// Std_ViewDockUndockFullscreen
//===========================================================================
DEF_STD_CMD_AC(StdViewDockUndockFullscreen)

StdViewDockUndockFullscreen::StdViewDockUndockFullscreen()
    : Command("Std_ViewDockUndockFullscreen")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("D&ocument Window");
    sToolTipText = QT_TR_NOOP(
        "Displays the active view either in fullscreen, undocked, or docked mode"
    );
    sWhatsThis = "Std_ViewDockUndockFullscreen";
    sStatusTip = sToolTipText;
    eType = Alter3DView;

    CommandManager& rcCmdMgr = Application::Instance->commandManager();
    rcCmdMgr.addCommand(new StdViewDock());
    rcCmdMgr.addCommand(new StdViewUndock());
    rcCmdMgr.addCommand(new StdViewFullscreen());
}

Action* StdViewDockUndockFullscreen::createAction()
{
    auto pcAction = new ActionGroup(this, getMainWindow());
    pcAction->setDropDownMenu(true);
    pcAction->setText(QCoreApplication::translate(this->className(), getMenuText()));

    CommandManager& rcCmdMgr = Application::Instance->commandManager();
    Command* cmdD = rcCmdMgr.getCommandByName("Std_ViewDock");
    Command* cmdU = rcCmdMgr.getCommandByName("Std_ViewUndock");
    Command* cmdF = rcCmdMgr.getCommandByName("Std_ViewFullscreen");
    cmdD->addToGroup(pcAction, true);
    cmdU->addToGroup(pcAction, true);
    cmdF->addToGroup(pcAction, true);

    return pcAction;
}

void StdViewDockUndockFullscreen::activated(int iMsg)
{
    // Check if main window is in fullscreen mode.
    if (getMainWindow()->isFullScreen()) {
        getMainWindow()->showNormal();
    }

    MDIView* view = getMainWindow()->activeWindow();
    if (!view) {  // no active view
        return;
    }

    const auto oldmode = view->currentViewMode();
    auto mode = (MDIView::ViewMode)iMsg;

    // Pressing the same button again toggles the view back to docked.
    if (mode == oldmode) {
        mode = MDIView::Child;
    }

    if (mode == oldmode) {
        return;
    }

    // Change the view mode after an mdi view was already visible doesn't
    // work well with Qt5 any more because of some strange OpenGL behaviour.
    // A workaround is to clone the mdi view, set its view mode and delete
    // the original view.

    bool needsClone = mode == MDIView::Child || oldmode == MDIView::Child;
    Gui::MDIView* clone = needsClone ? view->clone() : nullptr;

    if (clone) {
        if (mode == MDIView::Child) {
            getMainWindow()->addWindow(clone);
        }
        else {
            clone->setCurrentViewMode(mode);
        }

        // destroy the old view
        view->deleteSelf();
    }
    else {
        // no clone needed, simply change the view mode
        view->setCurrentViewMode(mode);
    }
}

bool StdViewDockUndockFullscreen::isActive()
{
    MDIView* view = getMainWindow()->activeWindow();
    if (!view) {
        return false;
    }

    // update the action group if needed
    auto pActGrp = qobject_cast<ActionGroup*>(_pcAction);
    if (pActGrp) {
        int index = pActGrp->checkedAction();
        int mode = (int)(view->currentViewMode());
        if (index != mode) {
            // active window has changed with another view mode
            pActGrp->setCheckedAction(mode);
        }
    }

    return true;
}


//===========================================================================
// Std_ViewVR
//===========================================================================
DEF_STD_CMD_A(StdCmdViewVR)

StdCmdViewVR::StdCmdViewVR()
    : Command("Std_ViewVR")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("FreeCAD VR");
    sToolTipText = QT_TR_NOOP("Extends the FreeCAD 3D Window to a VR device");
    sWhatsThis = "Std_ViewVR";
    sStatusTip = sToolTipText;
    eType = Alter3DView;
}

void StdCmdViewVR::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.SendMsgToActiveView(\"ViewVR\")");
}

bool StdCmdViewVR::isActive()
{
    return getGuiApplication()->sendHasMsgToActiveView("ViewVR");
}


//===========================================================================
// Std_ViewScreenShot
//===========================================================================
DEF_STD_CMD_A(StdViewScreenShot)

StdViewScreenShot::StdViewScreenShot()
    : Command("Std_ViewScreenShot")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Save &Image…");
    sToolTipText = QT_TR_NOOP("Creates a screenshot of the active view");
    sWhatsThis = "Std_ViewScreenShot";
    sStatusTip = sToolTipText;
    sPixmap = "Std_ViewScreenShot";
    eType = Alter3DView;
}

void StdViewScreenShot::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
    if (view) {
        QStringList formats;
        SbViewportRegion vp(view->getViewer()->getSoRenderManager()->getViewportRegion());
        {
            SoQtOffscreenRenderer rd(vp);
            formats = rd.getWriteImageFiletypeInfo();
        }

        Base::Reference<ParameterGrp> hExt = App::GetApplication()
                                                 .GetUserParameter()
                                                 .GetGroup("BaseApp")
                                                 ->GetGroup("Preferences")
                                                 ->GetGroup("General");
        QString ext = QString::fromLatin1(hExt->GetASCII("OffscreenImageFormat").c_str());
        int backtype = hExt->GetInt("OffscreenImageBackground", 0);

        Base::Reference<ParameterGrp> methodGrp = App::GetApplication().GetParameterGroupByPath(
            "User parameter:BaseApp/Preferences/View"
        );
        QByteArray method = methodGrp->GetASCII("SavePicture").c_str();

        QStringList filter;
        QString selFilter;
        for (QStringList::Iterator it = formats.begin(); it != formats.end(); ++it) {
            filter << QStringLiteral("%1 %2 (*.%3)")
                          .arg((*it).toUpper(), QObject::tr("files"), (*it).toLower());
            if (ext == *it) {
                selFilter = filter.last();
            }
        }

        FileOptionsDialog fd(getMainWindow(), Qt::WindowFlags());
        fd.setFileMode(QFileDialog::AnyFile);
        fd.setAcceptMode(QFileDialog::AcceptSave);
        fd.setWindowTitle(QObject::tr("Save Image"));
        fd.setNameFilters(filter);
        if (!selFilter.isEmpty()) {
            fd.selectNameFilter(selFilter);
        }

        // create the image options widget
        auto opt = new DlgSettingsImageImp(&fd);
        SbVec2s sz = vp.getWindowSize();
        opt->setImageSize((int)sz[0], (int)sz[1]);
        opt->setBackgroundType(backtype);
        opt->setMethod(method);

        fd.setOptionsWidget(FileOptionsDialog::ExtensionRight, opt);
        fd.setOption(QFileDialog::DontConfirmOverwrite, false);
        opt->onSelectedFilter(fd.selectedNameFilter());
        QObject::connect(
            &fd,
            &FileOptionsDialog::filterSelected,
            opt,
            &DlgSettingsImageImp::onSelectedFilter
        );

        if (fd.exec() == QDialog::Accepted) {
            selFilter = fd.selectedNameFilter();
            QString fn = fd.selectedFiles().constFirst();
            // We must convert '\' path separators to '/' before otherwise
            // Python would interpret them as escape sequences.
            fn.replace(QLatin1Char('\\'), QLatin1Char('/'));

            Gui::WaitCursor wc;

            // get the defined values
            int w = opt->imageWidth();
            int h = opt->imageHeight();

            // search for the matching format
            QString format = formats.front();  // take the first as default
            for (QStringList::Iterator it = formats.begin(); it != formats.end(); ++it) {
                if (selFilter.startsWith((*it).toUpper())) {
                    format = *it;
                    break;
                }
            }

            hExt->SetASCII("OffscreenImageFormat", (const char*)format.toLatin1());

            method = opt->method();
            methodGrp->SetASCII("SavePicture", method.constData());

            // which background chosen
            const char* background;
            switch (opt->backgroundType()) {
                case 0:
                    background = "Current";
                    break;
                case 1:
                    background = "White";
                    break;
                case 2:
                    background = "Black";
                    break;
                case 3:
                    background = "Transparent";
                    break;
                default:
                    background = "Current";
                    break;
            }
            hExt->SetInt("OffscreenImageBackground", opt->backgroundType());

            QString comment = opt->comment();
            if (!comment.isEmpty()) {
                // Replace newline escape sequence through '\\n' string to build one big string,
                // otherwise Python would interpret it as an invalid command.
                // Python does the decoding for us.
                QStringList lines = comment.split(QLatin1String("\n"), Qt::KeepEmptyParts);

                comment = lines.join(QLatin1String("\\n"));
                doCommand(
                    Gui,
                    "Gui.activeDocument().activeView().saveImage('%s',%d,%d,'%s','%s')",
                    fn.toUtf8().constData(),
                    w,
                    h,
                    background,
                    comment.toUtf8().constData()
                );
            }
            else {
                doCommand(
                    Gui,
                    "Gui.activeDocument().activeView().saveImage('%s',%d,%d,'%s')",
                    fn.toUtf8().constData(),
                    w,
                    h,
                    background
                );
            }

            // When adding a watermark check if the image could be created
            if (opt->addWatermark()) {
                QFileInfo fi(fn);
                QPixmap pixmap;
                if (fi.exists() && pixmap.load(fn)) {
                    QString name = qApp->applicationName();
                    std::map<std::string, std::string>& config = App::Application::Config();
                    QString url = QString::fromLatin1(config["MaintainerUrl"].c_str());
                    url = QUrl(url).host();

                    QPixmap appicon = Gui::BitmapFactory().pixmap(config["AppIcon"].c_str());

                    QPainter painter;
                    painter.begin(&pixmap);

                    painter.drawPixmap(8, h - 15 - appicon.height(), appicon);

                    QFont font = painter.font();
                    font.setPointSize(20);

                    QFontMetrics fm(font);
                    int n = QtTools::horizontalAdvance(fm, name);
                    int h = pixmap.height();

                    painter.setFont(font);
                    painter.drawText(8 + appicon.width(), h - 24, name);

                    font.setPointSize(12);
                    QFontMetrics fm2(font);
                    int u = QtTools::horizontalAdvance(fm2, url);
                    painter.setFont(font);
                    painter.drawText(8 + appicon.width() + n - u, h - 6, url);

                    painter.end();
                    pixmap.save(fn);
                }
            }
        }
    }
}

bool StdViewScreenShot::isActive()
{
    return isViewOfType(Gui::View3DInventor::getClassTypeId());
}

//===========================================================================
// Std_ViewLoadImage
//===========================================================================
DEF_STD_CMD(StdViewLoadImage)

StdViewLoadImage::StdViewLoadImage()
    : Command("Std_ViewLoadImage")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&Load Image…");
    sToolTipText = QT_TR_NOOP("Loads an image");
    sWhatsThis = "Std_ViewLoadImage";
    sStatusTip = sToolTipText;
    sPixmap = "image-open";
    eType = 0;
}

void StdViewLoadImage::activated(int iMsg)
{
    Q_UNUSED(iMsg);

    // add all supported QImage formats
    QStringList mimeTypeFilters;
    QList<QByteArray> supportedMimeTypes = QImageReader::supportedMimeTypes();
    for (const auto& mimeTypeName : supportedMimeTypes) {
        mimeTypeFilters.append(QString::fromLatin1(mimeTypeName));
    }

    // Reading an image
    QFileDialog dialog(Gui::getMainWindow());
    dialog.setWindowTitle(QObject::tr("Choose an Image File to Open"));
    dialog.setMimeTypeFilters(mimeTypeFilters);
    dialog.selectMimeTypeFilter(QStringLiteral("image/png"));
    dialog.setDefaultSuffix(QStringLiteral("png"));
    dialog.setAcceptMode(QFileDialog::AcceptOpen);
    dialog.setOption(QFileDialog::DontUseNativeDialog);

    if (dialog.exec()) {
        QString fileName = dialog.selectedFiles().constFirst();
        ImageView* view = new ImageView(Gui::getMainWindow());
        view->loadFile(fileName);
        view->resize(400, 300);
        Gui::getMainWindow()->addWindow(view);
    }
}

//===========================================================================
// Std_ViewCreate
//===========================================================================
DEF_STD_CMD_A(StdCmdViewCreate)

StdCmdViewCreate::StdCmdViewCreate()
    : Command("Std_ViewCreate")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("New 3D View");
    sToolTipText = QT_TR_NOOP("Opens a new 3D view window for the active document");
    sWhatsThis = "Std_ViewCreate";
    sStatusTip = sToolTipText;
    sPixmap = "window-new";
    eType = Alter3DView;
}

void StdCmdViewCreate::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    getActiveGuiDocument()->createView(View3DInventor::getClassTypeId());
    getActiveGuiDocument()->getActiveView()->viewAll();
}

bool StdCmdViewCreate::isActive()
{
    return (getActiveGuiDocument() != nullptr);
}

//===========================================================================
// Std_ToggleNavigation
//===========================================================================
DEF_STD_CMD_A(StdCmdToggleNavigation)

StdCmdToggleNavigation::StdCmdToggleNavigation()
    : Command("Std_ToggleNavigation")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Toggle Navigation/&Edit Mode");
    sToolTipText = QT_TR_NOOP("Toggles between navigation and edit mode");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_ToggleNavigation";
    // iAccel        = Qt::SHIFT+Qt::Key_Space;
    sAccel = "Esc";
    sPixmap = "Std_ToggleNavigation";
    eType = Alter3DView;
}

void StdCmdToggleNavigation::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    Gui::MDIView* view = Gui::getMainWindow()->activeWindow();
    if (view && view->isDerivedFrom<Gui::View3DInventor>()) {
        Gui::View3DInventorViewer* viewer = static_cast<Gui::View3DInventor*>(view)->getViewer();
        SbBool toggle = viewer->isRedirectedToSceneGraph();
        viewer->setRedirectToSceneGraph(!toggle);
    }
}

bool StdCmdToggleNavigation::isActive()
{
    // #0001087: Inventor Navigation continues with released Mouse Button
    // This happens because 'Esc' is also used to close the task dialog.
    // Add also new method 'isRedirectToSceneGraphEnabled' to explicitly
    // check if this is allowed.
    if (Gui::Control().activeDialog()) {
        return false;
    }
    Gui::MDIView* view = Gui::getMainWindow()->activeWindow();
    if (view && view->isDerivedFrom<Gui::View3DInventor>()) {
        Gui::View3DInventorViewer* viewer = static_cast<Gui::View3DInventor*>(view)->getViewer();
        return viewer->isEditing() && viewer->isRedirectToSceneGraphEnabled();
    }
    return false;
}


//===========================================================================
// Std_ViewExample1
//===========================================================================
DEF_STD_CMD_A(StdCmdAxisCross)

StdCmdAxisCross::StdCmdAxisCross()
    : Command("Std_AxisCross")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Toggle A&xis Cross");
    sToolTipText = QT_TR_NOOP("Toggles the axis cross at the origin");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_AxisCross";
    sPixmap = "Std_AxisCross";
    sAccel = "A,C";
}

void StdCmdAxisCross::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    auto view = qobject_cast<View3DInventor*>(Gui::getMainWindow()->activeWindow());
    if (view) {
        if (!view->getViewer()->hasAxisCross()) {
            doCommand(Command::Gui, "Gui.ActiveDocument.ActiveView.setAxisCross(True)");
        }
        else {
            doCommand(Command::Gui, "Gui.ActiveDocument.ActiveView.setAxisCross(False)");
        }
    }
}

bool StdCmdAxisCross::isActive()
{
    auto view = qobject_cast<View3DInventor*>(Gui::getMainWindow()->activeWindow());
    if (view && view->getViewer()->hasAxisCross()) {
        if (!_pcAction->isChecked()) {
            _pcAction->setChecked(true);
        }
    }
    else {
        if (_pcAction->isChecked()) {
            _pcAction->setChecked(false);
        }
    }
    if (view) {
        return true;
    }
    return false;
}

//===========================================================================
// Std_ViewExample1
//===========================================================================
DEF_STD_CMD_A(StdCmdViewExample1)

StdCmdViewExample1::StdCmdViewExample1()
    : Command("Std_ViewExample1")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Inventor Example #1");
    sToolTipText = QT_TR_NOOP("Shows a 3D texture with manipulator");
    sWhatsThis = "Std_ViewExample1";
    sStatusTip = sToolTipText;
    sPixmap = "Std_Tool1";
    eType = Alter3DView;
}

void StdCmdViewExample1::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.SendMsgToActiveView(\"Example1\")");
}

bool StdCmdViewExample1::isActive()
{
    return getGuiApplication()->sendHasMsgToActiveView("Example1");
}

//===========================================================================
// Std_ViewExample2
//===========================================================================
DEF_STD_CMD_A(StdCmdViewExample2)

StdCmdViewExample2::StdCmdViewExample2()
    : Command("Std_ViewExample2")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Inventor Example #2");
    sToolTipText = QT_TR_NOOP("Shows spheres and drag-lights");
    sWhatsThis = "Std_ViewExample2";
    sStatusTip = sToolTipText;
    sPixmap = "Std_Tool2";
    eType = Alter3DView;
}

void StdCmdViewExample2::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.SendMsgToActiveView(\"Example2\")");
}

bool StdCmdViewExample2::isActive()
{
    return getGuiApplication()->sendHasMsgToActiveView("Example2");
}

//===========================================================================
// Std_ViewExample3
//===========================================================================
DEF_STD_CMD_A(StdCmdViewExample3)

StdCmdViewExample3::StdCmdViewExample3()
    : Command("Std_ViewExample3")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Inventor Example #3");
    sToolTipText = QT_TR_NOOP("Shows an animated texture");
    sWhatsThis = "Std_ViewExample3";
    sStatusTip = sToolTipText;
    sPixmap = "Std_Tool3";
    eType = Alter3DView;
}

void StdCmdViewExample3::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.SendMsgToActiveView(\"Example3\")");
}

bool StdCmdViewExample3::isActive()
{
    return getGuiApplication()->sendHasMsgToActiveView("Example3");
}


//===========================================================================
// Std_ViewIvStereoOff
//===========================================================================
DEF_STD_CMD_A(StdCmdViewIvStereoOff)

StdCmdViewIvStereoOff::StdCmdViewIvStereoOff()
    : Command("Std_ViewIvStereoOff")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Stereo &Off");
    sToolTipText = QT_TR_NOOP("Switches stereo viewing off");
    sWhatsThis = "Std_ViewIvStereoOff";
    sStatusTip = sToolTipText;
    sPixmap = "Std_ViewIvStereoOff";
    eType = Alter3DView;
}

void StdCmdViewIvStereoOff::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().setStereoType(\"Mono\")");
}

bool StdCmdViewIvStereoOff::isActive()
{
    return getGuiApplication()->sendHasMsgToActiveView("SetStereoOff");
}


//===========================================================================
// Std_ViewIvStereoRedGreen
//===========================================================================
DEF_STD_CMD_A(StdCmdViewIvStereoRedGreen)

StdCmdViewIvStereoRedGreen::StdCmdViewIvStereoRedGreen()
    : Command("Std_ViewIvStereoRedGreen")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Stereo Re&d/Cyan");
    sToolTipText = QT_TR_NOOP("Switches stereo viewing to red/cyan");
    sWhatsThis = "Std_ViewIvStereoRedGreen";
    sStatusTip = sToolTipText;
    sPixmap = "Std_ViewIvStereoRedGreen";
    eType = Alter3DView;
}

void StdCmdViewIvStereoRedGreen::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().setStereoType(\"Anaglyph\")");
}

bool StdCmdViewIvStereoRedGreen::isActive()
{
    return getGuiApplication()->sendHasMsgToActiveView("SetStereoRedGreen");
}

//===========================================================================
// Std_ViewIvStereoQuadBuff
//===========================================================================
DEF_STD_CMD_A(StdCmdViewIvStereoQuadBuff)

StdCmdViewIvStereoQuadBuff::StdCmdViewIvStereoQuadBuff()
    : Command("Std_ViewIvStereoQuadBuff")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Stereo &Quad Buffer");
    sToolTipText = QT_TR_NOOP("Switches stereo viewing to quad buffer");
    sWhatsThis = "Std_ViewIvStereoQuadBuff";
    sStatusTip = sToolTipText;
    sPixmap = "Std_ViewIvStereoQuadBuff";
    eType = Alter3DView;
}

void StdCmdViewIvStereoQuadBuff::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().setStereoType(\"QuadBuffer\")");
}

bool StdCmdViewIvStereoQuadBuff::isActive()
{
    return getGuiApplication()->sendHasMsgToActiveView("SetStereoQuadBuff");
}

//===========================================================================
// Std_ViewIvStereoInterleavedRows
//===========================================================================
DEF_STD_CMD_A(StdCmdViewIvStereoInterleavedRows)

StdCmdViewIvStereoInterleavedRows::StdCmdViewIvStereoInterleavedRows()
    : Command("Std_ViewIvStereoInterleavedRows")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Stereo Interleaved &Rows");
    sToolTipText = QT_TR_NOOP("Switches stereo viewing to interleaved rows");
    sWhatsThis = "Std_ViewIvStereoInterleavedRows";
    sStatusTip = sToolTipText;
    sPixmap = "Std_ViewIvStereoInterleavedRows";
    eType = Alter3DView;
}

void StdCmdViewIvStereoInterleavedRows::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().setStereoType(\"InterleavedRows\")");
}

bool StdCmdViewIvStereoInterleavedRows::isActive()
{
    return getGuiApplication()->sendHasMsgToActiveView("SetStereoInterleavedRows");
}

//===========================================================================
// Std_ViewIvStereoInterleavedColumns
//===========================================================================
DEF_STD_CMD_A(StdCmdViewIvStereoInterleavedColumns)

StdCmdViewIvStereoInterleavedColumns::StdCmdViewIvStereoInterleavedColumns()
    : Command("Std_ViewIvStereoInterleavedColumns")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Stereo Interleaved &Columns");
    sToolTipText = QT_TR_NOOP("Switches stereo viewing to interleaved columns");
    sWhatsThis = "Std_ViewIvStereoInterleavedColumns";
    sStatusTip = sToolTipText;
    sPixmap = "Std_ViewIvStereoInterleavedColumns";
    eType = Alter3DView;
}

void StdCmdViewIvStereoInterleavedColumns::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.activeDocument().activeView().setStereoType(\"InterleavedColumns\")");
}

bool StdCmdViewIvStereoInterleavedColumns::isActive()
{
    return getGuiApplication()->sendHasMsgToActiveView("SetStereoInterleavedColumns");
}


//===========================================================================
// Std_ViewIvIssueCamPos
//===========================================================================
DEF_STD_CMD_A(StdCmdViewIvIssueCamPos)

StdCmdViewIvIssueCamPos::StdCmdViewIvIssueCamPos()
    : Command("Std_ViewIvIssueCamPos")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Issue Camera &Position");
    sToolTipText = QT_TR_NOOP(
        "Issues the camera position to the console and to a macro, to easily recall this position"
    );
    sWhatsThis = "Std_ViewIvIssueCamPos";
    sStatusTip = sToolTipText;
    sPixmap = "Std_ViewIvIssueCamPos";
    eType = Alter3DView;
}

void StdCmdViewIvIssueCamPos::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    std::string Temp, Temp2;
    std::string::size_type pos;

    const char* ppReturn = nullptr;
    getGuiApplication()->sendMsgToActiveView("GetCamera", &ppReturn);

    // remove the #inventor line...
    Temp2 = ppReturn;
    pos = Temp2.find_first_of("\n");
    Temp2.erase(0, pos);

    // remove all returns
    while ((pos = Temp2.find('\n')) != std::string::npos) {
        Temp2.replace(pos, 1, " ");
    }

    // build up the command string
    Temp += "Gui.SendMsgToActiveView(\"SetCamera ";
    Temp += Temp2;
    Temp += "\")";

    Base::Console().message("%s\n", Temp2.c_str());
    getGuiApplication()->macroManager()->addLine(MacroManager::Gui, Temp.c_str());
}

bool StdCmdViewIvIssueCamPos::isActive()
{
    return getGuiApplication()->sendHasMsgToActiveView("GetCamera");
}


//===========================================================================
// Std_ViewZoomIn
//===========================================================================
DEF_STD_CMD_A(StdViewZoomIn)

StdViewZoomIn::StdViewZoomIn()
    : Command("Std_ViewZoomIn")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Zoom &In");
    sToolTipText = QT_TR_NOOP("Increases the zoom factor by a fixed amount");
    sWhatsThis = "Std_ViewZoomIn";
    sStatusTip = sToolTipText;
    sPixmap = "zoom-in";
    sAccel = keySequenceToAccel(QKeySequence::ZoomIn);
    eType = Alter3DView;
}

void StdViewZoomIn::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    getGuiApplication()->sendMsgToFocusView("ZoomIn");
}

bool StdViewZoomIn::isActive()
{
    return getGuiApplication()->sendHasMsgToActiveView("ZoomIn");
}

//===========================================================================
// Std_ViewZoomOut
//===========================================================================
DEF_STD_CMD_A(StdViewZoomOut)

StdViewZoomOut::StdViewZoomOut()
    : Command("Std_ViewZoomOut")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Zoom &Out");
    sToolTipText = QT_TR_NOOP("Decreases the zoom factor by a fixed amount");
    sWhatsThis = "Std_ViewZoomOut";
    sStatusTip = sToolTipText;
    sPixmap = "zoom-out";
    sAccel = keySequenceToAccel(QKeySequence::ZoomOut);
    eType = Alter3DView;
}

void StdViewZoomOut::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    getGuiApplication()->sendMsgToFocusView("ZoomOut");
}

bool StdViewZoomOut::isActive()
{
    return getGuiApplication()->sendHasMsgToActiveView("ZoomOut");
}

namespace
{
class SelectionCallbackHandler
{

private:
    static std::unique_ptr<SelectionCallbackHandler> currentSelectionHandler;
    QCursor prevSelectionCursor;
    using FnCb = void (*)(void* userdata, SoEventCallback* node);
    FnCb fnCb;
    void* userData;
    bool prevSelectionEn;

public:
    // Creates a selection handler used to implement the common behaviour of BoxZoom, BoxSelection
    // and BoxElementSelection. Takes the viewer, a selection mode, a cursor, a function pointer to
    // be called on success and a void pointer for user data to be passed to the given function. The
    // selection handler class stores all necessary previous states, registers a event callback and
    // starts the selection in the given mode. If there is still a selection handler active, this
    // call will generate a message and returns.
    static void Create(
        View3DInventorViewer* viewer,
        View3DInventorViewer::SelectionMode selectionMode,
        const QCursor& cursor,
        FnCb doFunction = nullptr,
        void* ud = nullptr
    )
    {
        if (currentSelectionHandler) {
            Base::Console().message("SelectionCallbackHandler: A selection handler already active.");
            return;
        }

        currentSelectionHandler = std::make_unique<SelectionCallbackHandler>();
        if (viewer) {
            currentSelectionHandler->userData = ud;
            currentSelectionHandler->fnCb = doFunction;
            currentSelectionHandler->prevSelectionCursor = viewer->cursor();
            viewer->setEditingCursor(cursor);
            viewer->addEventCallback(
                SoEvent::getClassTypeId(),
                SelectionCallbackHandler::selectionCallback,
                currentSelectionHandler.get()
            );
            currentSelectionHandler->prevSelectionEn = viewer->isSelectionEnabled();
            viewer->setSelectionEnabled(false);
            viewer->startSelection(selectionMode);
        }
    }

    void* getUserData() const
    {
        return userData;
    }

    // Implements the event handler. In the normal case the provided function is called.
    // Also supports aborting the selection mode by pressing (releasing) the Escape key.
    static void selectionCallback(void* ud, SoEventCallback* n)
    {
        auto selectionHandler = static_cast<SelectionCallbackHandler*>(ud);
        auto view = static_cast<Gui::View3DInventorViewer*>(n->getUserData());
        const SoEvent* ev = n->getEvent();
        if (ev->isOfType(SoKeyboardEvent::getClassTypeId())) {

            n->setHandled();
            n->getAction()->setHandled();

            const auto ke = static_cast<const SoKeyboardEvent*>(ev);
            const SbBool press = ke->getState() == SoButtonEvent::DOWN ? true : false;
            if (ke->getKey() == SoKeyboardEvent::ESCAPE) {

                if (!press) {
                    view->abortSelection();
                    restoreState(selectionHandler, view);
                }
            }
        }
        else if (ev->isOfType(SoMouseButtonEvent::getClassTypeId())) {
            const auto mbe = static_cast<const SoMouseButtonEvent*>(ev);

            // Mark all incoming mouse button events as handled, especially, to deactivate the
            // selection node
            n->getAction()->setHandled();

            if (mbe->getButton() == SoMouseButtonEvent::BUTTON1
                && mbe->getState() == SoButtonEvent::UP) {
                if (selectionHandler && selectionHandler->fnCb) {
                    selectionHandler->fnCb(selectionHandler->getUserData(), n);
                }
                restoreState(selectionHandler, view);
            }
            // No other mouse events available from Coin3D to implement right mouse up abort
        }
    }

    static void restoreState(SelectionCallbackHandler* selectionHandler, View3DInventorViewer* view)
    {
        if (selectionHandler) {
            selectionHandler->fnCb = nullptr;
            view->setEditingCursor(selectionHandler->prevSelectionCursor);
            view->removeEventCallback(
                SoEvent::getClassTypeId(),
                SelectionCallbackHandler::selectionCallback,
                selectionHandler
            );
            view->setSelectionEnabled(selectionHandler->prevSelectionEn);
        }
        Application::Instance->commandManager().testActive();
        currentSelectionHandler = nullptr;
    }

    static QCursor makeCursor(
        [[maybe_unused]] QWidget* widget,
        const QSize& size,
        const char* svgFile,
        int hotX,
        int hotY
    )
    {
        qreal hotXF = hotX;
        qreal hotYF = hotY;
#if !defined(Q_OS_WIN32) && !defined(Q_OS_MACOS)
        if (qApp->platformName() == QLatin1String("xcb")) {
            qreal pRatio = widget->devicePixelRatioF();
            hotXF *= pRatio;
            hotYF *= pRatio;
        }
#endif
        QPixmap px(Gui::BitmapFactory().pixmapFromSvg(svgFile, size));
        return QCursor(px, static_cast<int>(hotXF), static_cast<int>(hotYF));
    }
};
}  // namespace

std::unique_ptr<SelectionCallbackHandler> SelectionCallbackHandler::currentSelectionHandler
    = std::unique_ptr<SelectionCallbackHandler>();
//===========================================================================
// Std_ViewBoxZoom
//===========================================================================

DEF_3DV_CMD(StdViewBoxZoom)

StdViewBoxZoom::StdViewBoxZoom()
    : Command("Std_ViewBoxZoom")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&Box Zoom");
    sToolTipText = QT_TR_NOOP("Activates the box zoom tool");
    sWhatsThis = "Std_ViewBoxZoom";
    sStatusTip = sToolTipText;
    sPixmap = "zoom-border";
    sAccel = "Ctrl+B";
    eType = Alter3DView;
}

void StdViewBoxZoom::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
    if (view) {
        View3DInventorViewer* viewer = view->getViewer();
        if (!viewer->isSelecting()) {
            // NOLINTBEGIN
            QCursor cursor
                = SelectionCallbackHandler::makeCursor(viewer, QSize(32, 32), "zoom-border-cross", 6, 6);
            SelectionCallbackHandler::Create(viewer, View3DInventorViewer::BoxZoom, cursor);
            // NOLINTEND
        }
    }
}

//===========================================================================
// Std_BoxSelection
//===========================================================================
DEF_3DV_CMD(StdBoxSelection)

StdBoxSelection::StdBoxSelection()
    : Command("Std_BoxSelection")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("&Box Selection");
    sToolTipText = QT_TR_NOOP("Activates the box selection tool");
    sWhatsThis = "Std_BoxSelection";
    sStatusTip = sToolTipText;
    sPixmap = "edit-select-box";
    sAccel = "Shift+B";
    eType = AlterSelection;
}

using SelectionMode = enum
{
    CENTER,
    INTERSECT
};

static std::vector<std::string> getBoxSelection(
    ViewProviderDocumentObject* vp,
    SelectionMode mode,
    bool selectElement,
    const Base::ViewProjMethod& proj,
    const Base::Polygon2d& polygon,
    const Base::Matrix4D& mat,
    bool transform = true,
    int depth = 0
)
{
    std::vector<std::string> ret;
    auto obj = vp->getObject();
    if (!obj || !obj->isAttachedToDocument()) {
        return ret;
    }

    // DO NOT check this view object Visibility, let the caller do this. Because
    // we may be called by upper object hierarchy that manages our visibility.

    auto bbox3 = vp->getBoundingBox(nullptr, transform);
    if (!bbox3.IsValid()) {
        return ret;
    }

    auto bbox = bbox3.Transformed(mat).ProjectBox(&proj);

    // check if both two boundary points are inside polygon, only
    // valid since we know the given polygon is a box.
    if (polygon.Contains(Base::Vector2d(bbox.MinX, bbox.MinY))
        && polygon.Contains(Base::Vector2d(bbox.MaxX, bbox.MaxY))) {
        ret.emplace_back("");
        return ret;
    }

    if (!bbox.Intersect(polygon)) {
        return ret;
    }

    const auto& subs = obj->getSubObjects(App::DocumentObject::GS_SELECT);
    if (subs.empty()) {
        if (!selectElement) {
            if (mode == INTERSECT || polygon.Contains(bbox.GetCenter())) {
                ret.emplace_back("");
            }
            return ret;
        }
        Base::PyGILStateLocker lock;
        PyObject* pyobj = nullptr;
        Base::Matrix4D matCopy(mat);
        obj->getSubObject(nullptr, &pyobj, &matCopy, transform, depth);
        if (!pyobj) {
            return ret;
        }
        Py::Object pyobject(pyobj, true);
        if (!PyObject_TypeCheck(pyobj, &Data::ComplexGeoDataPy::Type)) {
            return ret;
        }
        auto data = static_cast<Data::ComplexGeoDataPy*>(pyobj)->getComplexGeoDataPtr();
        for (auto type : data->getElementTypes()) {
            size_t count = data->countSubElements(type);
            if (!count) {
                continue;
            }
            for (size_t i = 1; i <= count; ++i) {
                std::string element(type);
                element += std::to_string(i);
                std::unique_ptr<Data::Segment> segment(data->getSubElementByName(element.c_str()));
                if (!segment) {
                    continue;
                }
                std::vector<Base::Vector3d> points;
                std::vector<Data::ComplexGeoData::Line> lines;
                data->getLinesFromSubElement(segment.get(), points, lines);
                if (lines.empty()) {
                    if (points.empty()) {
                        continue;
                    }
                    auto v = proj(points[0]);
                    if (polygon.Contains(Base::Vector2d(v.x, v.y))) {
                        ret.push_back(element);
                    }
                    continue;
                }
                Base::Polygon2d loop;
                // TODO: can we assume the line returned above are in proper
                // order if the element is a face?
                auto v = proj(points[lines.front().I1]);
                loop.Add(Base::Vector2d(v.x, v.y));
                for (auto& line : lines) {
                    for (auto i = line.I1; i < line.I2; ++i) {
                        auto v = proj(points[i + 1]);
                        loop.Add(Base::Vector2d(v.x, v.y));
                    }
                }
                if (!polygon.Intersect(loop)) {
                    continue;
                }
                if (mode == CENTER && !polygon.Contains(loop.CalcBoundBox().GetCenter())) {
                    continue;
                }
                ret.push_back(element);
            }
            break;
        }
        return ret;
    }

    size_t count = 0;
    for (auto& sub : subs) {
        App::DocumentObject* parent = nullptr;
        std::string childName;
        Base::Matrix4D smat(mat);
        auto sobj
            = obj->resolve(sub.c_str(), &parent, &childName, nullptr, nullptr, &smat, transform, depth + 1);
        if (!sobj) {
            continue;
        }
        int vis;
        if (!parent || (vis = parent->isElementVisible(childName.c_str())) < 0) {
            vis = sobj->Visibility.getValue() ? 1 : 0;
        }

        if (!vis) {
            continue;
        }

        auto svp = freecad_cast<ViewProviderDocumentObject*>(
            Application::Instance->getViewProvider(sobj)
        );
        if (!svp) {
            continue;
        }

        const auto& sels
            = getBoxSelection(svp, mode, selectElement, proj, polygon, smat, false, depth + 1);
        if (sels.size() == 1 && sels[0].empty()) {
            ++count;
        }
        for (auto& sel : sels) {
            ret.emplace_back(sub + sel);
        }
    }
    if (count == subs.size()) {
        ret.resize(1);
        ret[0].clear();
    }
    return ret;
}

static void doSelect(void* ud, SoEventCallback* cb)
{
    bool selectElement = ud ? true : false;
    auto viewer = static_cast<Gui::View3DInventorViewer*>(cb->getUserData());

    viewer->setSelectionEnabled(true);

    SelectionMode selectionMode = CENTER;

    std::vector<SbVec2f> picked = viewer->getGLPolygon();
    SoCamera* cam = viewer->getSoRenderManager()->getCamera();
    SbViewVolume vv = cam->getViewVolume();
    Gui::ViewVolumeProjection proj(vv);
    Base::Polygon2d polygon;
    if (picked.size() == 2) {
        SbVec2f pt1 = picked[0];
        SbVec2f pt2 = picked[1];
        polygon.Add(Base::Vector2d(pt1[0], pt1[1]));
        polygon.Add(Base::Vector2d(pt1[0], pt2[1]));
        polygon.Add(Base::Vector2d(pt2[0], pt2[1]));
        polygon.Add(Base::Vector2d(pt2[0], pt1[1]));

        // when selecting from right to left then select by intersection
        // otherwise if the center is inside the rectangle
        if (picked[0][0] > picked[1][0]) {
            selectionMode = INTERSECT;
        }
    }
    else {
        for (const auto& it : picked) {
            polygon.Add(Base::Vector2d(it[0], it[1]));
        }
    }

    App::Document* doc = App::GetApplication().getActiveDocument();
    if (doc) {
        cb->setHandled();

        const SoEvent* ev = cb->getEvent();
        if (ev && !ev->wasCtrlDown()) {
            Gui::Selection().clearSelection(doc->getName());
        }

        const std::vector<App::DocumentObject*> objects = doc->getObjects();
        for (auto obj : objects) {
            if (App::GeoFeatureGroupExtension::getGroupOfObject(obj)) {
                continue;
            }

            auto vp = freecad_cast<ViewProviderDocumentObject*>(
                Application::Instance->getViewProvider(obj)
            );
            if (!vp || !vp->isVisible()) {
                continue;
            }

            Base::Matrix4D mat;
            for (auto& sub : getBoxSelection(vp, selectionMode, selectElement, proj, polygon, mat)) {
                Gui::Selection().addSelection(doc->getName(), obj->getNameInDocument(), sub.c_str());
            }
        }
    }
}

void StdBoxSelection::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
    if (view) {
        View3DInventorViewer* viewer = view->getViewer();
        if (!viewer->isSelecting()) {
            // #0002931: Box select misbehaves with touchpad navigation style
            // Notify the navigation style to cleanup internal states
            int mode = viewer->navigationStyle()->getViewingMode();
            if (mode != Gui::NavigationStyle::IDLE) {
                SoKeyboardEvent ev;
                viewer->navigationStyle()->processEvent(&ev);
            }

            // NOLINTBEGIN
            QCursor cursor = SelectionCallbackHandler::makeCursor(
                viewer,
                QSize(32, 32),
                "edit-select-box-cross",
                6,
                6
            );
            SelectionCallbackHandler::Create(
                viewer,
                View3DInventorViewer::Rubberband,
                cursor,
                doSelect,
                nullptr
            );
            viewer->setSelectionEnabled(false);
            // NOLINTEND
        }
    }
}

//===========================================================================
// Std_BoxElementSelection
//===========================================================================

DEF_3DV_CMD(StdBoxElementSelection)

StdBoxElementSelection::StdBoxElementSelection()
    : Command("Std_BoxElementSelection")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Bo&x Element Selection");
    sToolTipText = QT_TR_NOOP("Activates box element selection");
    sWhatsThis = "Std_BoxElementSelection";
    sStatusTip = sToolTipText;
    sPixmap = "edit-element-select-box";
    sAccel = "Shift+E";
    eType = AlterSelection;
}

void StdBoxElementSelection::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
    if (view) {
        View3DInventorViewer* viewer = view->getViewer();
        if (!viewer->isSelecting()) {
            // #0002931: Box select misbehaves with touchpad navigation style
            // Notify the navigation style to cleanup internal states
            int mode = viewer->navigationStyle()->getViewingMode();
            if (mode != Gui::NavigationStyle::IDLE) {
                SoKeyboardEvent ev;
                viewer->navigationStyle()->processEvent(&ev);
            }

            // NOLINTBEGIN
            QCursor cursor = SelectionCallbackHandler::makeCursor(
                viewer,
                QSize(32, 32),
                "edit-element-select-box-cross",
                6,
                6
            );
            SelectionCallbackHandler::Create(
                viewer,
                View3DInventorViewer::Rubberband,
                cursor,
                doSelect,
                this
            );
            viewer->setSelectionEnabled(false);
            // NOLINTEND
        }
    }
}


//===========================================================================
// Std_TreeSelection
//===========================================================================

DEF_STD_CMD(StdTreeSelection)

StdTreeSelection::StdTreeSelection()
    : Command("Std_TreeSelection")
{
    sGroup = "TreeView";
    sMenuText = QT_TR_NOOP("&Go to Selection");
    sToolTipText = QT_TR_NOOP("Scrolls to the first selected item");
    sWhatsThis = "Std_TreeSelection";
    sStatusTip = sToolTipText;
    eType = Alter3DView;
    sPixmap = "tree-goto-sel";
    sAccel = "T,G";
}

void StdTreeSelection::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    TreeWidget::scrollItemToTop();
}

//===========================================================================
// Std_TreeCollapse
//===========================================================================

DEF_STD_CMD(StdCmdTreeCollapse)

StdCmdTreeCollapse::StdCmdTreeCollapse()
    : Command("Std_TreeCollapse")
{
    sGroup = "View";
    sMenuText = QT_TR_NOOP("Collapse Selected Items");
    sToolTipText = QT_TR_NOOP("Collapses the currently selected tree items");
    sWhatsThis = "Std_TreeCollapse";
    sStatusTip = sToolTipText;
    eType = Alter3DView;
}

void StdCmdTreeCollapse::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    QList<TreeWidget*> tree = Gui::getMainWindow()->findChildren<TreeWidget*>();
    for (QList<TreeWidget*>::iterator it = tree.begin(); it != tree.end(); ++it) {
        (*it)->expandSelectedItems(TreeItemMode::CollapseItem);
    }
}

//===========================================================================
// Std_TreeExpand
//===========================================================================

DEF_STD_CMD(StdCmdTreeExpand)

StdCmdTreeExpand::StdCmdTreeExpand()
    : Command("Std_TreeExpand")
{
    sGroup = "View";
    sMenuText = QT_TR_NOOP("Expand Selected Items");
    sToolTipText = QT_TR_NOOP("Expands the currently selected tree items");
    sWhatsThis = "Std_TreeExpand";
    sStatusTip = sToolTipText;
    eType = Alter3DView;
}

void StdCmdTreeExpand::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    QList<TreeWidget*> tree = Gui::getMainWindow()->findChildren<TreeWidget*>();
    for (QList<TreeWidget*>::iterator it = tree.begin(); it != tree.end(); ++it) {
        (*it)->expandSelectedItems(TreeItemMode::ExpandItem);
    }
}

//===========================================================================
// Std_TreeSelectAllInstance
//===========================================================================

DEF_STD_CMD_A(StdCmdTreeSelectAllInstances)

StdCmdTreeSelectAllInstances::StdCmdTreeSelectAllInstances()
    : Command("Std_TreeSelectAllInstances")
{
    sGroup = "View";
    sMenuText = QT_TR_NOOP("Select All Instances");
    sToolTipText = QT_TR_NOOP("Selects all instances of the currently selected object");
    sWhatsThis = "Std_TreeSelectAllInstances";
    sStatusTip = sToolTipText;
    sPixmap = "sel-instance";
    eType = AlterSelection;
}

bool StdCmdTreeSelectAllInstances::isActive()
{
    const auto& sels = Selection().getSelectionEx(
        "*",
        App::DocumentObject::getClassTypeId(),
        ResolveMode::OldStyleElement,
        true
    );
    if (sels.empty()) {
        return false;
    }
    auto obj = sels[0].getObject();
    if (!obj || !obj->isAttachedToDocument()) {
        return false;
    }
    return freecad_cast<ViewProviderDocumentObject*>(Application::Instance->getViewProvider(obj))
        != nullptr;
}

void StdCmdTreeSelectAllInstances::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    const auto& sels = Selection().getSelectionEx(
        "*",
        App::DocumentObject::getClassTypeId(),
        ResolveMode::OldStyleElement,
        true
    );
    if (sels.empty()) {
        return;
    }
    auto obj = sels[0].getObject();
    if (!obj || !obj->isAttachedToDocument()) {
        return;
    }
    auto vpd = freecad_cast<ViewProviderDocumentObject*>(Application::Instance->getViewProvider(obj));
    if (!vpd) {
        return;
    }
    Selection().selStackPush();
    Selection().clearCompleteSelection();
    const auto trees = getMainWindow()->findChildren<TreeWidget*>();
    for (auto tree : trees) {
        tree->selectAllInstances(*vpd);
    }
    Selection().selStackPush();
}


//===========================================================================
// Std_SceneInspector
//===========================================================================

DEF_3DV_CMD(StdCmdSceneInspector)

StdCmdSceneInspector::StdCmdSceneInspector()
    : Command("Std_SceneInspector")
{
    // setting the
    sGroup = "Tools";
    sMenuText = QT_TR_NOOP("Scene I&nspector");
    sToolTipText = QT_TR_NOOP("Opens the scene inspector");
    sWhatsThis = "Std_SceneInspector";
    sStatusTip = sToolTipText;
    eType = Alter3DView;
    sPixmap = "Std_SceneInspector";
}

void StdCmdSceneInspector::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    Gui::Document* doc = Application::Instance->activeDocument();
    if (doc) {
        static QPointer<Gui::Dialog::DlgInspector> dlg = nullptr;
        if (!dlg) {
            dlg = new Gui::Dialog::DlgInspector(getMainWindow());
        }
        dlg->setDocument(doc);
        dlg->setAttribute(Qt::WA_DeleteOnClose);
        dlg->show();
    }
}

//===========================================================================
// Std_TextureMapping
//===========================================================================

DEF_STD_CMD_A(StdCmdTextureMapping)

StdCmdTextureMapping::StdCmdTextureMapping()
    : Command("Std_TextureMapping")
{
    // setting the
    sGroup = "Tools";
    sMenuText = QT_TR_NOOP("Text&ure Mapping");
    sToolTipText = QT_TR_NOOP("Maps textures to shapes");
    sWhatsThis = "Std_TextureMapping";
    sStatusTip = sToolTipText;
    sPixmap = "Std_TextureMapping";
    eType = Alter3DView;
}

void StdCmdTextureMapping::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    Gui::Control().showDialog(new Gui::Dialog::TaskTextureMapping);
}

bool StdCmdTextureMapping::isActive()
{
    Gui::MDIView* view = getMainWindow()->activeWindow();
    return view && view->isDerivedFrom<Gui::View3DInventor>() && (!(Gui::Control().activeDialog()));
}

DEF_STD_CMD(StdCmdDemoMode)

StdCmdDemoMode::StdCmdDemoMode()
    : Command("Std_DemoMode")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("View &Turntable");
    sToolTipText = QT_TR_NOOP("Opens a turntable view");
    sWhatsThis = "Std_DemoMode";
    sStatusTip = sToolTipText;
    eType = Alter3DView;
    sPixmap = "Std_DemoMode";
}

void StdCmdDemoMode::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    static QPointer<QDialog> dlg = nullptr;
    if (!dlg) {
        dlg = new Gui::Dialog::DemoMode(getMainWindow());
    }
    dlg->setAttribute(Qt::WA_DeleteOnClose);
    dlg->show();
}


//===========================================================================
// Std_SelBack
//===========================================================================

DEF_STD_CMD_A(StdCmdSelBack)

StdCmdSelBack::StdCmdSelBack()
    : Command("Std_SelBack")
{
    sGroup = "View";
    sMenuText = QT_TR_NOOP("Selection &Back");
    static std::string toolTip = std::string("<p>")
        + QT_TR_NOOP("Restores the previous tree view selection. "
                     "Only works if tree RecordSelection mode is switched on.")
        + "</p>";
    sToolTipText = toolTip.c_str();
    sWhatsThis = "Std_SelBack";
    sStatusTip = sToolTipText;
    sPixmap = "sel-back";
    sAccel = "S, B";
    eType = AlterSelection;
}

void StdCmdSelBack::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    Selection().selStackGoBack();
}

bool StdCmdSelBack::isActive()
{
    return Selection().selStackBackSize() > 1;
}

//===========================================================================
// Std_SelForward
//===========================================================================

DEF_STD_CMD_A(StdCmdSelForward)

StdCmdSelForward::StdCmdSelForward()
    : Command("Std_SelForward")
{
    sGroup = "View";
    sMenuText = QT_TR_NOOP("Selection &Forward");
    static std::string toolTip = std::string("<p>")
        + QT_TR_NOOP("Restores the next tree view selection. "
                     "Only works if tree RecordSelection mode is switched on.")
        + "</p>";
    sToolTipText = toolTip.c_str();
    sWhatsThis = "Std_SelForward";
    sStatusTip = sToolTipText;
    sPixmap = "sel-forward";
    sAccel = "S, F";
    eType = AlterSelection;
}

void StdCmdSelForward::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    Selection().selStackGoForward();
}

bool StdCmdSelForward::isActive()
{
    return !!Selection().selStackForwardSize();
}

//=======================================================================
// Std_TreeSingleDocument
//===========================================================================
#define TREEVIEW_DOC_CMD_DEF(_name, _v) \
    DEF_STD_CMD_AC(StdTree##_name) \
    void StdTree##_name::activated(int) \
    { \
        TreeParams::setDocumentMode(_v); \
        if (_pcAction) \
            _pcAction->setBlockedChecked(true); \
    } \
    Action* StdTree##_name::createAction(void) \
    { \
        Action* pcAction = Command::createAction(); \
        pcAction->setCheckable(true); \
        pcAction->setIcon(QIcon()); \
        _pcAction = pcAction; \
        isActive(); \
        return pcAction; \
    } \
    bool StdTree##_name::isActive() \
    { \
        bool checked = TreeParams::getDocumentMode() == _v; \
        if (_pcAction && _pcAction->isChecked() != checked) \
            _pcAction->setBlockedChecked(checked); \
        return true; \
    }

TREEVIEW_DOC_CMD_DEF(SingleDocument, 0)

StdTreeSingleDocument::StdTreeSingleDocument()
    : Command("Std_TreeSingleDocument")
{
    sGroup = "TreeView";
    sMenuText = QT_TR_NOOP("&Single Document");
    sToolTipText = QT_TR_NOOP("Displays only the active document in the tree view");
    sWhatsThis = "Std_TreeSingleDocument";
    sStatusTip = sToolTipText;
    sPixmap = "tree-doc-single";
    eType = 0;
}

//===========================================================================
// Std_TreeMultiDocument
//===========================================================================
TREEVIEW_DOC_CMD_DEF(MultiDocument, 1)

StdTreeMultiDocument::StdTreeMultiDocument()
    : Command("Std_TreeMultiDocument")
{
    sGroup = "TreeView";
    sMenuText = QT_TR_NOOP("&Multi Document");
    sToolTipText = QT_TR_NOOP("Displays all documents in the tree view");
    sWhatsThis = "Std_TreeMultiDocument";
    sStatusTip = sToolTipText;
    sPixmap = "tree-doc-multi";
    eType = 0;
}

//===========================================================================
// Std_TreeCollapseDocument
//===========================================================================
TREEVIEW_DOC_CMD_DEF(CollapseDocument, 2)

StdTreeCollapseDocument::StdTreeCollapseDocument()
    : Command("Std_TreeCollapseDocument")
{
    sGroup = "TreeView";
    sMenuText = QT_TR_NOOP("Collapse/E&xpand");
    sToolTipText = QT_TR_NOOP("Expands the active document and collapses all others");
    sWhatsThis = "Std_TreeCollapseDocument";
    sStatusTip = sToolTipText;
    sPixmap = "tree-doc-collapse";
    eType = 0;
}

//===========================================================================
// Std_TreeSyncView
//===========================================================================
#define TREEVIEW_CMD_DEF(_name) \
    DEF_STD_CMD_AC(StdTree##_name) \
    void StdTree##_name::activated(int) \
    { \
        auto checked = !TreeParams::get##_name(); \
        TreeParams::set##_name(checked); \
        if (_pcAction) \
            _pcAction->setBlockedChecked(checked); \
    } \
    Action* StdTree##_name::createAction() \
    { \
        Action* pcAction = Command::createAction(); \
        pcAction->setCheckable(true); \
        pcAction->setIcon(QIcon()); \
        _pcAction = pcAction; \
        isActive(); \
        return pcAction; \
    } \
    bool StdTree##_name::isActive() \
    { \
        bool checked = TreeParams::get##_name(); \
        if (_pcAction && _pcAction->isChecked() != checked) \
            _pcAction->setBlockedChecked(checked); \
        return true; \
    }

TREEVIEW_CMD_DEF(SyncView)

StdTreeSyncView::StdTreeSyncView()
    : Command("Std_TreeSyncView")
{
    sGroup = "TreeView";
    sMenuText = QT_TR_NOOP("&1 Sync View");
    sToolTipText = QT_TR_NOOP(
        "Switches to the 3D view containing the selected item from the tree view"
    );
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_TreeSyncView";
    sPixmap = "tree-sync-view";
    sAccel = "T,1";
    eType = 0;
}

//===========================================================================
// Std_TreeSyncSelection
//===========================================================================
TREEVIEW_CMD_DEF(SyncSelection)

StdTreeSyncSelection::StdTreeSyncSelection()
    : Command("Std_TreeSyncSelection")
{
    sGroup = "TreeView";
    sMenuText = QT_TR_NOOP("&2 Sync Selection");
    sToolTipText = QT_TR_NOOP(
        "Expands the tree item when the corresponding object is selected in the 3D view"
    );
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_TreeSyncSelection";
    sPixmap = "tree-sync-sel";
    sAccel = "T,2";
    eType = 0;
}

//===========================================================================
// Std_TreeSyncPlacement
//===========================================================================
TREEVIEW_CMD_DEF(SyncPlacement)

StdTreeSyncPlacement::StdTreeSyncPlacement()
    : Command("Std_TreeSyncPlacement")
{
    sGroup = "TreeView";
    sMenuText = QT_TR_NOOP("&3 Sync Placement");
    sToolTipText
        = QT_TR_NOOP("Adjusts the placement on drag-and-drop of objects across coordinate systems (e.g. in part containers)");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_TreeSyncPlacement";
    sPixmap = "tree-sync-pla";
    sAccel = "T,3";
    eType = 0;
}

//===========================================================================
// Std_TreePreSelection
//===========================================================================
TREEVIEW_CMD_DEF(PreSelection)

StdTreePreSelection::StdTreePreSelection()
    : Command("Std_TreePreSelection")
{
    sGroup = "TreeView";
    sMenuText = QT_TR_NOOP("&4 Preselection");
    sToolTipText = QT_TR_NOOP(
        "Preselects the object in 3D view when hovering the cursor over the tree item"
    );
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_TreePreSelection";
    sPixmap = "tree-pre-sel";
    sAccel = "T,4";
    eType = 0;
}

//===========================================================================
// Std_TreeRecordSelection
//===========================================================================
TREEVIEW_CMD_DEF(RecordSelection)

StdTreeRecordSelection::StdTreeRecordSelection()
    : Command("Std_TreeRecordSelection")
{
    sGroup = "TreeView";
    sMenuText = QT_TR_NOOP("&5 Record Selection");
    sToolTipText
        = QT_TR_NOOP("Records the selection in the tree view in order to go back/forward using the navigation buttons");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_TreeRecordSelection";
    sPixmap = "tree-rec-sel";
    sAccel = "T,5";
    eType = 0;
}

//===========================================================================
// Std_TreeDrag
//===========================================================================
DEF_STD_CMD(StdTreeDrag)

StdTreeDrag::StdTreeDrag()
    : Command("Std_TreeDrag")
{
    sGroup = "TreeView";
    sMenuText = QT_TR_NOOP("Initiate &Dragging");
    sToolTipText = QT_TR_NOOP("Initiates dragging of the currently selected tree items");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_TreeDrag";
    sPixmap = "tree-item-drag";
    sAccel = "T,D";
    eType = 0;
}

void StdTreeDrag::activated(int)
{
    if (Gui::Selection().hasSelection()) {
        const auto trees = getMainWindow()->findChildren<TreeWidget*>();
        for (auto tree : trees) {
            if (tree->isVisible()) {
                tree->startDragging();
                break;
            }
        }
    }
}

//======================================================================
// Std_TreeViewActions
//===========================================================================
//
class StdCmdTreeViewActions: public GroupCommand
{
public:
    StdCmdTreeViewActions()
        : GroupCommand("Std_TreeViewActions")
    {
        sGroup = "TreeView";
        sMenuText = QT_TR_NOOP("Tree View Actions");
        sToolTipText = QT_TR_NOOP("Tree view behavior options and actions");
        sWhatsThis = "Std_TreeViewActions";
        sStatusTip = sToolTipText;
        eType = 0;
        bCanLog = false;

        addCommand(new StdTreeSyncView());
        addCommand(new StdTreeSyncSelection());
        addCommand(new StdTreeSyncPlacement());
        addCommand(new StdTreePreSelection());
        addCommand(new StdTreeRecordSelection());

        addCommand();

        addCommand(new StdTreeSingleDocument());
        addCommand(new StdTreeMultiDocument());
        addCommand(new StdTreeCollapseDocument());

        addCommand();

        addCommand(new StdTreeDrag(), !cmds.empty());
        addCommand(new StdTreeSelection(), !cmds.empty());

        addCommand();

        addCommand(new StdCmdSelBack());
        addCommand(new StdCmdSelForward());
    }
    const char* className() const override
    {
        return "StdCmdTreeViewActions";
    }
};


//======================================================================
// Std_SelBoundingBox
//===========================================================================
DEF_STD_CMD_AC(StdCmdSelBoundingBox)

StdCmdSelBoundingBox::StdCmdSelBoundingBox()
    : Command("Std_SelBoundingBox")
{
    sGroup = "View";
    sMenuText = QT_TR_NOOP("&Bounding Box");
    sToolTipText = QT_TR_NOOP("Shows selection bounding box");
    sWhatsThis = "Std_SelBoundingBox";
    sStatusTip = sToolTipText;
    sPixmap = "sel-bbox";
    eType = Alter3DView;
}

void StdCmdSelBoundingBox::activated(int iMsg)
{
    bool checked = !!iMsg;
    if (checked != ViewParams::instance()->getShowSelectionBoundingBox()) {
        ViewParams::instance()->setShowSelectionBoundingBox(checked);
        if (_pcAction) {
            _pcAction->setBlockedChecked(checked);
        }
    }
}

bool StdCmdSelBoundingBox::isActive()
{
    if (_pcAction) {
        bool checked = _pcAction->isChecked();
        if (checked != ViewParams::instance()->getShowSelectionBoundingBox()) {
            _pcAction->setBlockedChecked(!checked);
        }
    }
    return true;
}

Action* StdCmdSelBoundingBox::createAction()
{
    Action* pcAction = Command::createAction();
    pcAction->setCheckable(true);
    return pcAction;
}

//===========================================================================
// Std_DockOverlayAll
//===========================================================================

DEF_STD_CMD(StdCmdDockOverlayAll)

StdCmdDockOverlayAll::StdCmdDockOverlayAll()
    : Command("Std_DockOverlayAll")
{
    sGroup = "View";
    sMenuText = QT_TR_NOOP("Toggle Overl&ay for All Panels");
    sToolTipText = QT_TR_NOOP("Toggled overlay mode for all docked panels");
    sWhatsThis = "Std_DockOverlayAll";
    sStatusTip = sToolTipText;
    eType = 0;
}

void StdCmdDockOverlayAll::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleAll);
}

//===========================================================================
// Std_DockOverlayTransparentAll
//===========================================================================

DEF_STD_CMD(StdCmdDockOverlayTransparentAll)

StdCmdDockOverlayTransparentAll::StdCmdDockOverlayTransparentAll()
    : Command("Std_DockOverlayTransparentAll")
{
    sGroup = "View";
    sMenuText = QT_TR_NOOP("Toggle Tra&nsparent Panels");
    sToolTipText = QT_TR_NOOP(
        "Toggles transparent mode for all docked overlay panels.\n"
        "This makes the docked panels stay transparent at all times."
    );
    sWhatsThis = "Std_DockOverlayTransparentAll";
    sStatusTip = sToolTipText;
    eType = 0;
}

void StdCmdDockOverlayTransparentAll::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleTransparentAll);
}

//===========================================================================
// Std_DockOverlayToggle
//===========================================================================

DEF_STD_CMD(StdCmdDockOverlayToggle)

StdCmdDockOverlayToggle::StdCmdDockOverlayToggle()
    : Command("Std_DockOverlayToggle")
{
    sGroup = "View";
    sMenuText = QT_TR_NOOP("Toggle &Overlay");
    sToolTipText = QT_TR_NOOP("Toggles overlay mode for the docked window under the cursor");
    sWhatsThis = "Std_DockOverlayToggle";
    sStatusTip = sToolTipText;
    eType = 0;
}

void StdCmdDockOverlayToggle::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleActive);
}

//===========================================================================
// Std_DockOverlayToggleTransparent
//===========================================================================

DEF_STD_CMD(StdCmdDockOverlayToggleTransparent)

StdCmdDockOverlayToggleTransparent::StdCmdDockOverlayToggleTransparent()
    : Command("Std_DockOverlayToggleTransparent")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Toggle Tran&sparent Mode");
    sToolTipText = QT_TR_NOOP(
        "Toggles transparent mode for the docked panel under cursor.\n"
        "This makes the docked panel stay transparent at all times."
    );
    sWhatsThis = "Std_DockOverlayToggleTransparent";
    sStatusTip = sToolTipText;
    eType = 0;
}

void StdCmdDockOverlayToggleTransparent::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleTransparent);
}

//===========================================================================
// Std_DockOverlayToggleLeft
//===========================================================================

DEF_STD_CMD(StdCmdDockOverlayToggleLeft)

StdCmdDockOverlayToggleLeft::StdCmdDockOverlayToggleLeft()
    : Command("Std_DockOverlayToggleLeft")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Toggle &Left");
    sToolTipText = QT_TR_NOOP("Toggles the visibility of the left overlay panel");
    sWhatsThis = "Std_DockOverlayToggleLeft";
    sStatusTip = sToolTipText;
    sAccel = "Ctrl+Left";
    sPixmap = "Std_DockOverlayToggleLeft";
    eType = 0;
}

void StdCmdDockOverlayToggleLeft::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleLeft);
}

//===========================================================================
// Std_DockOverlayToggleRight
//===========================================================================

DEF_STD_CMD(StdCmdDockOverlayToggleRight)

StdCmdDockOverlayToggleRight::StdCmdDockOverlayToggleRight()
    : Command("Std_DockOverlayToggleRight")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Toggle &Right");
    sToolTipText = QT_TR_NOOP("Toggles the visibility of the right overlay panel");
    sWhatsThis = "Std_DockOverlayToggleRight";
    sStatusTip = sToolTipText;
    sAccel = "Ctrl+Right";
    sPixmap = "Std_DockOverlayToggleRight";
    eType = 0;
}

void StdCmdDockOverlayToggleRight::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleRight);
}

//===========================================================================
// Std_DockOverlayToggleTop
//===========================================================================

DEF_STD_CMD(StdCmdDockOverlayToggleTop)

StdCmdDockOverlayToggleTop::StdCmdDockOverlayToggleTop()
    : Command("Std_DockOverlayToggleTop")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Toggle &Top");
    sToolTipText = QT_TR_NOOP("Toggles the visibility of the top overlay panel");
    sWhatsThis = "Std_DockOverlayToggleTop";
    sStatusTip = sToolTipText;
    sAccel = "Ctrl+Up";
    sPixmap = "Std_DockOverlayToggleTop";
    eType = 0;
}

void StdCmdDockOverlayToggleTop::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleTop);
}

//===========================================================================
// Std_DockOverlayToggleBottom
//===========================================================================

DEF_STD_CMD(StdCmdDockOverlayToggleBottom)

StdCmdDockOverlayToggleBottom::StdCmdDockOverlayToggleBottom()
    : Command("Std_DockOverlayToggleBottom")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("Toggle &Bottom");
    sToolTipText = QT_TR_NOOP("Toggles the visibility of the bottom overlay panel");
    sWhatsThis = "Std_DockOverlayToggleBottom";
    sStatusTip = sToolTipText;
    sAccel = "Ctrl+Down";
    sPixmap = "Std_DockOverlayToggleBottom";
    eType = 0;
}

void StdCmdDockOverlayToggleBottom::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    OverlayManager::instance()->setOverlayMode(OverlayManager::OverlayMode::ToggleBottom);
}

//===========================================================================
// Std_DockOverlayMouseTransparent
//===========================================================================

DEF_STD_CMD_AC(StdCmdDockOverlayMouseTransparent)

StdCmdDockOverlayMouseTransparent::StdCmdDockOverlayMouseTransparent()
    : Command("Std_DockOverlayMouseTransparent")
{
    sGroup = "View";
    sMenuText = QT_TR_NOOP("Bypass &Mouse Events in Overlay Panels");
    sToolTipText = QT_TR_NOOP("Bypasses all mouse events in docked overlay panels");
    sWhatsThis = "Std_DockOverlayMouseTransparent";
    sStatusTip = sToolTipText;
    sAccel = "T, T";
    eType = NoTransaction;
}

void StdCmdDockOverlayMouseTransparent::activated(int iMsg)
{
    (void)iMsg;
    bool checked = !OverlayManager::instance()->isMouseTransparent();
    OverlayManager::instance()->setMouseTransparent(checked);
    if (_pcAction) {
        _pcAction->setBlockedChecked(checked);
    }
}

Action* StdCmdDockOverlayMouseTransparent::createAction()
{
    Action* pcAction = Command::createAction();
    pcAction->setCheckable(true);
    pcAction->setIcon(QIcon());
    _pcAction = pcAction;
    isActive();
    return pcAction;
}

bool StdCmdDockOverlayMouseTransparent::isActive()
{
    bool checked = OverlayManager::instance()->isMouseTransparent();
    if (_pcAction && _pcAction->isChecked() != checked) {
        _pcAction->setBlockedChecked(checked);
    }
    return true;
}

// ============================================================================

class StdCmdDockOverlay: public GroupCommand
{
public:
    StdCmdDockOverlay()
        : GroupCommand("Std_DockOverlay")
    {
        sGroup = "View";
        sMenuText = QT_TR_NOOP("Overlay Docked Panel");
        sToolTipText = QT_TR_NOOP("Sets the docked panel in overlay mode");
        sWhatsThis = "Std_DockOverlay";
        sStatusTip = sToolTipText;
        eType = 0;
        bCanLog = false;

        addCommand(new StdCmdDockOverlayAll());
        addCommand(new StdCmdDockOverlayTransparentAll());
        addCommand();
        addCommand(new StdCmdDockOverlayToggle());
        addCommand(new StdCmdDockOverlayToggleTransparent());
        addCommand();
        addCommand(new StdCmdDockOverlayMouseTransparent());
        addCommand();
        addCommand(new StdCmdDockOverlayToggleLeft());
        addCommand(new StdCmdDockOverlayToggleRight());
        addCommand(new StdCmdDockOverlayToggleTop());
        addCommand(new StdCmdDockOverlayToggleBottom());
    };
    virtual const char* className() const
    {
        return "StdCmdDockOverlay";
    }
};

//===========================================================================
// Std_StoreWorkingView
//===========================================================================
DEF_STD_CMD_A(StdStoreWorkingView)

StdStoreWorkingView::StdStoreWorkingView()
    : Command("Std_StoreWorkingView")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("St&ore Working View");
    sToolTipText = QT_TR_NOOP("Stores a temporary working view for the current document");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_StoreWorkingView";
    sAccel = "Shift+End";
    eType = NoTransaction;
}

void StdStoreWorkingView::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    if (auto view = dynamic_cast<Gui::View3DInventor*>(Gui::getMainWindow()->activeWindow())) {
        view->getViewer()->saveHomePosition();
    }
}

bool StdStoreWorkingView::isActive()
{
    return dynamic_cast<Gui::View3DInventor*>(Gui::getMainWindow()->activeWindow());
}

//===========================================================================
// Std_RecallWorkingView
//===========================================================================
DEF_STD_CMD_A(StdRecallWorkingView)

StdRecallWorkingView::StdRecallWorkingView()
    : Command("Std_RecallWorkingView")
{
    sGroup = "Standard-View";
    sMenuText = QT_TR_NOOP("R&ecall Working View");
    sToolTipText = QT_TR_NOOP("Recalls a previously stored temporary working view");
    sStatusTip = sToolTipText;
    sWhatsThis = "Std_RecallWorkingView";
    sAccel = "End";
    eType = NoTransaction;
}

void StdRecallWorkingView::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    if (auto view = dynamic_cast<Gui::View3DInventor*>(Gui::getMainWindow()->activeWindow())) {
        if (view->getViewer()->hasHomePosition()) {
            view->getViewer()->resetToHomePosition();
        }
    }
}

bool StdRecallWorkingView::isActive()
{
    auto view = dynamic_cast<Gui::View3DInventor*>(Gui::getMainWindow()->activeWindow());
    return view && view->getViewer()->hasHomePosition();
}

//===========================================================================
// Std_AlignToSelection
//===========================================================================
DEF_STD_CMD_A(StdCmdAlignToSelection)

StdCmdAlignToSelection::StdCmdAlignToSelection()
    : Command("Std_AlignToSelection")
{
    sGroup = "View";
    sMenuText = QT_TR_NOOP("&Align to Selection");
    sToolTipText = QT_TR_NOOP("Aligns the camera view to the selected elements in the 3D view");
    sWhatsThis = "Std_AlignToSelection";
    sPixmap = "align-to-selection";
    eType = Alter3DView;
}

void StdCmdAlignToSelection::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    doCommand(Command::Gui, "Gui.SendMsgToActiveView(\"AlignToSelection\")");
}

bool StdCmdAlignToSelection::isActive()
{
    return getGuiApplication()->sendHasMsgToActiveView("AlignToSelection");
}

//===========================================================================
// Std_ClarifySelection
//===========================================================================

DEF_STD_CMD_A(StdCmdClarifySelection)

StdCmdClarifySelection::StdCmdClarifySelection()
    : Command("Std_ClarifySelection")
{
    sGroup = "View";
    sMenuText = QT_TR_NOOP("Clarify Selection");
    sToolTipText = QT_TR_NOOP(
        "Displays a context menu at the mouse cursor to select overlapping "
        "or obstructed geometry in the 3D view.\n"
    );
    sWhatsThis = "Std_ClarifySelection";
    sStatusTip = sToolTipText;
    sAccel = "G, G";
    eType = NoTransaction | AlterSelection;
}

void StdCmdClarifySelection::activated(int iMsg)
{
    Q_UNUSED(iMsg);

    // Get the active view
    auto view3d = freecad_cast<View3DInventor*>(Application::Instance->activeView());
    if (!view3d) {
        return;
    }

    auto viewer = view3d->getViewer();
    if (!viewer) {
        return;
    }

    QWidget* widget = viewer->getGLWidget();
    if (!widget) {
        return;
    }

    // check if we have a stored right-click position (context menu) or should use current cursor
    // position (keyboard shortcut)
    SbVec2s point;
    auto& storedPosition = viewer->navigationStyle()->getRightClickPosition();
    if (storedPosition.has_value()) {
        point = storedPosition.value();
    }
    else {
        QPoint pos = QCursor::pos();
        QPoint local = widget->mapFromGlobal(pos);

        qreal devicePixelRatio = widget->devicePixelRatioF();
        point = SbVec2s(
            static_cast<short>(local.x() * devicePixelRatio),
            static_cast<short>((widget->height() - local.y() - 1) * devicePixelRatio)
        );
    }

    // Use ray picking to get all objects under cursor
    SoRayPickAction pickAction(viewer->getSoRenderManager()->getViewportRegion());
    pickAction.setPoint(point);

    constexpr double defaultMultiplier = 5.0F;
    double clarifyRadiusMultiplier
        = App::GetApplication()
              .GetParameterGroupByPath("User parameter:BaseApp/Preferences/View")
              ->GetFloat("ClarifySelectionRadiusMultiplier", defaultMultiplier);

    pickAction.setRadius(viewer->getPickRadius() * clarifyRadiusMultiplier);
    pickAction.setPickAll(static_cast<SbBool>(true));  // Get all objects under cursor
    pickAction.apply(viewer->getSoRenderManager()->getSceneGraph());

    const SoPickedPointList& pplist = pickAction.getPickedPointList();
    if (pplist.getLength() == 0) {
        return;
    }

    // Convert picked points to PickData list
    std::vector<PickData> selections;

    for (int i = 0; i < pplist.getLength(); ++i) {
        SoPickedPoint* pp = pplist[i];
        if (!pp || !pp->getPath()) {
            continue;
        }

        ViewProvider* vp = viewer->getViewProviderByPath(pp->getPath());
        if (!vp) {
            continue;
        }

        // Cast to ViewProviderDocumentObject to get the object
        auto vpDoc = freecad_cast<Gui::ViewProviderDocumentObject*>(vp);
        if (!vpDoc) {
            continue;
        }

        App::DocumentObject* obj = vpDoc->getObject();
        if (!obj) {
            continue;
        }

        // Get element information - handle sub-objects like Assembly parts
        std::string elementName = vp->getElement(pp->getDetail());
        std::string subName;

        // Try to get more detailed sub-object information
        bool hasSubObject = false;
        if (vp->getElementPicked(pp, subName)) {
            hasSubObject = true;
        }

        // Create PickData with selection information
        PickData pickData {
            .obj = obj,
            .element = elementName,
            .docName = obj->getDocument()->getName(),
            .objName = obj->getNameInDocument(),
            .subName = hasSubObject ? subName : elementName
        };

        selections.push_back(pickData);
    }

    if (selections.empty()) {
        return;
    }

    QPoint globalPos;
    if (storedPosition.has_value()) {
        qreal devicePixelRatio = widget->devicePixelRatioF();
        int logicalHeight = static_cast<int>(widget->height());
        QPoint localPos(
            static_cast<int>(point[0] / devicePixelRatio),
            logicalHeight - static_cast<int>(point[1] / devicePixelRatio) - 1
        );
        globalPos = widget->mapToGlobal(localPos);
    }
    else {
        globalPos = QCursor::pos();
    }

    // Use SelectionMenu to display and handle the pick menu
    SelectionMenu contextMenu(widget);
    contextMenu.doPick(selections, globalPos);
}

bool StdCmdClarifySelection::isActive()
{
    return qobject_cast<View3DInventor*>(getMainWindow()->activeWindow()) != nullptr;
}

//===========================================================================
// Instantiation
//===========================================================================


namespace Gui
{

void CreateViewStdCommands()
{
    // NOLINTBEGIN
    CommandManager& rcCmdMgr = Application::Instance->commandManager();

    // views
    rcCmdMgr.addCommand(new StdCmdViewBottom());
    rcCmdMgr.addCommand(new StdCmdViewHome());
    rcCmdMgr.addCommand(new StdCmdViewFront());
    rcCmdMgr.addCommand(new StdCmdViewLeft());
    rcCmdMgr.addCommand(new StdCmdViewRear());
    rcCmdMgr.addCommand(new StdCmdViewRight());
    rcCmdMgr.addCommand(new StdCmdViewTop());
    rcCmdMgr.addCommand(new StdCmdViewIsometric());
    rcCmdMgr.addCommand(new StdCmdViewDimetric());
    rcCmdMgr.addCommand(new StdCmdViewTrimetric());
    rcCmdMgr.addCommand(new StdCmdViewFitAll());
    rcCmdMgr.addCommand(new StdCmdViewVR());
    rcCmdMgr.addCommand(new StdCmdViewFitSelection());
    rcCmdMgr.addCommand(new StdCmdViewRotateLeft());
    rcCmdMgr.addCommand(new StdCmdViewRotateRight());
    rcCmdMgr.addCommand(new StdStoreWorkingView());
    rcCmdMgr.addCommand(new StdRecallWorkingView());
    rcCmdMgr.addCommand(new StdCmdViewGroup());
    rcCmdMgr.addCommand(new StdCmdAlignToSelection());
    rcCmdMgr.addCommand(new StdCmdClarifySelection());

    rcCmdMgr.addCommand(new StdCmdViewExample1());
    rcCmdMgr.addCommand(new StdCmdViewExample2());
    rcCmdMgr.addCommand(new StdCmdViewExample3());

    rcCmdMgr.addCommand(new StdCmdViewIvStereoQuadBuff());
    rcCmdMgr.addCommand(new StdCmdViewIvStereoRedGreen());
    rcCmdMgr.addCommand(new StdCmdViewIvStereoInterleavedColumns());
    rcCmdMgr.addCommand(new StdCmdViewIvStereoInterleavedRows());
    rcCmdMgr.addCommand(new StdCmdViewIvStereoOff());

    rcCmdMgr.addCommand(new StdCmdViewIvIssueCamPos());

    rcCmdMgr.addCommand(new StdCmdViewCreate());
    rcCmdMgr.addCommand(new StdViewScreenShot());
    rcCmdMgr.addCommand(new StdViewLoadImage());
    rcCmdMgr.addCommand(new StdMainFullscreen());
    rcCmdMgr.addCommand(new StdViewDockUndockFullscreen());
    rcCmdMgr.addCommand(new StdCmdToggleVisibility());
    rcCmdMgr.addCommand(new StdCmdToggleTransparency());
    rcCmdMgr.addCommand(new StdCmdToggleSelectability());
    rcCmdMgr.addCommand(new StdCmdShowSelection());
    rcCmdMgr.addCommand(new StdCmdHideSelection());
    rcCmdMgr.addCommand(new StdCmdSelectVisibleObjects());
    rcCmdMgr.addCommand(new StdCmdToggleObjects());
    rcCmdMgr.addCommand(new StdCmdShowObjects());
    rcCmdMgr.addCommand(new StdCmdHideObjects());
    rcCmdMgr.addCommand(new StdOrthographicCamera());
    rcCmdMgr.addCommand(new StdPerspectiveCamera());
    rcCmdMgr.addCommand(new StdCmdToggleClipPlane());
    rcCmdMgr.addCommand(new StdCmdDrawStyle());
    rcCmdMgr.addCommand(new StdCmdViewSaveCamera());
    rcCmdMgr.addCommand(new StdCmdViewRestoreCamera());
    rcCmdMgr.addCommand(new StdCmdFreezeViews());
    rcCmdMgr.addCommand(new StdViewZoomIn());
    rcCmdMgr.addCommand(new StdViewZoomOut());
    rcCmdMgr.addCommand(new StdViewBoxZoom());
    rcCmdMgr.addCommand(new StdBoxSelection());
    rcCmdMgr.addCommand(new StdBoxElementSelection());
    rcCmdMgr.addCommand(new StdCmdTreeExpand());
    rcCmdMgr.addCommand(new StdCmdTreeCollapse());
    rcCmdMgr.addCommand(new StdCmdTreeSelectAllInstances());
    rcCmdMgr.addCommand(new StdCmdSceneInspector());
    rcCmdMgr.addCommand(new StdCmdTextureMapping());
    rcCmdMgr.addCommand(new StdCmdDemoMode());
    rcCmdMgr.addCommand(new StdCmdToggleNavigation());
    rcCmdMgr.addCommand(new StdCmdAxisCross());
    rcCmdMgr.addCommand(new StdCmdSelBoundingBox());
    rcCmdMgr.addCommand(new StdCmdTreeViewActions());
    rcCmdMgr.addCommand(new StdCmdDockOverlay());

    auto hGrp = App::GetApplication().GetParameterGroupByPath(
        "User parameter:BaseApp/Preferences/View"
    );
    if (hGrp->GetASCII("GestureRollFwdCommand").empty()) {
        hGrp->SetASCII("GestureRollFwdCommand", "Std_SelForward");
    }
    if (hGrp->GetASCII("GestureRollBackCommand").empty()) {
        hGrp->SetASCII("GestureRollBackCommand", "Std_SelBack");
    }
    // NOLINTEND
}

}  // namespace Gui
