Files
create/src/Mod/Sketcher/Gui/Command.cpp
Zheng, Lei d4ac072306 Gui: support in-place editing
The link support means that an object can now appear in more than one
places, and even inside a document different from its own. This patch
adds support for in-place editing, meaning that the object can be edited
at correct place regardless where it is.

See [here](https://git.io/fjPIk) for more info about the relavent APIs.

This patch includes two example of modifications to support in-place
editing. One is the ViewProviderDragger, which simply adds the dragger
node to editing root node by calling
View3DInventorViewer::setupEditingRoot(dragger). The other much more
complex one is ViewProviderSketch which calls setupEditingRoot(0) to
transfer all its children node into editing root. ViewProviderSketch
also includes various modifications to command invocation, because we
can no longer assume the active document is the owner of the editing
object.

This patch also includes necessary modification of the 'Show' module to
support in-place editing.
2019-08-17 15:08:32 +02:00

905 lines
38 KiB
C++

/***************************************************************************
* Copyright (c) 2008 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 "PreCompiled.h"
#ifndef _PreComp_
# include <TopoDS_Shape.hxx>
# include <TopoDS_Face.hxx>
# include <TopoDS.hxx>
# include <BRepAdaptor_Surface.hxx>
# include <QApplication>
# include <QInputDialog>
# include <QMessageBox>
#endif
#include <App/DocumentObjectGroup.h>
#include <App/OriginFeature.h>
#include <Gui/Application.h>
#include <Gui/Document.h>
#include <Gui/Command.h>
#include <Gui/Control.h>
#include <Gui/MainWindow.h>
#include <Gui/DlgEditFileIncludeProptertyExternal.h>
#include <Gui/SelectionFilter.h>
#include <Mod/Sketcher/App/SketchObjectSF.h>
#include <Mod/Sketcher/App/SketchObject.h>
#include <Mod/Part/App/Attacher.h>
#include <Mod/Part/App/Part2DObject.h>
#include "SketchOrientationDialog.h"
#include "SketchMirrorDialog.h"
#include "ViewProviderSketch.h"
#include "TaskSketcherValidation.h"
#include "../App/Constraint.h"
using namespace std;
using namespace SketcherGui;
using namespace Part;
using namespace Attacher;
namespace SketcherGui {
class ExceptionWrongInput : public Base::Exception {
public:
ExceptionWrongInput()
: ErrMsg(QString())
{
}
//Pass untranslated strings, enclosed in QT_TR_NOOP()
ExceptionWrongInput(const char* ErrMsg){
this->ErrMsg = QObject::tr( ErrMsg );
this->setMessage(ErrMsg);
}
virtual ~ExceptionWrongInput() throw() {}
QString ErrMsg;
};
Attacher::eMapMode SuggestAutoMapMode(Attacher::SuggestResult::eSuggestResult* pMsgId = 0,
QString* message = 0,
std::vector<Attacher::eMapMode>* allmodes = 0){
//convert pointers into valid references, to avoid checking for null pointers everywhere
Attacher::SuggestResult::eSuggestResult buf;
if (pMsgId == 0)
pMsgId = &buf;
Attacher::SuggestResult::eSuggestResult &msg = *pMsgId;
QString buf2;
if (message == 0)
message = &buf2;
QString &msg_str = *message;
App::PropertyLinkSubList tmpSupport;
Gui::Selection().getAsPropertyLinkSubList(tmpSupport);
Attacher::SuggestResult sugr;
AttachEngine3D eng;
eng.setUp(tmpSupport);
eng.suggestMapModes(sugr);
if (allmodes)
*allmodes = sugr.allApplicableModes;
msg = sugr.message;
switch(msg){
case Attacher::SuggestResult::srOK:
break;
case Attacher::SuggestResult::srNoModesFit:
msg_str = QObject::tr("There are no modes that accept the selected set of subelements");
break;
case Attacher::SuggestResult::srLinkBroken:
msg_str = QObject::tr("Broken link to support subelements");
break;
case Attacher::SuggestResult::srUnexpectedError:
msg_str = QObject::tr("Unexpected error");
break;
case Attacher::SuggestResult::srIncompatibleGeometry:
if(tmpSupport.getSubValues()[0].substr(0,4) == std::string("Face"))
msg_str = QObject::tr("Face is non-planar");
else
msg_str = QObject::tr("Selected shapes are of wrong form (e.g., a curved edge where a straight one is needed)");
break;
default:
msg_str = QObject::tr("Unexpected error");
assert(0/*no message for eSuggestResult enum item*/);
}
return sugr.bestFitMode;
}
} //namespace SketcherGui
/* Sketch commands =======================================================*/
DEF_STD_CMD_A(CmdSketcherNewSketch);
CmdSketcherNewSketch::CmdSketcherNewSketch()
:Command("Sketcher_NewSketch")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Create sketch");
sToolTipText = QT_TR_NOOP("Create a new sketch");
sWhatsThis = "Sketcher_NewSketch";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_NewSketch";
}
void CmdSketcherNewSketch::activated(int iMsg)
{
Q_UNUSED(iMsg);
Attacher::eMapMode mapmode = Attacher::mmDeactivated;
bool bAttach = false;
if (Gui::Selection().hasSelection()){
Attacher::SuggestResult::eSuggestResult msgid = Attacher::SuggestResult::srOK;
QString msg_str;
std::vector<Attacher::eMapMode> validModes;
mapmode = SuggestAutoMapMode(&msgid, &msg_str, &validModes);
if (msgid == Attacher::SuggestResult::srOK)
bAttach = true;
if (msgid != Attacher::SuggestResult::srOK && msgid != Attacher::SuggestResult::srNoModesFit){
QMessageBox::warning(Gui::getMainWindow(),
QObject::tr("Sketch mapping"),
QObject::tr("Can't map the sketch to selected object. %1.").arg(msg_str));
return;
}
if (validModes.size() > 1){
validModes.insert(validModes.begin(), Attacher::mmDeactivated);
bool ok;
QStringList items;
items.push_back(QObject::tr("Don't attach"));
int iSugg = 0;//index of the auto-suggested mode in the list of valid modes
for (size_t i = 0 ; i < validModes.size() ; ++i){
items.push_back(QString::fromLatin1(AttachEngine::getModeName(validModes[i]).c_str()));
if (validModes[i] == mapmode)
iSugg = items.size()-1;
}
QString text = QInputDialog::getItem(Gui::getMainWindow(),
qApp->translate("Sketcher_NewSketch", "Sketch attachment"),
qApp->translate("Sketcher_NewSketch", "Select the method to attach this sketch to selected object"),
items, iSugg, false, &ok);
if (!ok) return;
int index = items.indexOf(text);
if (index == 0){
bAttach = false;
mapmode = Attacher::mmDeactivated;
} else {
bAttach = true;
mapmode = validModes[index-1];
}
}
}
if (bAttach) {
std::vector<Gui::SelectionObject> objects = Gui::Selection().getSelectionEx();
//assert (objects.size() == 1); //should have been filtered out by SuggestAutoMapMode
//Gui::SelectionObject &sel_support = objects[0];
App::PropertyLinkSubList support;
Gui::Selection().getAsPropertyLinkSubList(support);
std::string supportString = support.getPyReprString();
// create Sketch on Face
std::string FeatName = getUniqueObjectName("Sketch");
openCommand("Create a Sketch on Face");
doCommand(Doc,"App.activeDocument().addObject('Sketcher::SketchObject','%s')",FeatName.c_str());
if (mapmode < Attacher::mmDummy_NumberOfModes)
doCommand(Gui,"App.activeDocument().%s.MapMode = \"%s\"",FeatName.c_str(),AttachEngine::getModeName(mapmode).c_str());
else
assert(0 /* mapmode index out of range */);
doCommand(Gui,"App.activeDocument().%s.Support = %s",FeatName.c_str(),supportString.c_str());
doCommand(Gui,"App.activeDocument().recompute()"); // recompute the sketch placement based on its support
doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str());
Part::Feature *part = static_cast<Part::Feature*>(support.getValue());//if multi-part support, this will return 0
if (part){
App::DocumentObjectGroup* grp = part->getGroup();
if (grp) {
doCommand(Doc,"App.activeDocument().%s.addObject(App.activeDocument().%s)"
,grp->getNameInDocument(),FeatName.c_str());
}
}
}
else {
// ask user for orientation
SketchOrientationDialog Dlg;
if (Dlg.exec() != QDialog::Accepted)
return; // canceled
Base::Vector3d p = Dlg.Pos.getPosition();
Base::Rotation r = Dlg.Pos.getRotation();
// do the right view direction
std::string camstring;
switch(Dlg.DirType){
case 0:
camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA \\n position 0 0 87 \\n orientation 0 0 1 0 \\n nearDistance -112.88701 \\n farDistance 287.28702 \\n aspectRatio 1 \\n focalDistance 87 \\n height 143.52005 }";
break;
case 1:
camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA \\n position 0 0 -87 \\n orientation -1 0 0 3.1415927 \\n nearDistance -112.88701 \\n farDistance 287.28702 \\n aspectRatio 1 \\n focalDistance 87 \\n height 143.52005 }";
break;
case 2:
camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n position 0 -87 0 \\n orientation -1 0 0 4.712389\\n nearDistance -112.88701\\n farDistance 287.28702\\n aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}";
break;
case 3:
camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n position 0 87 0 \\n orientation 0 0.70710683 0.70710683 3.1415927\\n nearDistance -112.88701\\n farDistance 287.28702\\n aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}";
break;
case 4:
camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n position 87 0 0 \\n orientation 0.57735026 0.57735026 0.57735026 2.0943952 \\n nearDistance -112.887\\n farDistance 287.28699\\n aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}";
break;
case 5:
camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n position -87 0 0 \\n orientation -0.57735026 0.57735026 0.57735026 4.1887903 \\n nearDistance -112.887\\n farDistance 287.28699\\n aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}";
break;
}
std::string FeatName = getUniqueObjectName("Sketch");
openCommand("Create a new Sketch");
doCommand(Doc,"App.activeDocument().addObject('Sketcher::SketchObject','%s')",FeatName.c_str());
doCommand(Doc,"App.activeDocument().%s.Placement = App.Placement(App.Vector(%f,%f,%f),App.Rotation(%f,%f,%f,%f))",FeatName.c_str(),p.x,p.y,p.z,r[0],r[1],r[2],r[3]);
doCommand(Doc,"App.activeDocument().%s.MapMode = \"%s\"",FeatName.c_str(),AttachEngine::getModeName(Attacher::mmDeactivated).c_str());
doCommand(Gui,"Gui.activeDocument().activeView().setCamera('%s')",camstring.c_str());
doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str());
}
}
bool CmdSketcherNewSketch::isActive(void)
{
if (getActiveGuiDocument())
return true;
else
return false;
}
DEF_STD_CMD_A(CmdSketcherEditSketch);
CmdSketcherEditSketch::CmdSketcherEditSketch()
:Command("Sketcher_EditSketch")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Edit sketch");
sToolTipText = QT_TR_NOOP("Edit the selected sketch");
sWhatsThis = "Sketcher_EditSketch";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_EditSketch";
}
void CmdSketcherEditSketch::activated(int iMsg)
{
Q_UNUSED(iMsg);
Gui::SelectionFilter SketchFilter("SELECT Sketcher::SketchObject COUNT 1");
if (SketchFilter.match()) {
Sketcher::SketchObject *Sketch = static_cast<Sketcher::SketchObject*>(SketchFilter.Result[0][0].getObject());
openCommand("Edit Sketch");
doCommand(Gui,"Gui.activeDocument().setEdit('%s')",Sketch->getNameInDocument());
}
}
bool CmdSketcherEditSketch::isActive(void)
{
return Gui::Selection().countObjectsOfType(Sketcher::SketchObject::getClassTypeId()) == 1;
}
DEF_STD_CMD_A(CmdSketcherLeaveSketch);
CmdSketcherLeaveSketch::CmdSketcherLeaveSketch()
: Command("Sketcher_LeaveSketch")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Leave sketch");
sToolTipText = QT_TR_NOOP("Close the editing of the sketch");
sWhatsThis = "Sketcher_LeaveSketch";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_LeaveSketch";
eType = 0;
}
void CmdSketcherLeaveSketch::activated(int iMsg)
{
Q_UNUSED(iMsg);
Gui::Document *doc = getActiveGuiDocument();
if (doc) {
// checks if a Sketch Viewprovider is in Edit and is in no special mode
SketcherGui::ViewProviderSketch* vp = dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
if (vp && vp->getSketchMode() != ViewProviderSketch::STATUS_NONE)
vp->purgeHandler();
}
openCommand("Sketch changed");
doCommand(Gui,"Gui.activeDocument().resetEdit()");
doCommand(Doc,"App.ActiveDocument.recompute()");
commitCommand();
}
bool CmdSketcherLeaveSketch::isActive(void)
{
Gui::Document *doc = getActiveGuiDocument();
if (doc) {
// checks if a Sketch Viewprovider is in Edit and is in no special mode
SketcherGui::ViewProviderSketch* vp = dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
if (vp /*&& vp->getSketchMode() == ViewProviderSketch::STATUS_NONE*/)
return true;
}
return false;
}
DEF_STD_CMD_A(CmdSketcherReorientSketch);
CmdSketcherReorientSketch::CmdSketcherReorientSketch()
:Command("Sketcher_ReorientSketch")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Reorient sketch...");
sToolTipText = QT_TR_NOOP("Reorient the selected sketch");
sWhatsThis = "Sketcher_ReorientSketch";
sStatusTip = sToolTipText;
}
void CmdSketcherReorientSketch::activated(int iMsg)
{
Q_UNUSED(iMsg);
Sketcher::SketchObject* sketch = Gui::Selection().getObjectsOfType<Sketcher::SketchObject>().front();
if (sketch->Support.getValue()) {
int ret = QMessageBox::question(Gui::getMainWindow(),
qApp->translate("Sketcher_ReorientSketch","Sketch has support"),
qApp->translate("Sketcher_ReorientSketch","Sketch with a support face cannot be reoriented.\n"
"Do you want to detach it from the support?"),
QMessageBox::Yes|QMessageBox::No);
if (ret == QMessageBox::No)
return;
sketch->Support.setValue(0);
}
// ask user for orientation
SketchOrientationDialog Dlg;
if (Dlg.exec() != QDialog::Accepted)
return; // canceled
Base::Vector3d p = Dlg.Pos.getPosition();
Base::Rotation r = Dlg.Pos.getRotation();
// do the right view direction
std::string camstring;
switch(Dlg.DirType){
case 0:
camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA \\n "
"position 0 0 87 \\n orientation 0 0 1 0 \\n nearDistance -112.88701 \\n farDistance 287.28702 \\n "
"aspectRatio 1 \\n focalDistance 87 \\n height 143.52005 }";
break;
case 1:
camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA \\n "
"position 0 0 -87 \\n orientation -1 0 0 3.1415927 \\n nearDistance -112.88701 \\n farDistance 287.28702 \\n "
"aspectRatio 1 \\n focalDistance 87 \\n height 143.52005 }";
break;
case 2:
camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n "
"position 0 -87 0 \\n orientation -1 0 0 4.712389\\n nearDistance -112.88701\\n farDistance 287.28702\\n "
"aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}";
break;
case 3:
camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n "
"position 0 87 0 \\n orientation 0 0.70710683 0.70710683 3.1415927\\n nearDistance -112.88701\\n farDistance 287.28702\\n "
"aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}";
break;
case 4:
camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n "
"position 87 0 0 \\n orientation 0.57735026 0.57735026 0.57735026 2.0943952 \\n nearDistance -112.887\\n farDistance 287.28699\\n "
"aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}";
break;
case 5:
camstring = "#Inventor V2.1 ascii \\n OrthographicCamera {\\n viewportMapping ADJUST_CAMERA\\n "
"position -87 0 0 \\n orientation -0.57735026 0.57735026 0.57735026 4.1887903 \\n nearDistance -112.887\\n farDistance 287.28699\\n "
"aspectRatio 1\\n focalDistance 87\\n height 143.52005\\n\\n}";
break;
}
openCommand("Reorient Sketch");
FCMD_OBJ_CMD2("Placement = App.Placement(App.Vector(%f,%f,%f),App.Rotation(%f,%f,%f,%f))"
,sketch,p.x,p.y,p.z,r[0],r[1],r[2],r[3]);
doCommand(Gui,"Gui.ActiveDocument.setEdit('%s')",sketch->getNameInDocument());
}
bool CmdSketcherReorientSketch::isActive(void)
{
return Gui::Selection().countObjectsOfType
(Sketcher::SketchObject::getClassTypeId()) == 1;
}
DEF_STD_CMD_A(CmdSketcherMapSketch);
CmdSketcherMapSketch::CmdSketcherMapSketch()
: Command("Sketcher_MapSketch")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Map sketch to face...");
sToolTipText = QT_TR_NOOP("Map a sketch to a face");
sWhatsThis = "Sketcher_MapSketch";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_MapSketch";
}
void CmdSketcherMapSketch::activated(int iMsg)
{
Q_UNUSED(iMsg);
QString msg_str;
try{
Attacher::eMapMode suggMapMode;
std::vector<Attacher::eMapMode> validModes;
//check that selection is valid for at least some mapping mode.
Attacher::SuggestResult::eSuggestResult msgid = Attacher::SuggestResult::srOK;
suggMapMode = SuggestAutoMapMode(&msgid, &msg_str, &validModes);
App::Document* doc = App::GetApplication().getActiveDocument();
std::vector<App::DocumentObject*> sketches = doc->getObjectsOfType(Part::Part2DObject::getClassTypeId());
if (sketches.empty()) {
QMessageBox::warning(Gui::getMainWindow(),
qApp->translate("Sketcher_MapSketch", "No sketch found"),
qApp->translate("Sketcher_MapSketch", "The document doesn't have a sketch"));
return;
}
bool ok;
QStringList items;
for (std::vector<App::DocumentObject*>::iterator it = sketches.begin(); it != sketches.end(); ++it)
items.push_back(QString::fromUtf8((*it)->Label.getValue()));
QString text = QInputDialog::getItem(Gui::getMainWindow(),
qApp->translate("Sketcher_MapSketch", "Select sketch"),
qApp->translate("Sketcher_MapSketch", "Select a sketch from the list"),
items, 0, false, &ok);
if (!ok) return;
int index = items.indexOf(text);
Part2DObject &sketch = *(static_cast<Part2DObject*>(sketches[index]));
// check circular dependency
std::vector<Gui::SelectionObject> selobjs = Gui::Selection().getSelectionEx();
for (size_t i = 0 ; i < selobjs.size() ; ++i){
App::DocumentObject* part = static_cast<Part::Feature*>(selobjs[i].getObject());
if (!part) {
assert(0);
throw Base::ValueError("Unexpected null pointer in CmdSketcherMapSketch::activated");
}
std::vector<App::DocumentObject*> input = part->getOutList();
if (std::find(input.begin(), input.end(), &sketch) != input.end()) {
throw ExceptionWrongInput(QT_TR_NOOP("Some of the selected objects depend on the sketch to be mapped. Circular dependencies are not allowed!"));
}
}
//Ask for a new mode.
//outline:
// * find out the modes that are compatible with selection.
// * Test if current mode is OK.
// * fill in the dialog
// * execute the dialog
// * collect dialog result
// * action
bool bAttach = true;
bool bCurIncompatible = false;
// * find out the modes that are compatible with selection.
eMapMode curMapMode = eMapMode(sketch.MapMode.getValue());
// * Test if current mode is OK.
if (std::find(validModes.begin(), validModes.end(), curMapMode) == validModes.end())
bCurIncompatible = true;
// * fill in the dialog
validModes.insert(validModes.begin(), Attacher::mmDeactivated);
if(bCurIncompatible)
validModes.push_back(curMapMode);
//bool ok; //already defined
//QStringList items; //already defined
items.clear();
items.push_back(QObject::tr("Don't attach"));
int iSugg = 0;//index of the auto-suggested mode in the list of valid modes
int iCurr = 0;//index of current mode in the list of valid modes
for (size_t i = 0 ; i < validModes.size() ; ++i){
items.push_back(QString::fromLatin1(AttachEngine::getModeName(validModes[i]).c_str()));
if (validModes[i] == curMapMode) {
iCurr = items.size() - 1;
items.back().append(bCurIncompatible?
qApp->translate("Sketcher_MapSketch"," (incompatible with selection)")
:
qApp->translate("Sketcher_MapSketch"," (current)") );
}
if (validModes[i] == suggMapMode){
iSugg = items.size() - 1;
if(iSugg == 1){
iSugg = 0;//redirect deactivate to detach
} else {
items.back().append(qApp->translate("Sketcher_MapSketch"," (suggested)"));
}
}
}
// * execute the dialog
text = QInputDialog::getItem(Gui::getMainWindow()
,qApp->translate("Sketcher_MapSketch", "Sketch attachment")
,bCurIncompatible?
qApp->translate("Sketcher_MapSketch", "Current attachment mode is incompatible with the new selection. Select the method to attach this sketch to selected objects.")
:
qApp->translate("Sketcher_MapSketch", "Select the method to attach this sketch to selected objects.")
,items
, bCurIncompatible ? iSugg : iCurr
, false
, &ok);
// * collect dialog result
if (!ok) return;
index = items.indexOf(text);
if (index == 0){
bAttach = false;
suggMapMode = Attacher::mmDeactivated;
} else {
bAttach = true;
suggMapMode = validModes[index-1];
}
// * action
if (bAttach) {
App::PropertyLinkSubList support;
Gui::Selection().getAsPropertyLinkSubList(support);
std::string supportString = support.getPyReprString();
openCommand("Attach Sketch");
FCMD_OBJ_CMD2("MapMode = \"%s\"",&sketch,AttachEngine::getModeName(suggMapMode).c_str());
FCMD_OBJ_CMD2("Support = %s",&sketch,supportString.c_str());
commitCommand();
} else {
openCommand("Detach Sketch");
FCMD_OBJ_CMD2("MapMode = \"%s\"",&sketch,AttachEngine::getModeName(suggMapMode).c_str());
FCMD_OBJ_CMD2("Support = None",&sketch);
commitCommand();
}
} catch (ExceptionWrongInput &e) {
QMessageBox::warning(Gui::getMainWindow(),
qApp->translate("Sketcher_MapSketch", "Map sketch"),
qApp->translate("Sketcher_MapSketch", "Can't map a sketch to support:\n"
"%1").arg(e.ErrMsg.length() ? e.ErrMsg : msg_str));
}
}
bool CmdSketcherMapSketch::isActive(void)
{
return getActiveGuiDocument() != 0;
}
DEF_STD_CMD_A(CmdSketcherViewSketch);
CmdSketcherViewSketch::CmdSketcherViewSketch()
: Command("Sketcher_ViewSketch")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("View sketch");
sToolTipText = QT_TR_NOOP("View sketch perpendicular to sketch plane");
sWhatsThis = "Sketcher_ViewSketch";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_ViewSketch";
eType = 0;
}
void CmdSketcherViewSketch::activated(int iMsg)
{
Q_UNUSED(iMsg);
Gui::Document *doc = getActiveGuiDocument();
SketcherGui::ViewProviderSketch* vp = dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
if (vp) {
runCommand(Gui,"Gui.ActiveDocument.ActiveView.setCameraOrientation("
"App.Placement(Gui.editDocument().EditingTransform).Rotation.Q)");
}
}
bool CmdSketcherViewSketch::isActive(void)
{
Gui::Document *doc = getActiveGuiDocument();
if (doc) {
// checks if a Sketch Viewprovider is in Edit and is in no special mode
SketcherGui::ViewProviderSketch* vp = dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
if (vp /*&& vp->getSketchMode() == ViewProviderSketch::STATUS_NONE*/)
return true;
}
return false;
}
DEF_STD_CMD_A(CmdSketcherValidateSketch);
CmdSketcherValidateSketch::CmdSketcherValidateSketch()
: Command("Sketcher_ValidateSketch")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Validate sketch...");
sToolTipText = QT_TR_NOOP("Validate sketch");
sWhatsThis = "Sketcher_ValidateSketch";
sStatusTip = sToolTipText;
eType = 0;
}
void CmdSketcherValidateSketch::activated(int iMsg)
{
Q_UNUSED(iMsg);
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx(0, Sketcher::SketchObject::getClassTypeId());
if (selection.size() != 1) {
QMessageBox::warning(Gui::getMainWindow(),
qApp->translate("CmdSketcherValidateSketch", "Wrong selection"),
qApp->translate("CmdSketcherValidateSketch", "Select one sketch, please."));
return;
}
Sketcher::SketchObject* Obj = static_cast<Sketcher::SketchObject*>(selection[0].getObject());
Gui::Control().showDialog(new TaskSketcherValidation(Obj));
}
bool CmdSketcherValidateSketch::isActive(void)
{
return (hasActiveDocument() && !Gui::Control().activeDialog());
}
DEF_STD_CMD_A(CmdSketcherMirrorSketch);
CmdSketcherMirrorSketch::CmdSketcherMirrorSketch()
: Command("Sketcher_MirrorSketch")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Mirror sketch");
sToolTipText = QT_TR_NOOP("Mirror sketch");
sWhatsThis = "Sketcher_MirrorSketch";
sStatusTip = sToolTipText;
eType = 0;
sPixmap = "Sketcher_MirrorSketch";
}
void CmdSketcherMirrorSketch::activated(int iMsg)
{
Q_UNUSED(iMsg);
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx(0, Sketcher::SketchObject::getClassTypeId());
if (selection.size() < 1) {
QMessageBox::warning(Gui::getMainWindow(),
qApp->translate("CmdSketcherMirrorSketch", "Wrong selection"),
qApp->translate("CmdSketcherMirrorSketch", "Select one or more sketches, please."));
return;
}
// Ask the user which kind of mirroring he wants
SketchMirrorDialog * smd = new SketchMirrorDialog();
int refgeoid=-1;
Sketcher::PointPos refposid=Sketcher::none;
if( smd->exec() == QDialog::Accepted ){
refgeoid=smd->RefGeoid;
refposid=smd->RefPosid;
delete smd;
}
else {
delete smd;
return;
}
App::Document* doc = App::GetApplication().getActiveDocument();
openCommand("Create a mirror Sketch for each sketch");
for (std::vector<Gui::SelectionObject>::const_iterator it=selection.begin(); it != selection.end(); ++it) {
// create Sketch
std::string FeatName = getUniqueObjectName("MirroredSketch");
doCommand(Doc,"App.activeDocument().addObject('Sketcher::SketchObject','%s')",FeatName.c_str());
Sketcher::SketchObject* mirrorsketch = static_cast<Sketcher::SketchObject*>(doc->getObject(FeatName.c_str()));
const Sketcher::SketchObject* Obj = static_cast<const Sketcher::SketchObject*>((*it).getObject());
Base::Placement pl = Obj->Placement.getValue();
Base::Vector3d p = pl.getPosition();
Base::Rotation r = pl.getRotation();
doCommand(Doc,"App.activeDocument().%s.Placement = App.Placement(App.Vector(%f,%f,%f),App.Rotation(%f,%f,%f,%f))",
FeatName.c_str(),
p.x,p.y,p.z,r[0],r[1],r[2],r[3]);
Sketcher::SketchObject* tempsketch = new Sketcher::SketchObject();
int addedGeometries=tempsketch->addGeometry(Obj->getInternalGeometry());
int addedConstraints=tempsketch->addConstraints(Obj->Constraints.getValues());
std::vector<int> geoIdList;
for(int i=0;i<=addedGeometries;i++)
geoIdList.push_back(i);
tempsketch->addSymmetric(geoIdList, refgeoid, refposid);
std::vector<Part::Geometry *> tempgeo = tempsketch->getInternalGeometry();
std::vector<Sketcher::Constraint *> tempconstr = tempsketch->Constraints.getValues();
std::vector<Part::Geometry *> mirrorgeo (tempgeo.begin()+addedGeometries+1,tempgeo.end());
std::vector<Sketcher::Constraint *> mirrorconstr (tempconstr.begin()+addedConstraints+1,tempconstr.end());
for (std::vector<Sketcher::Constraint *>::const_iterator itc=mirrorconstr.begin(); itc != mirrorconstr.end(); ++itc) {
if ((*itc)->First!=Sketcher::Constraint::GeoUndef || (*itc)->First==Sketcher::GeoEnum::HAxis || (*itc)->First==Sketcher::GeoEnum::VAxis) // not x, y axes or origin
(*itc)->First-=(addedGeometries+1);
if ((*itc)->Second!=Sketcher::Constraint::GeoUndef || (*itc)->Second==Sketcher::GeoEnum::HAxis || (*itc)->Second==Sketcher::GeoEnum::VAxis) // not x, y axes or origin
(*itc)->Second-=(addedGeometries+1);
if ((*itc)->Third!=Sketcher::Constraint::GeoUndef || (*itc)->Third==Sketcher::GeoEnum::HAxis || (*itc)->Third==Sketcher::GeoEnum::VAxis) // not x, y axes or origin
(*itc)->Third-=(addedGeometries+1);
}
mirrorsketch->addGeometry(mirrorgeo);
mirrorsketch->addConstraints(mirrorconstr);
delete tempsketch;
}
doCommand(Gui,"App.activeDocument().recompute()");
}
bool CmdSketcherMirrorSketch::isActive(void)
{
return (hasActiveDocument() && !Gui::Control().activeDialog());
}
DEF_STD_CMD_A(CmdSketcherMergeSketches);
CmdSketcherMergeSketches::CmdSketcherMergeSketches()
: Command("Sketcher_MergeSketches")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("Merge sketches");
sToolTipText = QT_TR_NOOP("Merge sketches");
sWhatsThis = "Sketcher_MergeSketches";
sStatusTip = sToolTipText;
eType = 0;
sPixmap = "Sketcher_MergeSketch";
}
void CmdSketcherMergeSketches::activated(int iMsg)
{
Q_UNUSED(iMsg);
std::vector<Gui::SelectionObject> selection = getSelection().getSelectionEx(0, Sketcher::SketchObject::getClassTypeId());
if (selection.size() < 2) {
QMessageBox::warning(Gui::getMainWindow(),
qApp->translate("CmdSketcherMergeSketches", "Wrong selection"),
qApp->translate("CmdSketcherMergeSketches", "Select at least two sketches, please."));
return;
}
App::Document* doc = App::GetApplication().getActiveDocument();
// create Sketch
std::string FeatName = getUniqueObjectName("Sketch");
openCommand("Create a merge Sketch");
doCommand(Doc,"App.activeDocument().addObject('Sketcher::SketchObject','%s')",FeatName.c_str());
Sketcher::SketchObject* mergesketch = static_cast<Sketcher::SketchObject*>(doc->getObject(FeatName.c_str()));
int baseGeometry=0;
int baseConstraints=0;
for (std::vector<Gui::SelectionObject>::const_iterator it=selection.begin(); it != selection.end(); ++it) {
const Sketcher::SketchObject* Obj = static_cast<const Sketcher::SketchObject*>((*it).getObject());
int addedGeometries=mergesketch->addGeometry(Obj->getInternalGeometry());
int addedConstraints=mergesketch->addCopyOfConstraints(*Obj);
for (int i=0; i<=(addedConstraints-baseConstraints); i++){
Sketcher::Constraint * constraint= mergesketch->Constraints.getValues()[i+baseConstraints];
if (constraint->First!=Sketcher::Constraint::GeoUndef &&
constraint->First!=Sketcher::GeoEnum::HAxis &&
constraint->First!=Sketcher::GeoEnum::VAxis) // not x, y axes or origin
constraint->First+=baseGeometry;
if (constraint->Second!=Sketcher::Constraint::GeoUndef &&
constraint->Second!=Sketcher::GeoEnum::HAxis &&
constraint->Second!=Sketcher::GeoEnum::VAxis) // not x, y axes or origin
constraint->Second+=baseGeometry;
if (constraint->Third!=Sketcher::Constraint::GeoUndef &&
constraint->Third!=Sketcher::GeoEnum::HAxis &&
constraint->Third!=Sketcher::GeoEnum::VAxis) // not x, y axes or origin
constraint->Third+=baseGeometry;
}
baseGeometry=addedGeometries+1;
baseConstraints=addedConstraints+1;
}
// apply the placement of the first sketch in the list (#0002434)
doCommand(Doc,"App.activeDocument().ActiveObject.Placement=App.activeDocument().%s.Placement"
,selection.front().getFeatName());
doCommand(Doc,"App.activeDocument().recompute()");
}
bool CmdSketcherMergeSketches::isActive(void)
{
return (hasActiveDocument() && !Gui::Control().activeDialog());
}
// Acknowledgement of idea and original python macro goes to SpritKopf:
// https://github.com/Spritkopf/freecad-macros/blob/master/clip-sketch/clip_sketch.FCMacro
// https://forum.freecadweb.org/viewtopic.php?p=231481#p231085
DEF_STD_CMD_A(CmdSketcherViewSection);
CmdSketcherViewSection::CmdSketcherViewSection()
: Command("Sketcher_ViewSection")
{
sAppModule = "Sketcher";
sGroup = QT_TR_NOOP("Sketcher");
sMenuText = QT_TR_NOOP("View section");
sToolTipText = QT_TR_NOOP("Switches between section and full view");
sWhatsThis = "Sketcher_ViewSection";
sStatusTip = sToolTipText;
sPixmap = "Sketcher_ViewSection";
eType = 0;
}
void CmdSketcherViewSection::activated(int iMsg)
{
Q_UNUSED(iMsg);
doCommand(Doc,"ActiveSketch.ViewObject.TempoVis.sketchClipPlane(ActiveSketch)");
}
bool CmdSketcherViewSection::isActive(void)
{
Gui::Document *doc = getActiveGuiDocument();
if (doc) {
// checks if a Sketch Viewprovider is in Edit and is in no special mode
SketcherGui::ViewProviderSketch* vp = dynamic_cast<SketcherGui::ViewProviderSketch*>(doc->getInEdit());
if (vp /*&& vp->getSketchMode() == ViewProviderSketch::STATUS_NONE*/)
return true;
}
return false;
}
void CreateSketcherCommands(void)
{
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
rcCmdMgr.addCommand(new CmdSketcherNewSketch());
rcCmdMgr.addCommand(new CmdSketcherEditSketch());
rcCmdMgr.addCommand(new CmdSketcherLeaveSketch());
rcCmdMgr.addCommand(new CmdSketcherReorientSketch());
rcCmdMgr.addCommand(new CmdSketcherMapSketch());
rcCmdMgr.addCommand(new CmdSketcherViewSketch());
rcCmdMgr.addCommand(new CmdSketcherValidateSketch());
rcCmdMgr.addCommand(new CmdSketcherMirrorSketch());
rcCmdMgr.addCommand(new CmdSketcherMergeSketches());
rcCmdMgr.addCommand(new CmdSketcherViewSection());
}