All checks were successful
Build and Test / build (pull_request) Successful in 42m37s
Register .kc as a recognized file type alongside .FCStd: C++ changes: - Document.cpp: checkFileName() accepts .kc extension - Gui/Document.cpp: Save As and Save Copy dialogs include *.kc - CommandDoc.cpp: Merge document dialog includes *.kc - DlgProjectUtility.cpp: Project utility dialog includes *.kc Python changes: - FreeCADInit.py: register *.kc in import type system - kc_format.py: DocumentObserver that preserves silo/ ZIP entries across saves (caches before save, re-injects after save) - InitGui.py: register kc_format observer on startup Silo integration: - get_cad_file_path() generates .kc paths for new files - find_file_by_part_number() finds both .kc and .FCStd, preferring .kc - search_local_files() lists both .kc and .FCStd files The .kc format is a superset of .FCStd with a silo/ directory containing Kindred platform metadata. See docs/KC_SPECIFICATION.md.
2257 lines
75 KiB
C++
2257 lines
75 KiB
C++
/***************************************************************************
|
|
* 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 <Inventor/nodes/SoCamera.h>
|
|
#include <QApplication>
|
|
#include <QCheckBox>
|
|
#include <QClipboard>
|
|
#include <QDateTime>
|
|
#include <QMessageBox>
|
|
#include <QTextStream>
|
|
#include <QTreeWidgetItem>
|
|
|
|
#include <boost/regex.hpp>
|
|
#include <boost/algorithm/string/replace.hpp>
|
|
|
|
#include <App/AutoTransaction.h>
|
|
#include <App/Document.h>
|
|
#include <App/DocumentObject.h>
|
|
#include <App/Expression.h>
|
|
#include <App/GeoFeature.h>
|
|
#include <Base/Exception.h>
|
|
#include <Base/FileInfo.h>
|
|
#include <Base/Stream.h>
|
|
#include <Base/Tools.h>
|
|
|
|
#include "Action.h"
|
|
#include "Application.h"
|
|
#include "BitmapFactory.h"
|
|
#include "Command.h"
|
|
#include "Control.h"
|
|
#include "DockWindowManager.h"
|
|
#include "FileDialog.h"
|
|
#include "FileOrigin.h"
|
|
#include "MainWindow.h"
|
|
#include "OriginManager.h"
|
|
#include "Selection.h"
|
|
#include "Dialogs/DlgObjectSelection.h"
|
|
#include "Dialogs/DlgProjectInformationImp.h"
|
|
#include "Dialogs/DlgProjectUtility.h"
|
|
#include "GraphvizView.h"
|
|
#include "ManualAlignment.h"
|
|
#include "MergeDocuments.h"
|
|
#include "Navigation/NavigationStyle.h"
|
|
#include "Placement.h"
|
|
#include "Tools.h"
|
|
#include "Transform.h"
|
|
#include "View3DInventor.h"
|
|
#include "View3DInventorViewer.h"
|
|
#include "ViewProvider.h"
|
|
#include "WaitCursor.h"
|
|
|
|
FC_LOG_LEVEL_INIT("Command", false)
|
|
|
|
using namespace Gui;
|
|
|
|
|
|
//===========================================================================
|
|
// Std_Open
|
|
//===========================================================================
|
|
|
|
DEF_STD_CMD(StdCmdOpen)
|
|
|
|
StdCmdOpen::StdCmdOpen()
|
|
: Command("Std_Open")
|
|
{
|
|
// setting the
|
|
sGroup = "File";
|
|
sMenuText = QT_TR_NOOP("&Open…");
|
|
sToolTipText = QT_TR_NOOP("Opens a document or imports files");
|
|
sWhatsThis = "Std_Open";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "document-open";
|
|
sAccel = keySequenceToAccel(QKeySequence::Open);
|
|
eType = NoTransaction;
|
|
}
|
|
|
|
void StdCmdOpen::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
|
|
// Delegate to current origin
|
|
FileOrigin* origin = OriginManager::instance()->currentOrigin();
|
|
if (!origin) {
|
|
return;
|
|
}
|
|
|
|
// Check connection for origins that require authentication
|
|
if (origin->requiresAuthentication() &&
|
|
origin->connectionState() != ConnectionState::Connected) {
|
|
QMessageBox::warning(
|
|
getMainWindow(),
|
|
qApp->translate("StdCmdOpen", "Not Connected"),
|
|
qApp->translate("StdCmdOpen", "Please connect to %1 before opening files.")
|
|
.arg(QString::fromStdString(origin->name()))
|
|
);
|
|
return;
|
|
}
|
|
|
|
origin->openDocumentInteractive();
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Import
|
|
//===========================================================================
|
|
|
|
DEF_STD_CMD_A(StdCmdImport)
|
|
|
|
StdCmdImport::StdCmdImport()
|
|
: Command("Std_Import")
|
|
{
|
|
// setting the
|
|
sGroup = "File";
|
|
sMenuText = QT_TR_NOOP("&Import…");
|
|
sToolTipText = QT_TR_NOOP("Imports a file into the active document");
|
|
sWhatsThis = "Std_Import";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Std_Import";
|
|
sAccel = "Ctrl+Shift+I";
|
|
}
|
|
|
|
void StdCmdImport::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
|
|
// fill the list of registered endings
|
|
QString formatList;
|
|
const char* supported = QT_TR_NOOP("Supported formats");
|
|
const char* allFiles = QT_TR_NOOP("All files (*.*)");
|
|
formatList = QObject::tr(supported);
|
|
formatList += QLatin1String(" (");
|
|
|
|
std::vector<std::string> filetypes = App::GetApplication().getImportTypes();
|
|
std::vector<std::string>::const_iterator it;
|
|
for (it = filetypes.begin(); it != filetypes.end(); ++it) {
|
|
if (*it != "FCStd") {
|
|
// ignore the project file format
|
|
formatList += QLatin1String(" *.");
|
|
formatList += QLatin1String(it->c_str());
|
|
}
|
|
}
|
|
|
|
formatList += QLatin1String(");;");
|
|
|
|
std::map<std::string, std::string> FilterList = App::GetApplication().getImportFilters();
|
|
std::map<std::string, std::string>::const_iterator jt;
|
|
for (jt = FilterList.begin(); jt != FilterList.end(); ++jt) {
|
|
// ignore the project file format
|
|
if (jt->first.find("(*.FCStd)") == std::string::npos) {
|
|
formatList += QLatin1String(jt->first.c_str());
|
|
formatList += QLatin1String(";;");
|
|
}
|
|
}
|
|
formatList += QObject::tr(allFiles);
|
|
|
|
Base::Reference<ParameterGrp> hPath = App::GetApplication()
|
|
.GetUserParameter()
|
|
.GetGroup("BaseApp")
|
|
->GetGroup("Preferences")
|
|
->GetGroup("General");
|
|
QString selectedFilter = QString::fromStdString(hPath->GetASCII("FileImportFilter"));
|
|
QStringList fileList = FileDialog::getOpenFileNames(
|
|
getMainWindow(),
|
|
QObject::tr("Import File"),
|
|
QString(),
|
|
formatList,
|
|
&selectedFilter
|
|
);
|
|
if (!fileList.isEmpty()) {
|
|
hPath->SetASCII("FileImportFilter", selectedFilter.toLatin1().constData());
|
|
SelectModule::Dict dict = SelectModule::importHandler(fileList, selectedFilter);
|
|
|
|
bool emptyDoc = (getActiveGuiDocument()->getDocument()->countObjects() == 0);
|
|
// load the files with the associated modules
|
|
for (SelectModule::Dict::iterator it = dict.begin(); it != dict.end(); ++it) {
|
|
getGuiApplication()->importFrom(
|
|
it.key().toUtf8(),
|
|
getActiveGuiDocument()->getDocument()->getName(),
|
|
it.value().toLatin1()
|
|
);
|
|
}
|
|
|
|
if (emptyDoc) {
|
|
// only do a view fit if the document was empty before. See also parameter
|
|
// 'AutoFitToView' in importFrom()
|
|
std::list<Gui::MDIView*> views = getActiveGuiDocument()->getMDIViewsOfType(
|
|
Gui::View3DInventor::getClassTypeId()
|
|
);
|
|
for (const auto& view : views) {
|
|
view->viewAll();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool StdCmdImport::isActive()
|
|
{
|
|
return (getActiveGuiDocument() ? true : false);
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
// Std_Export
|
|
//===========================================================================
|
|
|
|
DEF_STD_CMD_A(StdCmdExport)
|
|
|
|
StdCmdExport::StdCmdExport()
|
|
: Command("Std_Export")
|
|
{
|
|
// setting the
|
|
sGroup = "File";
|
|
sMenuText = QT_TR_NOOP("&Export…");
|
|
sToolTipText = QT_TR_NOOP("Exports an object in the active document");
|
|
sWhatsThis = "Std_Export";
|
|
sStatusTip = sToolTipText;
|
|
sAccel = "Ctrl+E";
|
|
sPixmap = "Std_Export";
|
|
eType = 0;
|
|
}
|
|
|
|
/**
|
|
Create a default filename from a user-specified format string
|
|
|
|
Format options are:
|
|
%F - the basename of the .FCStd file (or the label, if it is not saved yet)
|
|
%Lx - the label of the selected object(s), separated by character 'x'
|
|
%Px - the label of the selected object(s) and their first parent, separated by character 'x'
|
|
%U - the date and time, in UTC, ISO 8601
|
|
%D - the date and time, in local timezone, ISO 8601
|
|
Any other characters are treated literally, though if the filename is illegal
|
|
it will be changed on saving.
|
|
|
|
The format string is stored in two user preferences (not currently exposed in the GUI):
|
|
* BaseApp/Preferences/General/ExportDefaultFilenameSingle
|
|
* BaseApp/Preferences/General/ExportDefaultFilenameMultiple
|
|
*/
|
|
QString createDefaultExportBasename()
|
|
{
|
|
QString defaultFilename;
|
|
|
|
auto selection = Gui::Selection().getObjectsOfType(App::DocumentObject::getClassTypeId());
|
|
QString exportFormatString;
|
|
if (selection.size() == 1) {
|
|
exportFormatString = QString::fromStdString(
|
|
App::GetApplication()
|
|
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
|
|
->GetASCII("ExportDefaultFilenameSingle", "%F-%P-")
|
|
);
|
|
}
|
|
else {
|
|
exportFormatString = QString::fromStdString(
|
|
App::GetApplication()
|
|
.GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
|
|
->GetASCII("ExportDefaultFilenameMultiple", "%F")
|
|
);
|
|
}
|
|
|
|
// For code simplicity, pull all values we might need
|
|
|
|
// %F - the basename of the.FCStd file(or the label, if it is not saved yet)
|
|
QString docFilename = QString::fromUtf8(App::GetApplication().getActiveDocument()->getFileName());
|
|
QFileInfo fi(docFilename);
|
|
QString fcstdBasename = fi.completeBaseName();
|
|
if (fcstdBasename.isEmpty()) {
|
|
fcstdBasename = QString::fromStdString(
|
|
App::GetApplication().getActiveDocument()->Label.getStrValue()
|
|
);
|
|
}
|
|
|
|
// %L - the label of the selected object(s)
|
|
QStringList objectLabels;
|
|
for (const auto& object : selection) {
|
|
objectLabels.push_back(QString::fromStdString(object->Label.getStrValue()));
|
|
}
|
|
|
|
// %P - the label of the selected objects and their first parent
|
|
QStringList parentLabels;
|
|
for (const auto& object : selection) {
|
|
auto parents = object->getParents();
|
|
QString firstParent;
|
|
if (!parents.empty()) {
|
|
firstParent = QString::fromStdString(parents.front().first->Label.getStrValue());
|
|
}
|
|
parentLabels.append(firstParent + QString::fromStdString(object->Label.getStrValue()));
|
|
}
|
|
|
|
// %U - the date and time, in UTC, ISO 8601
|
|
QDateTime utc = QDateTime(QDateTime::currentDateTimeUtc());
|
|
QString utcISO8601 = utc.toString(Qt::ISODate);
|
|
|
|
// %D - the date and time, in local timezone, ISO 8601
|
|
QDateTime local = utc.toLocalTime();
|
|
QString localISO8601 = local.toString(Qt::ISODate);
|
|
|
|
// Parse the format string one character at a time:
|
|
for (int i = 0; i < exportFormatString.size(); ++i) {
|
|
auto c = exportFormatString.at(i);
|
|
if (c != QLatin1Char('%')) {
|
|
// Anything that's not a format start character is just a literal
|
|
defaultFilename.append(c);
|
|
}
|
|
else {
|
|
// The format start character now requires us to look at at least the next single
|
|
// character (if there isn't another character, the % just gets eaten)
|
|
if (i < exportFormatString.size() - 1) {
|
|
++i;
|
|
auto formatChar = exportFormatString.at(i);
|
|
QChar separatorChar = QLatin1Char('-');
|
|
// If this format type requires an additional char, read that now (or default to
|
|
// '-' if the format string ends)
|
|
if (formatChar == QLatin1Char('L') || formatChar == QLatin1Char('P')) {
|
|
if (i < exportFormatString.size() - 1) {
|
|
++i;
|
|
separatorChar = exportFormatString.at(i);
|
|
}
|
|
}
|
|
|
|
// Handle our format characters:
|
|
if (formatChar == QLatin1Char('F')) {
|
|
defaultFilename.append(fcstdBasename);
|
|
}
|
|
else if (formatChar == QLatin1Char('L')) {
|
|
defaultFilename.append(objectLabels.join(separatorChar));
|
|
}
|
|
else if (formatChar == QLatin1Char('P')) {
|
|
defaultFilename.append(parentLabels.join(separatorChar));
|
|
}
|
|
else if (formatChar == QLatin1Char('U')) {
|
|
defaultFilename.append(utcISO8601);
|
|
}
|
|
else if (formatChar == QLatin1Char('D')) {
|
|
defaultFilename.append(localISO8601);
|
|
}
|
|
else {
|
|
FC_WARN(
|
|
"When parsing default export filename format string, %"
|
|
<< QString(formatChar).toStdString() << " is not a known format string."
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Finally, clean the string so it's valid for all operating systems:
|
|
QString invalidCharacters = QLatin1String("/\\?%*:|\"<>");
|
|
for (const auto& c : invalidCharacters) {
|
|
defaultFilename.replace(c, QLatin1String("_"));
|
|
}
|
|
|
|
return defaultFilename;
|
|
}
|
|
|
|
void StdCmdExport::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
|
|
auto selection = Gui::Selection().getObjectsOfType(App::DocumentObject::getClassTypeId());
|
|
if (selection.empty()) {
|
|
QMessageBox::warning(
|
|
Gui::getMainWindow(),
|
|
QCoreApplication::translate("StdCmdExport", "No Selection"),
|
|
QCoreApplication::translate(
|
|
"StdCmdExport",
|
|
"Select objects to export before using the Export command."
|
|
)
|
|
);
|
|
return;
|
|
}
|
|
|
|
App::DocumentObject* toExport = selection.front();
|
|
App::Document* doc = toExport->getDocument();
|
|
App::ExportInfo exportInfo = doc->exportInfo();
|
|
bool filenameWasGenerated = false;
|
|
|
|
// fill the list of registered suffixes
|
|
QStringList filterList;
|
|
std::map<std::string, std::string> filterMap = App::GetApplication().getExportFilters();
|
|
for (const auto& filter : filterMap) {
|
|
// ignore the project file format
|
|
if (filter.first.find("(*.FCStd)") == std::string::npos) {
|
|
filterList << QString::fromStdString(filter.first);
|
|
}
|
|
}
|
|
QString formatList = filterList.join(QLatin1String(";;"));
|
|
Base::Reference<ParameterGrp> hPath = App::GetApplication()
|
|
.GetUserParameter()
|
|
.GetGroup("BaseApp")
|
|
->GetGroup("Preferences")
|
|
->GetGroup("General");
|
|
QString selectedFilter;
|
|
|
|
if (!exportInfo.filter.empty()) {
|
|
selectedFilter = QString::fromStdString(exportInfo.filter);
|
|
}
|
|
else {
|
|
selectedFilter = QString::fromStdString(hPath->GetASCII("FileExportFilter"));
|
|
}
|
|
|
|
// Create a default filename for the export
|
|
// * If this is the first export (the ExportInfo object' fields are empty)
|
|
// * If this is a repeated export using a filled ExportInfo object:
|
|
// * If the user accepted the default filename last time, regenerate a new
|
|
// default, potentially updating the object label.
|
|
// * If not, default to their previously-set export filename.
|
|
// * If this is an export of a different object than last time
|
|
QString defaultFilename;
|
|
if (exportInfo.filename.empty() || exportInfo.generatedName || exportInfo.object != toExport) {
|
|
// First, get the name and path of the current .FCStd file, if there is one:
|
|
QString docFilename = QString::fromStdString(doc->getFileName());
|
|
|
|
// Find the default location for our exported file. Three possibilities:
|
|
QString exportPath;
|
|
if (!exportInfo.filename.empty()) {
|
|
QFileInfo fi(QString::fromStdString(exportInfo.filename));
|
|
exportPath = fi.path();
|
|
}
|
|
else if (!docFilename.isEmpty()) {
|
|
QFileInfo fi(docFilename);
|
|
exportPath = fi.path();
|
|
}
|
|
else {
|
|
exportPath = Gui::FileDialog::getWorkingDirectory();
|
|
}
|
|
|
|
if (exportInfo.generatedName
|
|
|| exportInfo.object != toExport) { /*<- static, true on first call*/
|
|
defaultFilename = exportPath + QLatin1Char('/') + createDefaultExportBasename();
|
|
|
|
// Append the last extension used, if there is one.
|
|
if (!exportInfo.filename.empty()) {
|
|
QFileInfo lastExportFile(QString::fromStdString(exportInfo.filename));
|
|
if (!lastExportFile.suffix().isEmpty()) {
|
|
defaultFilename += QLatin1String(".") + lastExportFile.suffix();
|
|
}
|
|
}
|
|
filenameWasGenerated = true;
|
|
}
|
|
}
|
|
else {
|
|
defaultFilename = QString::fromStdString(exportInfo.filename);
|
|
}
|
|
// Launch the file selection modal dialog
|
|
QString filename = FileDialog::getSaveFileName(
|
|
getMainWindow(),
|
|
QObject::tr("Export File"),
|
|
defaultFilename,
|
|
formatList,
|
|
&selectedFilter
|
|
);
|
|
if (!filename.isEmpty()) {
|
|
hPath->SetASCII("FileExportFilter", selectedFilter.toLatin1().constData());
|
|
|
|
SelectModule::Dict dict = SelectModule::exportHandler(filename, selectedFilter);
|
|
// export the files with the associated modules
|
|
for (SelectModule::Dict::iterator it = dict.begin(); it != dict.end(); ++it) {
|
|
getGuiApplication()->exportTo(it.key().toUtf8(), doc->getName(), it.value().toLatin1());
|
|
}
|
|
|
|
// Keep a record of if the user used our suggested generated filename. If they
|
|
// did, next time we can recreate it, which will update the object label if
|
|
// there is one.
|
|
QFileInfo defaultExportFI(defaultFilename);
|
|
QFileInfo thisExportFI(filename);
|
|
|
|
if (filenameWasGenerated
|
|
&& thisExportFI.completeBaseName() != defaultExportFI.completeBaseName()) {
|
|
filenameWasGenerated = false;
|
|
}
|
|
|
|
exportInfo.filename = filename.toStdString();
|
|
exportInfo.object = toExport;
|
|
exportInfo.filter = selectedFilter.toStdString();
|
|
exportInfo.generatedName = filenameWasGenerated;
|
|
|
|
doc->setExportInfo(exportInfo);
|
|
}
|
|
}
|
|
|
|
bool StdCmdExport::isActive()
|
|
{
|
|
return (getActiveGuiDocument() ? true : false);
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_MergeProjects
|
|
//===========================================================================
|
|
|
|
DEF_STD_CMD_A(StdCmdMergeProjects)
|
|
|
|
StdCmdMergeProjects::StdCmdMergeProjects()
|
|
: Command("Std_MergeProjects")
|
|
{
|
|
sAppModule = "File";
|
|
sGroup = "File";
|
|
sMenuText = QT_TR_NOOP("&Merge Document");
|
|
|
|
sToolTipText = QT_TR_NOOP("Merges another FreeCAD document into the active one");
|
|
|
|
sWhatsThis = "Std_MergeProjects";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Std_MergeProjects";
|
|
}
|
|
|
|
void StdCmdMergeProjects::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
|
|
QString exe = qApp->applicationName();
|
|
QString project = FileDialog::getOpenFileName(
|
|
Gui::getMainWindow(),
|
|
QString::fromUtf8(QT_TR_NOOP("Merge Document")),
|
|
FileDialog::getWorkingDirectory(),
|
|
QString::fromUtf8(QT_TR_NOOP("%1 document (*.FCStd *.kc)")).arg(exe)
|
|
);
|
|
if (!project.isEmpty()) {
|
|
FileDialog::setWorkingDirectory(project);
|
|
App::Document* doc = App::GetApplication().getActiveDocument();
|
|
QFileInfo info(QString::fromUtf8(doc->FileName.getValue()));
|
|
QFileInfo proj(project);
|
|
if (proj == info) {
|
|
QMessageBox::critical(
|
|
Gui::getMainWindow(),
|
|
QString::fromUtf8(QT_TR_NOOP("Merge Document")),
|
|
QString::fromUtf8(QT_TR_NOOP("Cannot merge document with itself."))
|
|
);
|
|
return;
|
|
}
|
|
|
|
doc->openTransaction("Merge document");
|
|
Base::FileInfo fi((const char*)project.toUtf8());
|
|
Base::ifstream str(fi, std::ios::in | std::ios::binary);
|
|
MergeDocuments md(doc);
|
|
md.importObjects(str);
|
|
str.close();
|
|
doc->commitTransaction();
|
|
}
|
|
}
|
|
|
|
bool StdCmdMergeProjects::isActive()
|
|
{
|
|
return this->hasActiveDocument();
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_DependencyGraph
|
|
//===========================================================================
|
|
|
|
DEF_STD_CMD_A(StdCmdDependencyGraph)
|
|
|
|
StdCmdDependencyGraph::StdCmdDependencyGraph()
|
|
: Command("Std_DependencyGraph")
|
|
{
|
|
// setting the
|
|
sGroup = "Tools";
|
|
sMenuText = QT_TR_NOOP("Dependency Gra&ph");
|
|
|
|
sToolTipText = QT_TR_NOOP("Shows the dependency graph of the objects in the active document");
|
|
sStatusTip = sToolTipText;
|
|
sWhatsThis = "Std_DependencyGraph";
|
|
eType = 0;
|
|
sPixmap = "Std_DependencyGraph";
|
|
}
|
|
|
|
void StdCmdDependencyGraph::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
App::Document* doc = App::GetApplication().getActiveDocument();
|
|
auto view = new Gui::GraphvizView(*doc);
|
|
view->setWindowTitle(qApp->translate("Std_DependencyGraph", "Dependency Graph"));
|
|
getMainWindow()->addWindow(view);
|
|
}
|
|
|
|
bool StdCmdDependencyGraph::isActive()
|
|
{
|
|
return (getActiveGuiDocument() ? true : false);
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_ExportDependencyGraph
|
|
//===========================================================================
|
|
|
|
DEF_STD_CMD_A(StdCmdExportDependencyGraph)
|
|
|
|
StdCmdExportDependencyGraph::StdCmdExportDependencyGraph()
|
|
: Command("Std_ExportDependencyGraph")
|
|
{
|
|
sGroup = "Tools";
|
|
sMenuText = QT_TR_NOOP("Export Dependency &Graph…");
|
|
sToolTipText = QT_TR_NOOP("Exports the dependency graph as a Graphviz (.gv) file");
|
|
sStatusTip = sToolTipText;
|
|
sWhatsThis = "Std_ExportDependencyGraph";
|
|
eType = 0;
|
|
// sPixmap = "Std_ExportDependencyGraph";
|
|
}
|
|
|
|
void StdCmdExportDependencyGraph::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
App::Document* doc = App::GetApplication().getActiveDocument();
|
|
QString format = QStringLiteral("%1 (*.gv)").arg(Gui::GraphvizView::tr("Graphviz format"));
|
|
QString fn = Gui::FileDialog::getSaveFileName(
|
|
Gui::getMainWindow(),
|
|
Gui::GraphvizView::tr("Export Graph"),
|
|
QString(),
|
|
format
|
|
);
|
|
if (!fn.isEmpty()) {
|
|
QFile file(fn);
|
|
if (file.open(QFile::WriteOnly)) {
|
|
std::stringstream str;
|
|
doc->exportGraphviz(str);
|
|
QByteArray buffer = QByteArray::fromStdString(str.str());
|
|
file.write(buffer);
|
|
file.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool StdCmdExportDependencyGraph::isActive()
|
|
{
|
|
return (getActiveGuiDocument() ? true : false);
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_New
|
|
//===========================================================================
|
|
|
|
DEF_STD_CMD(StdCmdNew)
|
|
|
|
StdCmdNew::StdCmdNew()
|
|
: Command("Std_New")
|
|
{
|
|
sGroup = "File";
|
|
sMenuText = QT_TR_NOOP("&New Document");
|
|
sToolTipText = QT_TR_NOOP("Creates a new empty document");
|
|
sWhatsThis = "Std_New";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "document-new";
|
|
sAccel = keySequenceToAccel(QKeySequence::New);
|
|
}
|
|
|
|
void StdCmdNew::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
|
|
// Delegate to current origin
|
|
FileOrigin* origin = OriginManager::instance()->currentOrigin();
|
|
if (!origin) {
|
|
return;
|
|
}
|
|
|
|
App::Document* doc = origin->newDocument();
|
|
if (!doc) {
|
|
return;
|
|
}
|
|
|
|
// Set default view orientation for the new document
|
|
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()
|
|
);
|
|
|
|
ParameterGrp::handle hViewGrp = App::GetApplication().GetParameterGroupByPath(
|
|
"User parameter:BaseApp/Preferences/View"
|
|
);
|
|
if (hViewGrp->GetBool("ShowAxisCross")) {
|
|
doCommand(Command::Gui, "Gui.ActiveDocument.ActiveView.setAxisCross(True)");
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Save
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdSave)
|
|
|
|
StdCmdSave::StdCmdSave()
|
|
: Command("Std_Save")
|
|
{
|
|
sGroup = "File";
|
|
sMenuText = QT_TR_NOOP("&Save");
|
|
sToolTipText = QT_TR_NOOP("Saves the active document");
|
|
sWhatsThis = "Std_Save";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "document-save";
|
|
sAccel = keySequenceToAccel(QKeySequence::Save);
|
|
eType = 0;
|
|
}
|
|
|
|
void StdCmdSave::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
|
|
App::Document* doc = App::GetApplication().getActiveDocument();
|
|
if (!doc) {
|
|
return;
|
|
}
|
|
|
|
// Use document's origin for save, not current origin
|
|
FileOrigin* origin = OriginManager::instance()->findOwningOrigin(doc);
|
|
if (!origin) {
|
|
// Document has no origin yet - use current origin for first save
|
|
origin = OriginManager::instance()->currentOrigin();
|
|
}
|
|
|
|
if (!origin) {
|
|
return;
|
|
}
|
|
|
|
// Try to save the document
|
|
if (!origin->saveDocument(doc)) {
|
|
// If save failed (e.g., no filename), try SaveAs
|
|
origin->saveDocumentAsInteractive(doc);
|
|
}
|
|
}
|
|
|
|
bool StdCmdSave::isActive()
|
|
{
|
|
return App::GetApplication().getActiveDocument() != nullptr;
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_SaveAs
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdSaveAs)
|
|
|
|
StdCmdSaveAs::StdCmdSaveAs()
|
|
: Command("Std_SaveAs")
|
|
{
|
|
sGroup = "File";
|
|
sMenuText = QT_TR_NOOP("Save &As…");
|
|
sToolTipText = QT_TR_NOOP("Saves the active document under a new file name");
|
|
sWhatsThis = "Std_SaveAs";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "document-save-as";
|
|
sAccel = keySequenceToAccel(QKeySequence::SaveAs);
|
|
eType = 0;
|
|
}
|
|
|
|
void StdCmdSaveAs::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
|
|
App::Document* doc = App::GetApplication().getActiveDocument();
|
|
if (!doc) {
|
|
return;
|
|
}
|
|
|
|
auto* mgr = OriginManager::instance();
|
|
FileOrigin* currentOrigin = mgr->currentOrigin();
|
|
FileOrigin* docOrigin = mgr->originForDocument(doc);
|
|
|
|
if (!currentOrigin) {
|
|
return;
|
|
}
|
|
|
|
// Determine workflow based on document and target origins
|
|
OriginType currentType = currentOrigin->type();
|
|
OriginType docType = docOrigin ? docOrigin->type() : OriginType::Local;
|
|
|
|
if (docOrigin == currentOrigin || !docOrigin) {
|
|
// Same origin or new document - standard SaveAs
|
|
currentOrigin->saveDocumentAsInteractive(doc);
|
|
}
|
|
else if (currentType == OriginType::PLM && docType == OriginType::Local) {
|
|
// Local → PLM: Migration workflow
|
|
// The PLM origin's saveDocumentAsInteractive should handle this
|
|
currentOrigin->saveDocumentAsInteractive(doc);
|
|
}
|
|
else if (currentType == OriginType::Local && docType == OriginType::PLM) {
|
|
// PLM → Local: Export workflow
|
|
// Use local origin to save without PLM tracking
|
|
currentOrigin->saveDocumentAsInteractive(doc);
|
|
}
|
|
else if (currentType == OriginType::PLM && docType == OriginType::PLM) {
|
|
// PLM → Different PLM: Transfer workflow
|
|
currentOrigin->saveDocumentAsInteractive(doc);
|
|
}
|
|
else {
|
|
// Default: use current origin
|
|
currentOrigin->saveDocumentAsInteractive(doc);
|
|
}
|
|
}
|
|
|
|
bool StdCmdSaveAs::isActive()
|
|
{
|
|
return App::GetApplication().getActiveDocument() != nullptr;
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_SaveCopy
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdSaveCopy)
|
|
|
|
StdCmdSaveCopy::StdCmdSaveCopy()
|
|
: Command("Std_SaveCopy")
|
|
{
|
|
sGroup = "File";
|
|
sMenuText = QT_TR_NOOP("Save Cop&y");
|
|
|
|
sToolTipText = QT_TR_NOOP("Saves a copy of the active document under a new file name");
|
|
sWhatsThis = "Std_SaveCopy";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Std_SaveCopy";
|
|
}
|
|
|
|
void StdCmdSaveCopy::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
doCommand(Command::Gui, "Gui.SendMsgToActiveView(\"SaveCopy\")");
|
|
}
|
|
|
|
bool StdCmdSaveCopy::isActive()
|
|
{
|
|
return (getActiveGuiDocument() ? true : false);
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_SaveAll
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdSaveAll)
|
|
|
|
StdCmdSaveAll::StdCmdSaveAll()
|
|
: Command("Std_SaveAll")
|
|
{
|
|
sGroup = "File";
|
|
sMenuText = QT_TR_NOOP("Sa&ve All");
|
|
sToolTipText = QT_TR_NOOP("Saves all open documents");
|
|
sWhatsThis = "Std_SaveAll";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Std_SaveAll";
|
|
}
|
|
|
|
void StdCmdSaveAll::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
Gui::Document::saveAll();
|
|
}
|
|
|
|
bool StdCmdSaveAll::isActive()
|
|
{
|
|
return (getActiveGuiDocument() ? true : false);
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
// Std_Revert
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdRevert)
|
|
|
|
StdCmdRevert::StdCmdRevert()
|
|
: Command("Std_Revert")
|
|
{
|
|
sGroup = "File";
|
|
sMenuText = QT_TR_NOOP("Rever&t");
|
|
sToolTipText = QT_TR_NOOP("Reverts to the saved version of this file");
|
|
sWhatsThis = "Std_Revert";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Std_Revert";
|
|
eType = NoTransaction;
|
|
}
|
|
|
|
void StdCmdRevert::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
QMessageBox msgBox(Gui::getMainWindow());
|
|
msgBox.setIcon(QMessageBox::Question);
|
|
msgBox.setWindowTitle(qApp->translate("Std_Revert", "Revert Document"));
|
|
msgBox.setText(
|
|
qApp->translate("Std_Revert", "This will discard all the changes since the last file save.")
|
|
);
|
|
msgBox.setInformativeText(qApp->translate("Std_Revert", "Continue?"));
|
|
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
|
msgBox.setDefaultButton(QMessageBox::No);
|
|
int ret = msgBox.exec();
|
|
if (ret == QMessageBox::Yes) {
|
|
doCommand(Command::App, "App.ActiveDocument.restore()");
|
|
}
|
|
}
|
|
|
|
bool StdCmdRevert::isActive()
|
|
{
|
|
return (getActiveGuiDocument() ? true : false);
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_ProjectInfo
|
|
//===========================================================================
|
|
|
|
DEF_STD_CMD_A(StdCmdProjectInfo)
|
|
|
|
StdCmdProjectInfo::StdCmdProjectInfo()
|
|
: Command("Std_ProjectInfo")
|
|
{
|
|
// setting the
|
|
sGroup = "File";
|
|
sMenuText = QT_TR_NOOP("Doc&ument Information");
|
|
|
|
sToolTipText = QT_TR_NOOP("Shows information about the active document");
|
|
sWhatsThis = "Std_ProjectInfo";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "document-properties";
|
|
}
|
|
|
|
void StdCmdProjectInfo::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
Gui::Dialog::DlgProjectInformationImp dlg(getActiveGuiDocument()->getDocument(), getMainWindow());
|
|
dlg.exec();
|
|
}
|
|
|
|
bool StdCmdProjectInfo::isActive()
|
|
{
|
|
return (getActiveGuiDocument() ? true : false);
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_ProjectUtil
|
|
//===========================================================================
|
|
|
|
DEF_STD_CMD_A(StdCmdProjectUtil)
|
|
|
|
StdCmdProjectUtil::StdCmdProjectUtil()
|
|
: Command("Std_ProjectUtil")
|
|
{
|
|
// setting the
|
|
sGroup = "Tools";
|
|
sWhatsThis = "Std_ProjectUtil";
|
|
sMenuText = QT_TR_NOOP("Do&cument Utility");
|
|
|
|
sToolTipText = QT_TR_NOOP("Extracts or creates document files");
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Std_ProjectUtil";
|
|
}
|
|
|
|
void StdCmdProjectUtil::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
Gui::Dialog::DlgProjectUtility dlg(getMainWindow());
|
|
dlg.exec();
|
|
}
|
|
|
|
bool StdCmdProjectUtil::isActive()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Print
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdPrint)
|
|
|
|
StdCmdPrint::StdCmdPrint()
|
|
: Command("Std_Print")
|
|
{
|
|
sGroup = "File";
|
|
sMenuText = QT_TR_NOOP("&Print");
|
|
|
|
sToolTipText = QT_TR_NOOP("Prints the active document");
|
|
sWhatsThis = "Std_Print";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "document-print";
|
|
sAccel = keySequenceToAccel(QKeySequence::Print);
|
|
eType = 0;
|
|
}
|
|
|
|
void StdCmdPrint::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
if (getMainWindow()->activeWindow()) {
|
|
getMainWindow()->showMessage(QObject::tr("Printing…"));
|
|
getMainWindow()->activeWindow()->print();
|
|
}
|
|
}
|
|
|
|
bool StdCmdPrint::isActive()
|
|
{
|
|
return getGuiApplication()->sendHasMsgToActiveView("Print");
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_PrintPreview
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdPrintPreview)
|
|
|
|
StdCmdPrintPreview::StdCmdPrintPreview()
|
|
: Command("Std_PrintPreview")
|
|
{
|
|
sGroup = "File";
|
|
sMenuText = QT_TR_NOOP("Print Previe&w");
|
|
|
|
sToolTipText = QT_TR_NOOP("Previews the active document before printing");
|
|
sWhatsThis = "Std_PrintPreview";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "document-print-preview";
|
|
eType = 0;
|
|
}
|
|
|
|
void StdCmdPrintPreview::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
if (getMainWindow()->activeWindow()) {
|
|
getMainWindow()->activeWindow()->printPreview();
|
|
}
|
|
}
|
|
|
|
bool StdCmdPrintPreview::isActive()
|
|
{
|
|
return getGuiApplication()->sendHasMsgToActiveView("PrintPreview");
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_PrintPdf
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdPrintPdf)
|
|
|
|
StdCmdPrintPdf::StdCmdPrintPdf()
|
|
: Command("Std_PrintPdf")
|
|
{
|
|
sGroup = "File";
|
|
sMenuText = QT_TR_NOOP("Export P&DF");
|
|
|
|
sToolTipText = QT_TR_NOOP("Exports the active document as a PDF file");
|
|
sWhatsThis = "Std_PrintPdf";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Std_PrintPdf";
|
|
eType = 0;
|
|
}
|
|
|
|
void StdCmdPrintPdf::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
if (getMainWindow()->activeWindow()) {
|
|
getMainWindow()->showMessage(QObject::tr("Exporting PDF…"));
|
|
getMainWindow()->activeWindow()->printPdf();
|
|
}
|
|
}
|
|
|
|
bool StdCmdPrintPdf::isActive()
|
|
{
|
|
return getGuiApplication()->sendHasMsgToActiveView("PrintPdf");
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Quit
|
|
//===========================================================================
|
|
|
|
DEF_STD_CMD_C(StdCmdQuit)
|
|
|
|
StdCmdQuit::StdCmdQuit()
|
|
: Command("Std_Quit")
|
|
{
|
|
sGroup = "File";
|
|
sMenuText = QT_TR_NOOP("E&xit");
|
|
sToolTipText = QT_TR_NOOP("Quits the application");
|
|
sWhatsThis = "Std_Quit";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "application-exit";
|
|
sAccel = keySequenceToAccel(QKeySequence::Quit);
|
|
eType = NoTransaction;
|
|
}
|
|
|
|
Action* StdCmdQuit::createAction()
|
|
{
|
|
Action* pcAction = Command::createAction();
|
|
pcAction->setMenuRole(QAction::QuitRole);
|
|
|
|
return pcAction;
|
|
}
|
|
|
|
void StdCmdQuit::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// close the main window and exit the event loop
|
|
getMainWindow()->close();
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Undo
|
|
//===========================================================================
|
|
|
|
DEF_STD_CMD_AC(StdCmdUndo)
|
|
|
|
StdCmdUndo::StdCmdUndo()
|
|
: Command("Std_Undo")
|
|
{
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("&Undo");
|
|
sToolTipText = QT_TR_NOOP("Undoes the previous action");
|
|
sWhatsThis = "Std_Undo";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "edit-undo";
|
|
sAccel = keySequenceToAccel(QKeySequence::Undo);
|
|
eType = ForEdit | NoTransaction;
|
|
}
|
|
|
|
void StdCmdUndo::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// Application::Instance->slotUndo();
|
|
getGuiApplication()->sendMsgToActiveView("Undo");
|
|
}
|
|
|
|
bool StdCmdUndo::isActive()
|
|
{
|
|
return getGuiApplication()->sendHasMsgToActiveView("Undo");
|
|
}
|
|
|
|
Action* StdCmdUndo::createAction()
|
|
{
|
|
Action* pcAction;
|
|
|
|
pcAction = new UndoAction(this, getMainWindow());
|
|
pcAction->setShortcut(QString::fromLatin1(getAccel()));
|
|
applyCommandData(this->className(), pcAction);
|
|
if (getPixmap()) {
|
|
pcAction->setIcon(Gui::BitmapFactory().iconFromTheme(getPixmap()));
|
|
}
|
|
|
|
return pcAction;
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Redo
|
|
//===========================================================================
|
|
|
|
DEF_STD_CMD_AC(StdCmdRedo)
|
|
|
|
StdCmdRedo::StdCmdRedo()
|
|
: Command("Std_Redo")
|
|
{
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("&Redo");
|
|
sToolTipText = QT_TR_NOOP("Redoes a previously undone action");
|
|
sWhatsThis = "Std_Redo";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "edit-redo";
|
|
sAccel = keySequenceToAccel(QKeySequence::Redo);
|
|
eType = ForEdit | NoTransaction;
|
|
}
|
|
|
|
void StdCmdRedo::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
// Application::Instance->slotRedo();
|
|
getGuiApplication()->sendMsgToActiveView("Redo");
|
|
}
|
|
|
|
bool StdCmdRedo::isActive()
|
|
{
|
|
return getGuiApplication()->sendHasMsgToActiveView("Redo");
|
|
}
|
|
|
|
Action* StdCmdRedo::createAction()
|
|
{
|
|
Action* pcAction;
|
|
|
|
pcAction = new RedoAction(this, getMainWindow());
|
|
pcAction->setShortcut(QString::fromLatin1(getAccel()));
|
|
applyCommandData(this->className(), pcAction);
|
|
if (getPixmap()) {
|
|
pcAction->setIcon(Gui::BitmapFactory().iconFromTheme(getPixmap()));
|
|
}
|
|
|
|
return pcAction;
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Cut
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdCut)
|
|
|
|
StdCmdCut::StdCmdCut()
|
|
: Command("Std_Cut")
|
|
{
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("Cu&t");
|
|
sToolTipText = QT_TR_NOOP("Removes the selection and copies it to the clipboard");
|
|
sWhatsThis = "Std_Cut";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "edit-cut";
|
|
sAccel = keySequenceToAccel(QKeySequence::Cut);
|
|
}
|
|
|
|
void StdCmdCut::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
getGuiApplication()->sendMsgToActiveView("Cut");
|
|
}
|
|
|
|
bool StdCmdCut::isActive()
|
|
{
|
|
return getGuiApplication()->sendHasMsgToActiveView("Cut");
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Copy
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdCopy)
|
|
|
|
StdCmdCopy::StdCmdCopy()
|
|
: Command("Std_Copy")
|
|
{
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("&Copy");
|
|
sToolTipText = QT_TR_NOOP("Copies the selection to the clipboard");
|
|
sWhatsThis = "Std_Copy";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "edit-copy";
|
|
sAccel = keySequenceToAccel(QKeySequence::Copy);
|
|
}
|
|
|
|
void StdCmdCopy::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
bool done = getGuiApplication()->sendMsgToFocusView("Copy");
|
|
if (!done) {
|
|
QMimeData* mimeData = getMainWindow()->createMimeDataFromSelection();
|
|
QClipboard* cb = QApplication::clipboard();
|
|
cb->setMimeData(mimeData);
|
|
}
|
|
}
|
|
|
|
bool StdCmdCopy::isActive()
|
|
{
|
|
if (getGuiApplication()->sendHasMsgToFocusView("Copy")) {
|
|
return true;
|
|
}
|
|
return Selection().hasSelection();
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Paste
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdPaste)
|
|
|
|
StdCmdPaste::StdCmdPaste()
|
|
: Command("Std_Paste")
|
|
{
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("&Paste");
|
|
sToolTipText = QT_TR_NOOP("Pastes the contents of the clipboard");
|
|
sWhatsThis = "Std_Paste";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "edit-paste";
|
|
sAccel = keySequenceToAccel(QKeySequence::Paste);
|
|
}
|
|
|
|
void StdCmdPaste::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
bool done = getGuiApplication()->sendMsgToFocusView("Paste");
|
|
if (!done) {
|
|
QClipboard* cb = QApplication::clipboard();
|
|
const QMimeData* mimeData = cb->mimeData();
|
|
if (mimeData) {
|
|
WaitCursor wc;
|
|
getMainWindow()->insertFromMimeData(mimeData);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool StdCmdPaste::isActive()
|
|
{
|
|
if (getGuiApplication()->sendHasMsgToFocusView("Paste")) {
|
|
return true;
|
|
}
|
|
QClipboard* cb = QApplication::clipboard();
|
|
const QMimeData* mime = cb->mimeData();
|
|
if (!mime) {
|
|
return false;
|
|
}
|
|
return getMainWindow()->canInsertFromMimeData(mime);
|
|
}
|
|
|
|
DEF_STD_CMD_A(StdCmdDuplicateSelection)
|
|
|
|
StdCmdDuplicateSelection::StdCmdDuplicateSelection()
|
|
: Command("Std_DuplicateSelection")
|
|
{
|
|
sAppModule = "Edit";
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("Duplicate Selecti&on");
|
|
sToolTipText = QT_TR_NOOP("Duplicates the selected objects to the active document");
|
|
sWhatsThis = "Std_DuplicateSelection";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "Std_DuplicateSelection";
|
|
}
|
|
|
|
void StdCmdDuplicateSelection::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
std::vector<App::DocumentObject*> sel;
|
|
std::set<App::DocumentObject*> objSet;
|
|
for (auto& s : Selection().getCompleteSelection()) {
|
|
if (s.pObject && s.pObject->isAttachedToDocument() && objSet.insert(s.pObject).second) {
|
|
sel.push_back(s.pObject);
|
|
}
|
|
}
|
|
if (sel.empty()) {
|
|
return;
|
|
}
|
|
|
|
bool hasXLink = false;
|
|
Base::FileInfo fi(App::Application::getTempFileName());
|
|
{
|
|
auto all = App::Document::getDependencyList(sel);
|
|
if (all.size() > sel.size()) {
|
|
DlgObjectSelection dlg(sel, getMainWindow());
|
|
if (dlg.exec() != QDialog::Accepted) {
|
|
return;
|
|
}
|
|
sel = dlg.getSelections();
|
|
if (sel.empty()) {
|
|
return;
|
|
}
|
|
}
|
|
std::vector<App::Document*> unsaved;
|
|
hasXLink = App::PropertyXLink::hasXLink(sel, &unsaved);
|
|
if (!unsaved.empty()) {
|
|
QMessageBox::critical(
|
|
getMainWindow(),
|
|
QObject::tr("Unsaved Document"),
|
|
QObject::tr(
|
|
"The exported object contains an external link. Save the document."
|
|
"at least once before exporting."
|
|
)
|
|
);
|
|
return;
|
|
}
|
|
|
|
// save stuff to file
|
|
Base::ofstream str(fi, std::ios::out | std::ios::binary);
|
|
App::Document* doc = sel.front()->getDocument();
|
|
MergeDocuments mimeView(doc);
|
|
doc->exportObjects(sel, str);
|
|
str.close();
|
|
}
|
|
App::Document* doc = App::GetApplication().getActiveDocument();
|
|
if (doc) {
|
|
bool proceed = true;
|
|
if (hasXLink && !doc->isSaved()) {
|
|
auto ret = QMessageBox::question(
|
|
getMainWindow(),
|
|
qApp->translate("Std_DuplicateSelection", "Object Dependencies"),
|
|
qApp->translate(
|
|
"Std_DuplicateSelection",
|
|
"To link to external objects, the document must be saved at least once.\n"
|
|
"Save the document now?"
|
|
),
|
|
QMessageBox::Yes,
|
|
QMessageBox::No
|
|
);
|
|
if (ret == QMessageBox::Yes) {
|
|
proceed = Application::Instance->getDocument(doc)->saveAs();
|
|
}
|
|
}
|
|
if (proceed) {
|
|
doc->openTransaction("Duplicate");
|
|
// restore objects from file and add to active document
|
|
Base::ifstream str(fi, std::ios::in | std::ios::binary);
|
|
MergeDocuments mimeView(doc);
|
|
mimeView.importObjects(str);
|
|
str.close();
|
|
doc->commitTransaction();
|
|
}
|
|
}
|
|
fi.deleteFile();
|
|
}
|
|
|
|
bool StdCmdDuplicateSelection::isActive()
|
|
{
|
|
return Gui::Selection().hasSelection();
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_SelectAll
|
|
//===========================================================================
|
|
|
|
DEF_STD_CMD_A(StdCmdSelectAll)
|
|
|
|
StdCmdSelectAll::StdCmdSelectAll()
|
|
: Command("Std_SelectAll")
|
|
{
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("Select &All");
|
|
sToolTipText = QT_TR_NOOP("Selects all objects in the active document");
|
|
sWhatsThis = "Std_SelectAll";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "edit-select-all";
|
|
sAccel = "Ctrl+A"; // supersedes shortcuts for text edits
|
|
|
|
// this cmd only alters selection, not doc or 3d view
|
|
eType = AlterSelection;
|
|
}
|
|
|
|
void StdCmdSelectAll::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
|
|
auto* activeDoc = Application::Instance->activeDocument();
|
|
if (activeDoc) {
|
|
auto* editingVP = activeDoc->getInEdit();
|
|
if (editingVP && editingVP->selectAll()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// fallback to doc level select
|
|
SelectionSingleton& rSel = Selection();
|
|
App::Document* doc = App::GetApplication().getActiveDocument();
|
|
if (doc) {
|
|
std::vector<App::DocumentObject*> objs = doc->getObjectsOfType(
|
|
App::DocumentObject::getClassTypeId()
|
|
);
|
|
rSel.setSelection(doc->getName(), objs);
|
|
}
|
|
}
|
|
|
|
bool StdCmdSelectAll::isActive()
|
|
{
|
|
return App::GetApplication().getActiveDocument() != nullptr;
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Delete
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdDelete)
|
|
|
|
StdCmdDelete::StdCmdDelete()
|
|
: Command("Std_Delete")
|
|
{
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("&Delete");
|
|
sToolTipText = QT_TR_NOOP("Deletes the selected objects");
|
|
sWhatsThis = "Std_Delete";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "edit-delete";
|
|
sAccel = keySequenceToAccel(QtTools::deleteKeySequence());
|
|
eType = ForEdit;
|
|
}
|
|
|
|
void StdCmdDelete::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
|
|
std::set<App::Document*> docs;
|
|
try {
|
|
openCommand(QT_TRANSLATE_NOOP("Command", "Delete"));
|
|
if (getGuiApplication()->sendHasMsgToFocusView(getName())) {
|
|
commitCommand();
|
|
return;
|
|
}
|
|
|
|
App::TransactionLocker tlock;
|
|
|
|
Gui::getMainWindow()->setUpdatesEnabled(false);
|
|
auto editDoc = Application::Instance->editDocument();
|
|
ViewProviderDocumentObject* vpedit = nullptr;
|
|
if (editDoc) {
|
|
vpedit = freecad_cast<ViewProviderDocumentObject*>(editDoc->getInEdit());
|
|
}
|
|
if (vpedit && !vpedit->acceptDeletionsInEdit()) {
|
|
for (auto& sel : Selection().getSelectionEx(editDoc->getDocument()->getName())) {
|
|
if (sel.getObject() == vpedit->getObject()) {
|
|
if (!sel.getSubNames().empty()) {
|
|
vpedit->onDelete(sel.getSubNames());
|
|
docs.insert(editDoc->getDocument());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
std::set<QString> affectedLabels;
|
|
bool more = false;
|
|
auto sels = Selection().getSelectionEx();
|
|
bool autoDeletion = true;
|
|
for (auto& sel : sels) {
|
|
auto obj = sel.getObject();
|
|
if (obj == nullptr) {
|
|
Base::Console().developerWarning(
|
|
"StdCmdDelete::activated",
|
|
"App::DocumentObject pointer is nullptr\n"
|
|
);
|
|
continue;
|
|
}
|
|
for (auto parent : obj->getInList()) {
|
|
if (!Selection().isSelected(parent)) {
|
|
ViewProvider* vp = Application::Instance->getViewProvider(parent);
|
|
if (vp && !vp->canDelete(obj)) {
|
|
autoDeletion = false;
|
|
QString label;
|
|
if (parent->getDocument() != obj->getDocument()) {
|
|
label = QLatin1String(parent->getFullName().c_str());
|
|
}
|
|
else {
|
|
label = QLatin1String(parent->getNameInDocument());
|
|
}
|
|
if (parent->Label.getStrValue() != parent->getNameInDocument()) {
|
|
label += QStringLiteral(" (%1)").arg(
|
|
QString::fromUtf8(parent->Label.getValue())
|
|
);
|
|
}
|
|
affectedLabels.insert(label);
|
|
if (affectedLabels.size() >= 10) {
|
|
more = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (more) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!autoDeletion) {
|
|
QString bodyMessage;
|
|
QTextStream bodyMessageStream(&bodyMessage);
|
|
bodyMessageStream << qApp->translate(
|
|
"Std_Delete",
|
|
"The following referencing objects might break.\n\n"
|
|
"Continue?\n"
|
|
);
|
|
for (const auto& currentLabel : affectedLabels) {
|
|
bodyMessageStream << '\n' << currentLabel;
|
|
}
|
|
if (more) {
|
|
bodyMessageStream << "\n...";
|
|
}
|
|
|
|
auto ret = QMessageBox::warning(
|
|
Gui::getMainWindow(),
|
|
qApp->translate("Std_Delete", "Object Dependencies"),
|
|
bodyMessage,
|
|
QMessageBox::Yes,
|
|
QMessageBox::No
|
|
);
|
|
if (ret == QMessageBox::Yes) {
|
|
autoDeletion = true;
|
|
}
|
|
}
|
|
if (autoDeletion) {
|
|
for (auto& sel : sels) {
|
|
auto obj = sel.getObject();
|
|
Gui::ViewProvider* vp = Application::Instance->getViewProvider(obj);
|
|
if (vp) {
|
|
// ask the ViewProvider if it wants to do some clean up
|
|
if (vp->onDelete(sel.getSubNames())) {
|
|
docs.insert(obj->getDocument());
|
|
FCMD_OBJ_DOC_CMD(obj, "removeObject('" << obj->getNameInDocument() << "')");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!docs.empty()) {
|
|
const auto& outList = App::PropertyXLink::getDocumentOutList();
|
|
for (auto it = docs.begin(); it != docs.end(); ++it) {
|
|
auto itd = outList.find(*it);
|
|
if (itd != outList.end()) {
|
|
for (auto doc : itd->second) {
|
|
if (doc != *it) {
|
|
docs.erase(doc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (auto doc : docs) {
|
|
FCMD_DOC_CMD(doc, "recompute()");
|
|
}
|
|
}
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
QMessageBox::critical(
|
|
getMainWindow(),
|
|
QObject::tr("Delete Failed"),
|
|
QString::fromLatin1(e.what())
|
|
);
|
|
e.reportException();
|
|
}
|
|
catch (...) {
|
|
QMessageBox::critical(
|
|
getMainWindow(),
|
|
QObject::tr("Delete Failed"),
|
|
QStringLiteral("Unknown error")
|
|
);
|
|
}
|
|
commitCommand();
|
|
Gui::getMainWindow()->setUpdatesEnabled(true);
|
|
Gui::getMainWindow()->update();
|
|
}
|
|
|
|
bool StdCmdDelete::isActive()
|
|
{
|
|
return !Selection().getCompleteSelection().empty();
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Refresh
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdRefresh)
|
|
|
|
StdCmdRefresh::StdCmdRefresh()
|
|
: Command("Std_Refresh")
|
|
{
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("Recompute");
|
|
sToolTipText = QT_TR_NOOP("Recomputes the active document");
|
|
sWhatsThis = "Std_Refresh";
|
|
sStatusTip = sToolTipText;
|
|
sPixmap = "view-refresh";
|
|
sAccel = keySequenceToAccel(QKeySequence::Refresh);
|
|
eType = AlterDoc | Alter3DView | AlterSelection | ForEdit;
|
|
bCanLog = false;
|
|
|
|
// Make it optional to create a transaction for a recompute.
|
|
// The new default behaviour is quite cumbersome in some cases because when
|
|
// undoing the last transaction the manual recompute will clear the redo stack.
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
|
|
"User parameter:BaseApp/Preferences/Document"
|
|
);
|
|
bool create = hGrp->GetBool("TransactionOnRecompute", false);
|
|
if (!create) {
|
|
eType = eType | NoTransaction;
|
|
}
|
|
}
|
|
|
|
void StdCmdRefresh::activated([[maybe_unused]] int iMsg)
|
|
{
|
|
if (!getActiveGuiDocument()) {
|
|
return;
|
|
}
|
|
|
|
App::AutoTransaction trans((eType & NoTransaction) ? nullptr : "Recompute");
|
|
try {
|
|
doCommand(Doc, "App.activeDocument().recompute(None,True,True)");
|
|
}
|
|
catch (Base::Exception& /*e*/) {
|
|
auto ret = QMessageBox::warning(
|
|
getMainWindow(),
|
|
QObject::tr("Dependency Error"),
|
|
qApp->translate(
|
|
"Std_Refresh",
|
|
"The document contains dependency cycles.\n"
|
|
"Check the report view for more details.\n\n"
|
|
"Proceed?"
|
|
),
|
|
QMessageBox::Yes,
|
|
QMessageBox::No
|
|
);
|
|
if (ret == QMessageBox::No) {
|
|
return;
|
|
}
|
|
doCommand(Doc, "App.activeDocument().recompute(None,True)");
|
|
}
|
|
}
|
|
|
|
bool StdCmdRefresh::isActive()
|
|
{
|
|
return this->getDocument() && this->getDocument()->mustExecute();
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Transform
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdTransform)
|
|
|
|
StdCmdTransform::StdCmdTransform()
|
|
: Command("Std_Transform")
|
|
{
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("Transform");
|
|
|
|
sToolTipText = QT_TR_NOOP("Transforms the selected object");
|
|
sStatusTip = sToolTipText;
|
|
sWhatsThis = "Std_Transform";
|
|
}
|
|
|
|
void StdCmdTransform::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
Gui::Control().showDialog(new Gui::Dialog::TaskTransform());
|
|
}
|
|
|
|
bool StdCmdTransform::isActive()
|
|
{
|
|
return (Gui::Control().activeDialog() == nullptr);
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Placement
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdPlacement)
|
|
|
|
StdCmdPlacement::StdCmdPlacement()
|
|
: Command("Std_Placement")
|
|
{
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("P&lacement");
|
|
|
|
sToolTipText = QT_TR_NOOP(
|
|
"Opens the placement editor to adjust the placement of the selected object"
|
|
);
|
|
|
|
sStatusTip = sToolTipText;
|
|
sWhatsThis = "Std_Placement";
|
|
sPixmap = "Std_Placement";
|
|
}
|
|
|
|
void StdCmdPlacement::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
std::vector<App::DocumentObject*> sel = Gui::Selection().getObjectsOfType(
|
|
App::GeoFeature::getClassTypeId()
|
|
);
|
|
auto plm = new Gui::Dialog::TaskPlacement();
|
|
if (!sel.empty()) {
|
|
App::Property* prop = sel.front()->getPropertyByName("Placement");
|
|
if (prop && prop->is<App::PropertyPlacement>()) {
|
|
plm->setPlacement(static_cast<App::PropertyPlacement*>(prop)->getValue());
|
|
|
|
std::vector<Gui::SelectionObject> selection;
|
|
selection.reserve(sel.size());
|
|
std::transform(
|
|
sel.cbegin(),
|
|
sel.cend(),
|
|
std::back_inserter(selection),
|
|
[](App::DocumentObject* obj) { return Gui::SelectionObject(obj); }
|
|
);
|
|
|
|
plm->setPropertyName(QLatin1String("Placement"));
|
|
plm->setSelection(selection);
|
|
plm->bindObject();
|
|
plm->clearSelection();
|
|
}
|
|
}
|
|
Gui::Control().showDialog(plm);
|
|
}
|
|
|
|
bool StdCmdPlacement::isActive()
|
|
{
|
|
std::vector<App::DocumentObject*> sel = Gui::Selection().getObjectsOfType(
|
|
App::GeoFeature::getClassTypeId()
|
|
);
|
|
return !(sel.empty() || std::ranges::any_of(sel, [](auto obj) { return obj->isFreezed(); }));
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_TransformManip
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdTransformManip)
|
|
|
|
StdCmdTransformManip::StdCmdTransformManip()
|
|
: Command("Std_TransformManip")
|
|
{
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("Trans&form");
|
|
sToolTipText = QT_TR_NOOP("Transforms the selected object in the 3D view");
|
|
sStatusTip = sToolTipText;
|
|
sWhatsThis = "Std_TransformManip";
|
|
sPixmap = "Std_TransformManip";
|
|
}
|
|
|
|
void StdCmdTransformManip::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
if (getActiveGuiDocument()->getInEdit()) {
|
|
getActiveGuiDocument()->resetEdit();
|
|
}
|
|
std::vector<App::DocumentObject*> sel = Gui::Selection().getObjectsOfType(
|
|
App::GeoFeature::getClassTypeId()
|
|
);
|
|
Gui::ViewProvider* vp = Application::Instance->getViewProvider(sel.front());
|
|
// FIXME: Need a way to force 'Transform' edit mode
|
|
// #0000477: Proper interface for edit modes of view provider
|
|
if (vp) {
|
|
getActiveGuiDocument()->setEdit(vp, Gui::ViewProvider::Transform);
|
|
}
|
|
}
|
|
|
|
bool StdCmdTransformManip::isActive()
|
|
{
|
|
std::vector<App::DocumentObject*> sel = Gui::Selection().getObjectsOfType(
|
|
App::GeoFeature::getClassTypeId()
|
|
);
|
|
return (sel.size() == 1 && !sel.front()->isFreezed());
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Alignment
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdAlignment)
|
|
|
|
StdCmdAlignment::StdCmdAlignment()
|
|
: Command("Std_Alignment")
|
|
{
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("Ali&gn To…");
|
|
|
|
sToolTipText = QT_TR_NOOP("Aligns the selected objects");
|
|
sStatusTip = sToolTipText;
|
|
sWhatsThis = "Std_Alignment";
|
|
sPixmap = "Std_Alignment";
|
|
}
|
|
|
|
void StdCmdAlignment::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
std::vector<App::DocumentObject*> sel = Gui::Selection().getObjectsOfType(
|
|
App::GeoFeature::getClassTypeId()
|
|
);
|
|
ManualAlignment* align = ManualAlignment::instance();
|
|
QObject::connect(align, &ManualAlignment::emitCanceled, align, &QObject::deleteLater);
|
|
QObject::connect(align, &ManualAlignment::emitFinished, align, &QObject::deleteLater);
|
|
|
|
// Get the fixed and moving meshes
|
|
FixedGroup fixedGroup;
|
|
std::map<int, MovableGroup> groupMap;
|
|
fixedGroup.addView(sel[0]);
|
|
groupMap[0].addView(sel[1]);
|
|
|
|
// add the fixed group
|
|
align->setFixedGroup(fixedGroup);
|
|
|
|
// create the model of movable groups
|
|
MovableGroupModel model;
|
|
model.addGroups(groupMap);
|
|
align->setModel(model);
|
|
Base::Type style = Base::Type::fromName("Gui::CADNavigationStyle");
|
|
Base::Vector3d upDir(0, 1, 0), viewDir(0, 0, -1);
|
|
Gui::Document* doc = Application::Instance->activeDocument();
|
|
if (doc) {
|
|
auto mdi = qobject_cast<View3DInventor*>(doc->getActiveView());
|
|
if (mdi) {
|
|
View3DInventorViewer* viewer = mdi->getViewer();
|
|
SoCamera* camera = viewer->getSoRenderManager()->getCamera();
|
|
if (camera) {
|
|
SbVec3f up(0, 1, 0), dir(0, 0, -1);
|
|
camera->orientation.getValue().multVec(dir, dir);
|
|
viewDir.Set(dir[0], dir[1], dir[2]);
|
|
camera->orientation.getValue().multVec(up, up);
|
|
upDir.Set(up[0], up[1], up[2]);
|
|
}
|
|
style = viewer->navigationStyle()->getTypeId();
|
|
}
|
|
}
|
|
|
|
align->setMinPoints(1);
|
|
align->startAlignment(style);
|
|
align->setViewingDirections(viewDir, upDir, viewDir, upDir);
|
|
Gui::Selection().clearSelection();
|
|
}
|
|
|
|
bool StdCmdAlignment::isActive()
|
|
{
|
|
if (ManualAlignment::hasInstance()) {
|
|
return false;
|
|
}
|
|
return Gui::Selection().countObjectsOfType<App::GeoFeature>() == 2;
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Edit
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdEdit)
|
|
|
|
StdCmdEdit::StdCmdEdit()
|
|
: Command("Std_Edit")
|
|
{
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("Toggle &Edit Mode");
|
|
sToolTipText = QT_TR_NOOP("Toggles the selected object's edit mode");
|
|
sWhatsThis = "Std_Edit";
|
|
sStatusTip = sToolTipText;
|
|
sAccel = "";
|
|
sPixmap = "edit-edit";
|
|
eType = ForEdit;
|
|
}
|
|
|
|
void StdCmdEdit::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();
|
|
if (viewer->isEditingViewProvider()) {
|
|
doCommand(Command::Gui, "Gui.activeDocument().resetEdit()");
|
|
}
|
|
else {
|
|
if (!Selection().getCompleteSelection().empty()) {
|
|
SelectionSingleton::SelObj obj = Selection().getCompleteSelection()[0];
|
|
doCommand(Command::Gui, "Gui.activeDocument().setEdit(\"%s\",0)", obj.FeatName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool StdCmdEdit::isActive()
|
|
{
|
|
return (!Selection().getCompleteSelection().empty())
|
|
|| (Gui::Control().activeDialog() != nullptr);
|
|
}
|
|
|
|
//===========================================================================
|
|
// Std_Properties
|
|
//===========================================================================
|
|
DEF_STD_CMD_A(StdCmdProperties)
|
|
|
|
StdCmdProperties::StdCmdProperties()
|
|
: Command("Std_Properties")
|
|
{
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("Propert&ies");
|
|
sToolTipText = QT_TR_NOOP(
|
|
"Shows the property view, which displays the properties of the selected object."
|
|
);
|
|
sWhatsThis = "Std_Properties";
|
|
sStatusTip = sToolTipText;
|
|
sAccel = "Alt+Return";
|
|
sPixmap = "document-properties";
|
|
eType = Alter3DView;
|
|
}
|
|
|
|
void StdCmdProperties::activated(int iMsg)
|
|
{
|
|
Q_UNUSED(iMsg);
|
|
auto dw = Gui::DockWindowManager::instance();
|
|
if (auto propertyView = dw->getDockWindow("Property view")) {
|
|
dw->activate(propertyView);
|
|
return;
|
|
}
|
|
if (auto comboView = dw->getDockWindow("Model")) {
|
|
dw->activate(comboView);
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool StdCmdProperties::isActive()
|
|
{
|
|
return !Selection().getCompleteSelection().empty();
|
|
}
|
|
|
|
//======================================================================
|
|
// StdCmdExpression
|
|
//===========================================================================
|
|
class StdCmdExpression: public Gui::Command
|
|
{
|
|
public:
|
|
StdCmdExpression()
|
|
: Command("Std_Expressions")
|
|
{
|
|
sGroup = "Edit";
|
|
sMenuText = QT_TR_NOOP("Expression Actions");
|
|
sToolTipText = QT_TR_NOOP("Actions that apply to expressions");
|
|
sWhatsThis = "Std_Expressions";
|
|
sStatusTip = sToolTipText;
|
|
eType = ForEdit;
|
|
}
|
|
|
|
const char* className() const override
|
|
{
|
|
return "StdCmdExpression";
|
|
}
|
|
|
|
protected:
|
|
void activated(int iMsg) override
|
|
{
|
|
std::map<App::Document*, std::set<App::DocumentObject*>> objs;
|
|
switch (iMsg) {
|
|
case 0:
|
|
for (auto& sel : Selection().getCompleteSelection()) {
|
|
objs[sel.pObject->getDocument()].insert(sel.pObject);
|
|
}
|
|
break;
|
|
case 1:
|
|
if (App::GetApplication().getActiveDocument()) {
|
|
auto doc = App::GetApplication().getActiveDocument();
|
|
auto array = doc->getObjects();
|
|
auto& set = objs[doc];
|
|
set.insert(array.begin(), array.end());
|
|
}
|
|
break;
|
|
case 2:
|
|
for (auto doc : App::GetApplication().getDocuments()) {
|
|
auto& set = objs[doc];
|
|
auto array = doc->getObjects();
|
|
set.insert(array.begin(), array.end());
|
|
}
|
|
break;
|
|
case 3:
|
|
pasteExpressions();
|
|
break;
|
|
}
|
|
copyExpressions(objs);
|
|
}
|
|
|
|
Gui::Action* createAction() override
|
|
{
|
|
auto pcAction = new ActionGroup(this, getMainWindow());
|
|
pcAction->setDropDownMenu(true);
|
|
applyCommandData(this->className(), pcAction);
|
|
|
|
pcActionCopySel = pcAction->addAction(QObject::tr("Copy Selected"));
|
|
pcActionCopyActive = pcAction->addAction(QObject::tr("Copy Active Document"));
|
|
pcActionCopyAll = pcAction->addAction(QObject::tr("Copy All Documents"));
|
|
pcActionPaste = pcAction->addAction(QObject::tr("Paste"));
|
|
|
|
return pcAction;
|
|
}
|
|
|
|
void copyExpressions(const std::map<App::Document*, std::set<App::DocumentObject*>>& objs)
|
|
{
|
|
std::ostringstream ss;
|
|
std::vector<App::Property*> props;
|
|
for (auto& v : objs) {
|
|
for (auto obj : v.second) {
|
|
props.clear();
|
|
obj->getPropertyList(props);
|
|
for (auto prop : props) {
|
|
auto p = dynamic_cast<App::PropertyExpressionContainer*>(prop);
|
|
if (!p) {
|
|
continue;
|
|
}
|
|
for (auto& v : p->getExpressions()) {
|
|
ss << "##@@ " << v.first.toString() << ' ' << obj->getFullName() << '.'
|
|
<< p->getName() << " (" << obj->Label.getValue() << ')' << std::endl;
|
|
ss << "##@@";
|
|
if (!v.second->comment.empty()) {
|
|
if (v.second->comment[0] == '&'
|
|
|| v.second->comment.find('\n') != std::string::npos
|
|
|| v.second->comment.find('\r') != std::string::npos) {
|
|
std::string comment = v.second->comment;
|
|
boost::replace_all(comment, "&", "&");
|
|
boost::replace_all(comment, "\n", " ");
|
|
boost::replace_all(comment, "\r", " ");
|
|
ss << '&' << comment;
|
|
}
|
|
else {
|
|
ss << v.second->comment;
|
|
}
|
|
}
|
|
ss << std::endl << v.second->toString(true) << std::endl << std::endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
QApplication::clipboard()->setText(QString::fromUtf8(ss.str().c_str()));
|
|
}
|
|
|
|
void pasteExpressions()
|
|
{
|
|
std::map<
|
|
App::Document*,
|
|
std::map<App::PropertyExpressionContainer*, std::map<App::ObjectIdentifier, App::ExpressionPtr>>>
|
|
exprs;
|
|
|
|
bool failed = false;
|
|
std::string txt = QApplication::clipboard()->text().toUtf8().constData();
|
|
const char* tstart = txt.c_str();
|
|
const char* tend = tstart + txt.size();
|
|
|
|
static boost::regex rule("^##@@ ([^ ]+) (\\w+)#(\\w+)\\.(\\w+) [^\n]+\n##@@([^\n]*)\n");
|
|
boost::cmatch m;
|
|
if (!boost::regex_search(tstart, m, rule)) {
|
|
FC_WARN("No expression header found");
|
|
return;
|
|
}
|
|
boost::cmatch m2;
|
|
bool found = true;
|
|
for (; found; m = m2) {
|
|
found = boost::regex_search(m[0].second, tend, m2, rule);
|
|
|
|
auto pathName = m.str(1);
|
|
auto docName = m.str(2);
|
|
auto objName = m.str(3);
|
|
auto propName = m.str(4);
|
|
auto comment = m.str(5);
|
|
|
|
App::Document* doc = App::GetApplication().getDocument(docName.c_str());
|
|
if (!doc) {
|
|
FC_WARN("Cannot find document '" << docName << "'");
|
|
continue;
|
|
}
|
|
|
|
auto obj = doc->getObject(objName.c_str());
|
|
if (!obj) {
|
|
FC_WARN("Cannot find object '" << docName << '#' << objName << "'");
|
|
continue;
|
|
}
|
|
|
|
auto prop = dynamic_cast<App::PropertyExpressionContainer*>(
|
|
obj->getPropertyByName(propName.c_str())
|
|
);
|
|
if (!prop) {
|
|
FC_WARN("Invalid property '" << docName << '#' << objName << '.' << propName << "'");
|
|
continue;
|
|
}
|
|
|
|
size_t len = (found ? m2[0].first : tend) - m[0].second;
|
|
try {
|
|
App::ExpressionPtr expr(App::Expression::parse(obj, std::string(m[0].second, len)));
|
|
if (expr && !comment.empty()) {
|
|
if (comment[0] == '&') {
|
|
expr->comment = comment.c_str() + 1;
|
|
boost::replace_all(expr->comment, "&", "&");
|
|
boost::replace_all(expr->comment, " ", "\n");
|
|
boost::replace_all(expr->comment, " ", "\r");
|
|
}
|
|
else {
|
|
expr->comment = comment;
|
|
}
|
|
}
|
|
exprs[doc][prop][App::ObjectIdentifier::parse(obj, pathName)] = std::move(expr);
|
|
}
|
|
catch (Base::Exception& e) {
|
|
FC_ERR(e.what() << std::endl << m[0].str());
|
|
failed = true;
|
|
}
|
|
}
|
|
if (failed) {
|
|
QMessageBox::critical(
|
|
getMainWindow(),
|
|
QObject::tr("Expression Error"),
|
|
QObject::tr(
|
|
"Failed to parse some of the expressions.\n"
|
|
"Check the report view for more details."
|
|
)
|
|
);
|
|
return;
|
|
}
|
|
|
|
openCommand(QT_TRANSLATE_NOOP("Command", "Paste expressions"));
|
|
try {
|
|
for (auto& v : exprs) {
|
|
for (auto& v2 : v.second) {
|
|
auto& expressions = v2.second;
|
|
auto old = v2.first->getExpressions();
|
|
for (auto it = expressions.begin(), itNext = it; it != expressions.end();
|
|
it = itNext) {
|
|
++itNext;
|
|
auto iter = old.find(it->first);
|
|
if (iter != old.end() && it->second->isSame(*iter->second)) {
|
|
expressions.erase(it);
|
|
}
|
|
}
|
|
if (!expressions.empty()) {
|
|
v2.first->setExpressions(std::move(expressions));
|
|
}
|
|
}
|
|
}
|
|
commitCommand();
|
|
}
|
|
catch (const Base::Exception& e) {
|
|
abortCommand();
|
|
QMessageBox::critical(
|
|
getMainWindow(),
|
|
QObject::tr("Failed to Paste Expressions"),
|
|
QString::fromLatin1(e.what())
|
|
);
|
|
e.reportException();
|
|
}
|
|
}
|
|
|
|
bool isActive() override
|
|
{
|
|
if (!App::GetApplication().getActiveDocument()) {
|
|
pcActionCopyAll->setEnabled(false);
|
|
pcActionCopySel->setEnabled(false);
|
|
pcActionCopyActive->setEnabled(false);
|
|
pcActionPaste->setEnabled(false);
|
|
return true;
|
|
}
|
|
pcActionCopyActive->setEnabled(true);
|
|
pcActionCopyAll->setEnabled(true);
|
|
pcActionCopySel->setEnabled(Selection().hasSelection());
|
|
|
|
pcActionPaste->setEnabled(QApplication::clipboard()->text().startsWith(QLatin1String("##@@ ")));
|
|
return true;
|
|
}
|
|
|
|
QAction* pcActionCopyAll {nullptr};
|
|
QAction* pcActionCopySel {nullptr};
|
|
QAction* pcActionCopyActive {nullptr};
|
|
QAction* pcActionPaste {nullptr};
|
|
};
|
|
|
|
namespace Gui
|
|
{
|
|
|
|
void CreateDocCommands()
|
|
{
|
|
CommandManager& rcCmdMgr = Application::Instance->commandManager();
|
|
|
|
rcCmdMgr.addCommand(new StdCmdNew());
|
|
rcCmdMgr.addCommand(new StdCmdOpen());
|
|
rcCmdMgr.addCommand(new StdCmdImport());
|
|
rcCmdMgr.addCommand(new StdCmdExport());
|
|
rcCmdMgr.addCommand(new StdCmdMergeProjects());
|
|
rcCmdMgr.addCommand(new StdCmdDependencyGraph());
|
|
rcCmdMgr.addCommand(new StdCmdExportDependencyGraph());
|
|
|
|
rcCmdMgr.addCommand(new StdCmdSave());
|
|
rcCmdMgr.addCommand(new StdCmdSaveAs());
|
|
rcCmdMgr.addCommand(new StdCmdSaveCopy());
|
|
rcCmdMgr.addCommand(new StdCmdSaveAll());
|
|
rcCmdMgr.addCommand(new StdCmdRevert());
|
|
rcCmdMgr.addCommand(new StdCmdProjectInfo());
|
|
rcCmdMgr.addCommand(new StdCmdProjectUtil());
|
|
rcCmdMgr.addCommand(new StdCmdUndo());
|
|
rcCmdMgr.addCommand(new StdCmdRedo());
|
|
rcCmdMgr.addCommand(new StdCmdPrint());
|
|
rcCmdMgr.addCommand(new StdCmdPrintPreview());
|
|
rcCmdMgr.addCommand(new StdCmdPrintPdf());
|
|
rcCmdMgr.addCommand(new StdCmdQuit());
|
|
rcCmdMgr.addCommand(new StdCmdCut());
|
|
rcCmdMgr.addCommand(new StdCmdCopy());
|
|
rcCmdMgr.addCommand(new StdCmdPaste());
|
|
rcCmdMgr.addCommand(new StdCmdDuplicateSelection());
|
|
rcCmdMgr.addCommand(new StdCmdSelectAll());
|
|
rcCmdMgr.addCommand(new StdCmdDelete());
|
|
rcCmdMgr.addCommand(new StdCmdRefresh());
|
|
rcCmdMgr.addCommand(new StdCmdTransform());
|
|
rcCmdMgr.addCommand(new StdCmdPlacement());
|
|
rcCmdMgr.addCommand(new StdCmdTransformManip());
|
|
rcCmdMgr.addCommand(new StdCmdAlignment());
|
|
rcCmdMgr.addCommand(new StdCmdEdit());
|
|
rcCmdMgr.addCommand(new StdCmdProperties());
|
|
rcCmdMgr.addCommand(new StdCmdExpression());
|
|
}
|
|
|
|
} // namespace Gui
|