Gui: Command API changes
* invoke(), distinguish between command triggering source. Also add support of auto transaction using App::AutoTransaction. Now all command will support undo/redo by default. * setupCheckable(), a helper function to Improve support of PythonGroupCommand * getObjectCmd(), helper function to output Python command to refer to an object without ambiguity. Because with introduction of external linking, an object can no longer be safely referred through the current active document. * Support auto MacroManager command logger. For commands that does not log any output to MacroManager, a log entry of 'Gui.runCommand()' will be auto generated. * Support linked object in copyVisual() * Modified do/runCommand() to print calling file and line number. * Add various helper macros for run command involving a document or object.
This commit is contained in:
@@ -37,6 +37,7 @@
|
||||
# include <QToolButton>
|
||||
#endif
|
||||
|
||||
#include <Base/Tools.h>
|
||||
#include "Action.h"
|
||||
#include "Application.h"
|
||||
#include "Command.h"
|
||||
@@ -92,7 +93,7 @@ void Action::addTo(QWidget *w)
|
||||
*/
|
||||
void Action::onActivated ()
|
||||
{
|
||||
_pcCmd->invoke(0);
|
||||
_pcCmd->invoke(0,Command::TriggerAction);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,11 +101,13 @@ void Action::onActivated ()
|
||||
*/
|
||||
void Action::onToggled(bool b)
|
||||
{
|
||||
_pcCmd->invoke( b ? 1 : 0 );
|
||||
_pcCmd->invoke( b ? 1 : 0 , Command::TriggerAction);
|
||||
}
|
||||
|
||||
void Action::setCheckable(bool b)
|
||||
{
|
||||
if(b == _action->isCheckable())
|
||||
return;
|
||||
_action->setCheckable(b);
|
||||
if (b) {
|
||||
disconnect(_action, SIGNAL(triggered(bool)), this, SLOT(onActivated()));
|
||||
@@ -116,9 +119,14 @@ void Action::setCheckable(bool b)
|
||||
}
|
||||
}
|
||||
|
||||
void Action::setChecked(bool b)
|
||||
void Action::setChecked(bool b, bool no_signal)
|
||||
{
|
||||
bool blocked;
|
||||
if(no_signal)
|
||||
blocked = _action->blockSignals(true);
|
||||
_action->setChecked(b);
|
||||
if(no_signal)
|
||||
_action->blockSignals(blocked);
|
||||
}
|
||||
|
||||
bool Action::isChecked() const
|
||||
@@ -154,6 +162,11 @@ void Action::setIcon (const QIcon & icon)
|
||||
_action->setIcon(icon);
|
||||
}
|
||||
|
||||
QIcon Action::icon () const
|
||||
{
|
||||
return _action->icon();
|
||||
}
|
||||
|
||||
void Action::setStatusTip(const QString & s)
|
||||
{
|
||||
_action->setStatusTip(s);
|
||||
@@ -206,7 +219,7 @@ void Action::setMenuRole(QAction::MenuRole menuRole)
|
||||
* to the command object.
|
||||
*/
|
||||
ActionGroup::ActionGroup ( Command* pcCmd,QObject * parent)
|
||||
: Action(pcCmd, parent), _group(0), _dropDown(false)
|
||||
: Action(pcCmd, parent), _group(0), _dropDown(false),_external(false),_toggle(false)
|
||||
{
|
||||
_group = new QActionGroup(this);
|
||||
connect(_group, SIGNAL(triggered(QAction*)), this, SLOT(onActivated (QAction*)));
|
||||
@@ -319,16 +332,13 @@ void ActionGroup::setCheckedAction(int i)
|
||||
*/
|
||||
void ActionGroup::onActivated ()
|
||||
{
|
||||
_pcCmd->invoke(this->property("defaultAction").toInt());
|
||||
_pcCmd->invoke(this->property("defaultAction").toInt(), Command::TriggerAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the command.
|
||||
*/
|
||||
void ActionGroup::onActivated (int index)
|
||||
void ActionGroup::onToggled(bool)
|
||||
{
|
||||
_pcCmd->invoke(index);
|
||||
}
|
||||
onActivated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the command.
|
||||
@@ -337,6 +347,9 @@ void ActionGroup::onActivated (QAction* a)
|
||||
{
|
||||
int index = _group->actions().indexOf(a);
|
||||
|
||||
// Calling QToolButton::setIcon() etc. has no effect if it has QAction set.
|
||||
// We have to change the QAction icon instead
|
||||
#if 0
|
||||
QList<QWidget*> widgets = a->associatedWidgets();
|
||||
for (QList<QWidget*>::iterator it = widgets.begin(); it != widgets.end(); ++it) {
|
||||
QMenu* menu = qobject_cast<QMenu*>(*it);
|
||||
@@ -344,12 +357,17 @@ void ActionGroup::onActivated (QAction* a)
|
||||
QToolButton* button = qobject_cast<QToolButton*>(menu->parent());
|
||||
if (button) {
|
||||
button->setIcon(a->icon());
|
||||
button->setText(a->text());
|
||||
button->setToolTip(a->toolTip());
|
||||
this->setProperty("defaultAction", QVariant(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_pcCmd->invoke(index);
|
||||
#endif
|
||||
this->setIcon(a->icon());
|
||||
this->setToolTip(a->toolTip());
|
||||
this->setProperty("defaultAction", QVariant(index));
|
||||
_pcCmd->invoke(index, Command::TriggerChildAction);
|
||||
}
|
||||
|
||||
void ActionGroup::onHovered (QAction *a)
|
||||
|
||||
@@ -54,12 +54,13 @@ public:
|
||||
virtual void setVisible(bool);
|
||||
|
||||
void setCheckable(bool);
|
||||
void setChecked (bool);
|
||||
void setChecked (bool, bool no_signal=false);
|
||||
bool isChecked() const;
|
||||
|
||||
void setShortcut (const QString &);
|
||||
QKeySequence shortcut() const;
|
||||
void setIcon (const QIcon &);
|
||||
QIcon icon() const;
|
||||
void setStatusTip (const QString &);
|
||||
QString statusTip() const;
|
||||
void setText (const QString &);
|
||||
@@ -69,6 +70,7 @@ public:
|
||||
void setWhatsThis (const QString &);
|
||||
QString whatsThis() const;
|
||||
void setMenuRole(QAction::MenuRole menuRole);
|
||||
QAction *action() {return _action;};
|
||||
|
||||
public Q_SLOTS:
|
||||
virtual void onActivated ();
|
||||
@@ -112,13 +114,15 @@ public:
|
||||
|
||||
public Q_SLOTS:
|
||||
void onActivated ();
|
||||
void onActivated (int);
|
||||
void onToggled(bool);
|
||||
void onActivated (QAction*);
|
||||
void onHovered (QAction*);
|
||||
|
||||
protected:
|
||||
QActionGroup* _group;
|
||||
bool _dropDown;
|
||||
bool _external;
|
||||
bool _toggle;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
# include <Inventor/nodes/SoPerspectiveCamera.h>
|
||||
#endif
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
#include <Python.h>
|
||||
#include <frameobject.h>
|
||||
|
||||
#include "Command.h"
|
||||
#include "Action.h"
|
||||
#include "Application.h"
|
||||
@@ -55,10 +60,13 @@
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include <Base/Sequencer.h>
|
||||
#include <Base/Tools.h>
|
||||
|
||||
#include <App/Document.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include <Gui/ViewProviderLink.h>
|
||||
|
||||
FC_LOG_LEVEL_INIT("Command", true, true);
|
||||
|
||||
using Base::Interpreter;
|
||||
using namespace Gui;
|
||||
@@ -218,6 +226,7 @@ Command::Command(const char* name)
|
||||
sGroup = QT_TR_NOOP("Standard");
|
||||
eType = AlterDoc | Alter3DView | AlterSelection;
|
||||
bEnabled = true;
|
||||
bCanLog = true;
|
||||
}
|
||||
|
||||
Command::~Command()
|
||||
@@ -238,18 +247,26 @@ bool Command::isViewOfType(Base::Type t) const
|
||||
|
||||
void Command::addTo(QWidget *pcWidget)
|
||||
{
|
||||
if (!_pcAction)
|
||||
if (!_pcAction) {
|
||||
_pcAction = createAction();
|
||||
testActive();
|
||||
}
|
||||
|
||||
_pcAction->addTo(pcWidget);
|
||||
}
|
||||
|
||||
void Command::addToGroup(ActionGroup* group, bool checkable)
|
||||
{
|
||||
if (!_pcAction)
|
||||
_pcAction = createAction();
|
||||
|
||||
addToGroup(group);
|
||||
_pcAction->setCheckable(checkable);
|
||||
}
|
||||
|
||||
void Command::addToGroup(ActionGroup* group)
|
||||
{
|
||||
if (!_pcAction) {
|
||||
_pcAction = createAction();
|
||||
testActive();
|
||||
}
|
||||
group->addAction(_pcAction->findChild<QAction*>());
|
||||
}
|
||||
|
||||
@@ -286,8 +303,84 @@ App::DocumentObject* Command::getObject(const char* Name) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Command::invoke(int i)
|
||||
int Command::_busy;
|
||||
|
||||
class PendingLine {
|
||||
public:
|
||||
PendingLine(MacroManager::LineType type, const char *line) {
|
||||
Application::Instance->macroManager()->addLine(type,line,true);
|
||||
}
|
||||
~PendingLine() {
|
||||
cancel();
|
||||
}
|
||||
void cancel() {
|
||||
Application::Instance->macroManager()->addLine(MacroManager::Cmt,0,true);
|
||||
}
|
||||
};
|
||||
|
||||
class CommandTrigger {
|
||||
public:
|
||||
CommandTrigger(Command::TriggerSource &trigger, Command::TriggerSource source)
|
||||
:trigger(trigger),saved(trigger)
|
||||
{
|
||||
trigger = source;
|
||||
}
|
||||
|
||||
~CommandTrigger() {
|
||||
trigger = saved;
|
||||
}
|
||||
private:
|
||||
Command::TriggerSource &trigger;
|
||||
Command::TriggerSource saved;
|
||||
};
|
||||
|
||||
void Command::setupCheckable(int iMsg) {
|
||||
QAction *action = 0;
|
||||
Gui::ActionGroup* pcActionGroup = qobject_cast<Gui::ActionGroup*>(_pcAction);
|
||||
if(pcActionGroup) {
|
||||
QList<QAction*> a = pcActionGroup->actions();
|
||||
assert(iMsg < a.size());
|
||||
action = a[iMsg];
|
||||
}else
|
||||
action = _pcAction->action();
|
||||
|
||||
if(!action)
|
||||
return;
|
||||
|
||||
bool checkable = action->isCheckable();
|
||||
_pcAction->setCheckable(checkable);
|
||||
if(checkable) {
|
||||
bool checked = false;
|
||||
switch(triggerSource()) {
|
||||
case TriggerNone:
|
||||
checked = !action->isChecked();
|
||||
break;
|
||||
case TriggerAction:
|
||||
checked = _pcAction->isChecked();
|
||||
break;
|
||||
case TriggerChildAction:
|
||||
checked = action->isChecked();
|
||||
break;
|
||||
}
|
||||
bool blocked = action->blockSignals(true);
|
||||
action->setChecked(checked);
|
||||
action->blockSignals(blocked);
|
||||
if(action!=_pcAction->action())
|
||||
_pcAction->setChecked(checked,true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Command::invoke(int i, TriggerSource trigger)
|
||||
{
|
||||
CommandTrigger cmdTrigger(_trigger,trigger);
|
||||
if(displayText.empty()) {
|
||||
displayText = getMenuText();
|
||||
boost::replace_all(displayText,"&","");
|
||||
if(displayText.empty())
|
||||
displayText = getName();
|
||||
}
|
||||
App::AutoTransaction committer((eType&NoTransaction)?0:displayText.c_str(),true);
|
||||
// Do not query _pcAction since it isn't created necessarily
|
||||
#ifdef FC_LOGUSERACTION
|
||||
Base::Console().Log("CmdG: %s\n",sName);
|
||||
@@ -295,9 +388,45 @@ void Command::invoke(int i)
|
||||
// set the application module type for the macro
|
||||
getGuiApplication()->macroManager()->setModule(sAppModule);
|
||||
try {
|
||||
std::unique_ptr<LogDisabler> disabler;
|
||||
if(bCanLog && !_busy)
|
||||
disabler.reset(new LogDisabler);
|
||||
// check if it really works NOW (could be a delay between click deactivation of the button)
|
||||
if (isActive())
|
||||
activated( i );
|
||||
if (isActive()) {
|
||||
auto manager = getGuiApplication()->macroManager();
|
||||
if(!disabler)
|
||||
activated( i );
|
||||
else {
|
||||
Gui::SelectionLogDisabler disabler;
|
||||
auto lines = manager->getLines();
|
||||
std::ostringstream ss;
|
||||
ss << "### Begin command " << sName;
|
||||
// Add a pending line to mark the start of a command
|
||||
PendingLine pending(MacroManager::Cmt, ss.str().c_str());
|
||||
activated( i );
|
||||
ss.str("");
|
||||
if(manager->getLines() == lines) {
|
||||
// This command does not record any lines, lets do it for
|
||||
// him. The above LogDisabler is to prevent nested command
|
||||
// logging, i.e. we only auto log the first invoking
|
||||
// command.
|
||||
|
||||
// Cancel the above pending line first
|
||||
pending.cancel();
|
||||
ss << "Gui.runCommand('" << sName << "'," << i << ')';
|
||||
if(eType & AlterDoc)
|
||||
manager->addLine(MacroManager::App, ss.str().c_str());
|
||||
else
|
||||
manager->addLine(MacroManager::Gui, ss.str().c_str());
|
||||
}else{
|
||||
// In case the command has any output to the console, lets
|
||||
// mark the end of the command here
|
||||
ss << "### End command " << sName;
|
||||
manager->addLine(MacroManager::Cmt, ss.str().c_str());
|
||||
}
|
||||
}
|
||||
getMainWindow()->updateActions();
|
||||
}
|
||||
}
|
||||
catch (const Base::SystemExitException&) {
|
||||
throw;
|
||||
@@ -350,6 +479,19 @@ void Command::testActive(void)
|
||||
}
|
||||
}
|
||||
|
||||
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
|
||||
if(pcAction) {
|
||||
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
|
||||
for(auto action : pcAction->actions()) {
|
||||
auto name = action->property("CommandName").toByteArray();
|
||||
if(!name.size())
|
||||
continue;
|
||||
Command* cmd = rcCmdMgr.getCommandByName(name);
|
||||
if(cmd)
|
||||
action->setEnabled(cmd->isActive());
|
||||
}
|
||||
}
|
||||
|
||||
bool bActive = isActive();
|
||||
_pcAction->setEnabled(bActive);
|
||||
}
|
||||
@@ -381,11 +523,35 @@ Gui::SelectionSingleton& Command::getSelection(void)
|
||||
return Gui::Selection();
|
||||
}
|
||||
|
||||
std::string Command::getUniqueObjectName(const char *BaseName) const
|
||||
std::string Command::getUniqueObjectName(const char *BaseName, const App::DocumentObject *obj) const
|
||||
{
|
||||
assert(hasActiveDocument());
|
||||
auto doc = obj?obj->getDocument():App::GetApplication().getActiveDocument();
|
||||
assert(doc);
|
||||
return doc->getUniqueObjectName(BaseName);
|
||||
}
|
||||
|
||||
return getActiveGuiDocument()->getDocument()->getUniqueObjectName(BaseName);
|
||||
std::string Command::getObjectCmd(const char *Name, const App::Document *doc,
|
||||
const char *prefix, const char *postfix, bool gui)
|
||||
{
|
||||
if(!doc) doc = App::GetApplication().getActiveDocument();
|
||||
if(!doc || !Name)
|
||||
return std::string("None");
|
||||
std::ostringstream str;
|
||||
if(prefix)
|
||||
str << prefix;
|
||||
str << (gui?"Gui":"App") << ".getDocument('" << doc->getName()
|
||||
<< "').getObject('" << Name << "')";
|
||||
if(postfix)
|
||||
str << postfix;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
std::string Command::getObjectCmd(const App::DocumentObject *obj,
|
||||
const char *prefix, const char *postfix, bool gui)
|
||||
{
|
||||
if(!obj || !obj->getNameInDocument())
|
||||
return std::string("None");
|
||||
return getObjectCmd(obj->getNameInDocument(), obj->getDocument(), prefix, postfix,gui);
|
||||
}
|
||||
|
||||
void Command::setAppModuleName(const char* s)
|
||||
@@ -406,7 +572,6 @@ void Command::setGroupName(const char* s)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// UNDO REDO transaction handling
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -420,31 +585,24 @@ void Command::setGroupName(const char* s)
|
||||
*/
|
||||
void Command::openCommand(const char* sCmdName)
|
||||
{
|
||||
// Using OpenCommand with no active document !
|
||||
assert(Gui::Application::Instance->activeDocument());
|
||||
|
||||
if (sCmdName)
|
||||
Gui::Application::Instance->activeDocument()->openCommand(sCmdName);
|
||||
else
|
||||
Gui::Application::Instance->activeDocument()->openCommand("Command");
|
||||
if (!sCmdName)
|
||||
sCmdName = "Command";
|
||||
App::GetApplication().setActiveTransaction(sCmdName);
|
||||
}
|
||||
|
||||
void Command::commitCommand(void)
|
||||
{
|
||||
assert(Gui::Application::Instance->activeDocument());
|
||||
Gui::Application::Instance->activeDocument()->commitCommand();
|
||||
App::GetApplication().closeActiveTransaction();
|
||||
}
|
||||
|
||||
void Command::abortCommand(void)
|
||||
{
|
||||
assert(Gui::Application::Instance->activeDocument());
|
||||
Gui::Application::Instance->activeDocument()->abortCommand();
|
||||
App::GetApplication().closeActiveTransaction(true);
|
||||
}
|
||||
|
||||
bool Command::hasPendingCommand(void)
|
||||
{
|
||||
assert(Gui::Application::Instance->activeDocument());
|
||||
return Gui::Application::Instance->activeDocument()->hasPendingCommand();
|
||||
return !!App::GetApplication().getActiveTransaction();
|
||||
}
|
||||
|
||||
bool Command::_blockCmd = false;
|
||||
@@ -455,7 +613,7 @@ void Command::blockCommand(bool block)
|
||||
}
|
||||
|
||||
/// Run a App level Action
|
||||
void Command::doCommand(DoCmd_Type eType, const char* sCmd, ...)
|
||||
void Command::_doCommand(const char *file, int line, DoCmd_Type eType, const char* sCmd, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, sCmd);
|
||||
@@ -470,37 +628,68 @@ void Command::doCommand(DoCmd_Type eType, const char* sCmd, ...)
|
||||
Base::Console().Log("CmdC: %s\n", format.constData());
|
||||
#endif
|
||||
|
||||
if (eType == Gui)
|
||||
Gui::Application::Instance->macroManager()->addLine(MacroManager::Gui, format.constData());
|
||||
else
|
||||
Gui::Application::Instance->macroManager()->addLine(MacroManager::App, format.constData());
|
||||
_runCommand(file,line,eType,format.constData());
|
||||
}
|
||||
|
||||
Base::Interpreter().runString(format.constData());
|
||||
void Command::printPyCaller() {
|
||||
if(!FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG))
|
||||
return;
|
||||
PyFrameObject* frame = PyEval_GetFrame();
|
||||
if(!frame)
|
||||
return;
|
||||
int line = PyFrame_GetLineNumber(frame);
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
const char *file = PyUnicode_AsUTF8(frame->f_code->co_filename);
|
||||
#else
|
||||
const char *file = PyString_AsString(frame->f_code->co_filename);
|
||||
#endif
|
||||
printCaller(file?file:"<no file>",line);
|
||||
}
|
||||
|
||||
void Command::printCaller(const char *file, int line) {
|
||||
if(!FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG))
|
||||
return;
|
||||
std::ostringstream str;
|
||||
#ifdef FC_OS_WIN32
|
||||
const char *_f = std::strstr(file, "\\src\\");
|
||||
#else
|
||||
const char *_f = std::strstr(file, "/src/");
|
||||
#endif
|
||||
str << "# " << (_f?_f+5:file)<<'('<<line<<')';
|
||||
Gui::Application::Instance->macroManager()->addLine(MacroManager::Cmt,str.str().c_str());
|
||||
}
|
||||
|
||||
/// Run a App level Action
|
||||
void Command::runCommand(DoCmd_Type eType, const char* sCmd)
|
||||
void Command::_runCommand(const char *file, int line, DoCmd_Type eType, const char* sCmd)
|
||||
{
|
||||
LogDisabler d1;
|
||||
SelectionLogDisabler d2;
|
||||
Base::PyGILStateLocker lock;
|
||||
|
||||
printCaller(file,line);
|
||||
if (eType == Gui)
|
||||
Gui::Application::Instance->macroManager()->addLine(MacroManager::Gui,sCmd);
|
||||
else
|
||||
Gui::Application::Instance->macroManager()->addLine(MacroManager::App,sCmd);
|
||||
Base::Interpreter().runString(sCmd);
|
||||
|
||||
try {
|
||||
Base::Interpreter().runString(sCmd);
|
||||
}catch(Py::Exception &) {
|
||||
Base::PyException::ThrowException();
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a App level Action
|
||||
void Command::runCommand(DoCmd_Type eType, const QByteArray& sCmd)
|
||||
void Command::_runCommand(const char *file, int line, DoCmd_Type eType, const QByteArray& sCmd)
|
||||
{
|
||||
if (eType == Gui)
|
||||
Gui::Application::Instance->macroManager()->addLine(MacroManager::Gui,sCmd.constData());
|
||||
else
|
||||
Gui::Application::Instance->macroManager()->addLine(MacroManager::App,sCmd.constData());
|
||||
Base::Interpreter().runString(sCmd.constData());
|
||||
_runCommand(file,line,eType,sCmd.constData());
|
||||
}
|
||||
|
||||
void Command::addModule(DoCmd_Type eType,const char* sModuleName)
|
||||
{
|
||||
if(alreadyLoadedModule.find(sModuleName) == alreadyLoadedModule.end()) {
|
||||
LogDisabler d1;
|
||||
SelectionLogDisabler d2;
|
||||
std::string sCmd("import ");
|
||||
sCmd += sModuleName;
|
||||
if (eType == Gui)
|
||||
@@ -512,7 +701,7 @@ void Command::addModule(DoCmd_Type eType,const char* sModuleName)
|
||||
}
|
||||
}
|
||||
|
||||
std::string Command::assureWorkbench(const char * sName)
|
||||
std::string Command::_assureWorkbench(const char *file, int line, const char * sName)
|
||||
{
|
||||
// check if the WB is already open?
|
||||
std::string actName = WorkbenchManager::instance()->active()->name();
|
||||
@@ -521,20 +710,67 @@ std::string Command::assureWorkbench(const char * sName)
|
||||
return actName;
|
||||
|
||||
// else - switch to new WB
|
||||
doCommand(Gui,"Gui.activateWorkbench('%s')",sName);
|
||||
_doCommand(file,line,Gui,"Gui.activateWorkbench('%s')",sName);
|
||||
|
||||
return actName;
|
||||
|
||||
}
|
||||
|
||||
void Command::copyVisual(const char* to, const char* attr, const char* from)
|
||||
void Command::_copyVisual(const char *file, int line, const char* to, const char* attr, const char* from)
|
||||
{
|
||||
doCommand(Gui,"Gui.ActiveDocument.%s.%s=Gui.ActiveDocument.%s.%s", to, attr, from, attr);
|
||||
_copyVisual(file,line,to,attr,from,attr);
|
||||
}
|
||||
|
||||
void Command::copyVisual(const char* to, const char* attr_to, const char* from, const char* attr_from)
|
||||
void Command::_copyVisual(const char *file, int line, const char* to, const char* attr_to, const char* from, const char* attr_from)
|
||||
{
|
||||
doCommand(Gui,"Gui.ActiveDocument.%s.%s=Gui.ActiveDocument.%s.%s", to, attr_to, from, attr_from);
|
||||
auto doc = App::GetApplication().getActiveDocument();
|
||||
if(!doc)
|
||||
return;
|
||||
return _copyVisual(file,line,doc->getObject(to),attr_to,
|
||||
doc->getObject(from),attr_from);
|
||||
}
|
||||
|
||||
void Command::_copyVisual(const char *file, int line, const App::DocumentObject *to, const char* attr_to, const App::DocumentObject *from, const char *attr_from)
|
||||
{
|
||||
if(!from || !from->getNameInDocument() || !to || !to->getNameInDocument())
|
||||
return;
|
||||
static std::map<std::string,std::string> attrMap = {
|
||||
{"ShapeColor","ShapeMaterial.DiffuseColor"},
|
||||
// {"LineColor","ShapeMaterial.DiffuseColor"},
|
||||
// {"PointColor","ShapeMaterial.DiffuseColor"},
|
||||
{"Transparency","Transparency"},
|
||||
};
|
||||
auto it = attrMap.find(attr_to);
|
||||
auto objCmd = getObjectCmd(to);
|
||||
if(it!=attrMap.end()) {
|
||||
auto obj = from;
|
||||
for(int depth=0;;++depth) {
|
||||
auto vp = dynamic_cast<Gui::ViewProviderLink*>(
|
||||
Gui::Application::Instance->getViewProvider(obj));
|
||||
if(vp && vp->OverrideMaterial.getValue()) {
|
||||
_doCommand(file,line,Gui,"%s.ViewObject.%s=%s.ViewObject.%s",
|
||||
objCmd.c_str(),attr_to,getObjectCmd(obj).c_str(),it->second.c_str());
|
||||
return;
|
||||
}
|
||||
auto linked = obj->getLinkedObject(false,0,false,depth);
|
||||
if(!linked || linked==obj)
|
||||
break;
|
||||
obj = linked;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
_doCommand(file,line,Gui,
|
||||
"%s.ViewObject.%s=getattr(%s.getLinkedObject(True).ViewObject,'%s',%s.ViewObject.%s)",
|
||||
objCmd.c_str(),attr_to,getObjectCmd(from).c_str(),attr_from,objCmd.c_str(),attr_to);
|
||||
}catch(Base::Exception &e) {
|
||||
// e.ReportException();
|
||||
}
|
||||
}
|
||||
|
||||
void Command::_copyVisual(const char *file, int line, const App::DocumentObject *to, const char* attr, const App::DocumentObject *from)
|
||||
{
|
||||
_copyVisual(file,line,to,attr,from,attr);
|
||||
}
|
||||
|
||||
std::string Command::getPythonTuple(const std::string& name, const std::vector<std::string>& subnames)
|
||||
@@ -899,6 +1135,8 @@ PythonCommand::PythonCommand(const char* name, PyObject * pcPyCommand, const cha
|
||||
type += int(AlterSelection);
|
||||
if (cmdType.find("ForEdit") != std::string::npos)
|
||||
type += int(ForEdit);
|
||||
if (cmdType.find("NoTransaction") != std::string::npos)
|
||||
type += int(NoTransaction);
|
||||
eType = type;
|
||||
}
|
||||
}
|
||||
@@ -1149,6 +1387,8 @@ void PythonGroupCommand::activated(int iMsg)
|
||||
assert(iMsg < a.size());
|
||||
QAction* act = a[iMsg];
|
||||
|
||||
setupCheckable(iMsg);
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
Py::Object cmd(_pcPyCommand);
|
||||
if (cmd.hasAttr("Activated")) {
|
||||
@@ -1160,12 +1400,21 @@ void PythonGroupCommand::activated(int iMsg)
|
||||
// If the command group doesn't implement the 'Activated' method then invoke the command directly
|
||||
else {
|
||||
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
|
||||
rcCmdMgr.runCommandByName(act->property("CommandName").toByteArray());
|
||||
auto cmd = rcCmdMgr.getCommandByName(act->property("CommandName").toByteArray());
|
||||
if(cmd) {
|
||||
bool checked = act->isCheckable() && act->isChecked();
|
||||
cmd->invoke(checked?1:0,TriggerAction);
|
||||
}
|
||||
}
|
||||
|
||||
// It is better to let ActionGroup::onActivated() to handle icon and
|
||||
// text change. The net effect is that the GUI won't change by user
|
||||
// inovking command through runCommandByName()
|
||||
#if 0
|
||||
// Since the default icon is reset when enabing/disabling the command we have
|
||||
// to explicitly set the icon of the used command.
|
||||
pcAction->setIcon(a[iMsg]->icon());
|
||||
#endif
|
||||
}
|
||||
catch(Py::Exception&) {
|
||||
Base::PyGILStateLocker lock;
|
||||
@@ -1180,6 +1429,7 @@ bool PythonGroupCommand::isActive(void)
|
||||
try {
|
||||
Base::PyGILStateLocker lock;
|
||||
Py::Object cmd(_pcPyCommand);
|
||||
|
||||
if (cmd.hasAttr("IsActive")) {
|
||||
Py::Callable call(cmd.getAttr("IsActive"));
|
||||
Py::Tuple args;
|
||||
@@ -1214,16 +1464,19 @@ Action * PythonGroupCommand::createAction(void)
|
||||
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
|
||||
|
||||
Py::Callable call(cmd.getAttr("GetCommands"));
|
||||
Py::Tuple args;
|
||||
Py::Tuple ret(call.apply(args));
|
||||
for (Py::Tuple::iterator it = ret.begin(); it != ret.end(); ++it) {
|
||||
Py::Sequence args;
|
||||
Py::Sequence ret(call.apply(args));
|
||||
for (auto it = ret.begin(); it != ret.end(); ++it) {
|
||||
Py::String str(*it);
|
||||
QAction* cmd = pcAction->addAction(QString());
|
||||
cmd->setProperty("CommandName", QByteArray(static_cast<std::string>(str).c_str()));
|
||||
|
||||
PythonCommand* pycmd = dynamic_cast<PythonCommand*>(rcCmdMgr.getCommandByName(cmd->property("CommandName").toByteArray()));
|
||||
if (pycmd) {
|
||||
cmd->setCheckable(pycmd->isCheckable());
|
||||
if (pycmd && pycmd->isCheckable()) {
|
||||
cmd->setCheckable(true);
|
||||
cmd->blockSignals(true);
|
||||
cmd->setChecked(pycmd->isChecked());
|
||||
cmd->blockSignals(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1233,15 +1486,18 @@ Action * PythonGroupCommand::createAction(void)
|
||||
defaultId = static_cast<int>(def);
|
||||
}
|
||||
|
||||
// if the command is 'exclusive' then activate the default action
|
||||
if (pcAction->isExclusive()) {
|
||||
QList<QAction*> a = pcAction->actions();
|
||||
if (defaultId >= 0 && defaultId < a.size()) {
|
||||
QAction* qtAction = a[defaultId];
|
||||
if (qtAction->isCheckable()) {
|
||||
QList<QAction*> a = pcAction->actions();
|
||||
if (defaultId >= 0 && defaultId < a.size()) {
|
||||
QAction* qtAction = a[defaultId];
|
||||
if (qtAction->isCheckable()) {
|
||||
// if the command is 'exclusive' then activate the default action
|
||||
if (pcAction->isExclusive()) {
|
||||
qtAction->blockSignals(true);
|
||||
qtAction->setChecked(true);
|
||||
qtAction->blockSignals(false);
|
||||
}else if(qtAction->isCheckable()){
|
||||
pcAction->setCheckable(true);
|
||||
pcAction->setChecked(qtAction->isChecked(),true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,167 @@
|
||||
|
||||
#include <Base/Type.h>
|
||||
|
||||
/** @defgroup CommandMacros Helper macros for running commands through Python interpreter */
|
||||
//@{
|
||||
|
||||
/** Runs a command for accessing document attribute or method
|
||||
* @param _type: type of document, Gui or App
|
||||
* @param _doc: pointer to a document
|
||||
* @param _cmd: command string, streamable
|
||||
*
|
||||
* Example:
|
||||
* @code{.cpp}
|
||||
* _FCMD_DOC_CMD(Gui,doc,"getObject('" << objName << "')");
|
||||
* @endcode
|
||||
*
|
||||
* Translates to command (assuming doc's name is 'DocName', and
|
||||
* and objName constains value 'ObjName'):
|
||||
* @code{.py}
|
||||
* Gui.getDocument('DocName').getObject('ObjName')
|
||||
* @endcode
|
||||
*/
|
||||
#define _FCMD_DOC_CMD(_type,_doc,_cmd) do{\
|
||||
auto __doc = _doc;\
|
||||
if(__doc && __doc->getName()) {\
|
||||
std::ostringstream _str;\
|
||||
_str << #_type ".getDocument('" << __doc->getName() << "')." << _cmd;\
|
||||
Gui::Command::runCommand(Gui::Command::Doc,_str.str().c_str());\
|
||||
}\
|
||||
}while(0)
|
||||
|
||||
/** Runs a command for accessing App.Document attribute or method
|
||||
*
|
||||
* @param _doc: pointer to a document
|
||||
* @param _cmd: command string, streamable
|
||||
* @sa _FCMD_DOC_CMD()
|
||||
*/
|
||||
#define FCMD_DOC_CMD(_doc,_cmd) _FCMD_DOC_CMD(App,_doc,_cmd)
|
||||
|
||||
/** Runs a command for accessing an object's document attribute or method
|
||||
* @param _type: type of the document, Gui or App
|
||||
* @param _obj: pointer to a DocumentObject
|
||||
* @param _cmd: command string, streamable
|
||||
*/
|
||||
#define _FCMD_OBJ_DOC_CMD(_type,_obj,_cmd) do{\
|
||||
auto __obj = _obj;\
|
||||
if(__obj)\
|
||||
_FCMD_DOC_CMD(_type,__obj->getDocument(),_cmd);\
|
||||
}while(0)
|
||||
|
||||
/** Runs a command for accessing an object's App::Document attribute or method
|
||||
* @param _obj: pointer to a DocumentObject
|
||||
* @param _cmd: command string, streamable
|
||||
*/
|
||||
#define FCMD_OBJ_DOC_CMD(_obj,_cmd) _FCMD_OBJ_DOC_CMD(App,_obj,_cmd)
|
||||
|
||||
/** Runs a command for accessing an object's Gui::Document attribute or method
|
||||
* @param _obj: pointer to a DocumentObject
|
||||
* @param _cmd: command string, streamable
|
||||
*/
|
||||
#define FCMD_VOBJ_DOC_CMD(_obj,_cmd) _FCMD_OBJ_DOC_CMD(Gui,_obj,_cmd)
|
||||
|
||||
/** Runs a command for accessing a document/view object's attribute or method
|
||||
* @param _type: type of the object, Gui or App
|
||||
* @param _obj: pointer to a DocumentObject
|
||||
* @param _cmd: command string, streamable
|
||||
*
|
||||
* Example:
|
||||
* @code{.cpp}
|
||||
* _FCMD_OBJ_CMD(Gui,obj,"Visibility = " << (visible?"True":"False"));
|
||||
* @endcode
|
||||
*
|
||||
* Translates to command (assuming obj's document name is 'DocName', obj's name
|
||||
* is 'ObjName', and visible is true):
|
||||
* @code{.py}
|
||||
* Gui.getDocument('DocName').getObject('ObjName').Visibility = True
|
||||
* @endcode
|
||||
*/
|
||||
#define _FCMD_OBJ_CMD(_type,_cmd_type,_obj,_cmd) do{\
|
||||
auto __obj = _obj;\
|
||||
if(__obj && __obj->getNameInDocument()) {\
|
||||
std::ostringstream _str;\
|
||||
_str << #_type ".getDocument('" << __obj->getDocument()->getName() \
|
||||
<< "').getObject('" << __obj->getNameInDocument() << "')." << _cmd;\
|
||||
Gui::Command::runCommand(Gui::Command::_cmd_type,_str.str().c_str());\
|
||||
}\
|
||||
}while(0)
|
||||
|
||||
/** Runs a command for accessing an document object's attribute or method
|
||||
* @param _obj: pointer to a DocumentObject
|
||||
* @param _cmd: command string, streamable
|
||||
* @sa _FCMD_OBJ_CMD()
|
||||
*/
|
||||
#define FCMD_OBJ_CMD(_obj,_cmd) _FCMD_OBJ_CMD(App,Doc,_obj,_cmd)
|
||||
|
||||
/** Runs a command for accessing an view object's attribute or method
|
||||
* @param _obj: pointer to a DocumentObject
|
||||
* @param _cmd: command string, streamable
|
||||
* @sa _FCMD_OBJ_CMD()
|
||||
*/
|
||||
#define FCMD_VOBJ_CMD(_obj,_cmd) _FCMD_OBJ_CMD(Gui,Gui,_obj,_cmd)
|
||||
|
||||
/** Runs a command for accessing a document object's attribute or method
|
||||
* @param _cmd: command string, supporting printf like formatter
|
||||
* @param _obj: pointer to a DocumentObject
|
||||
*
|
||||
* Example:
|
||||
* @code{.cpp}
|
||||
* FCMD_OBJ_CMD2("Visibility = %s", obj, visible?"True":"False");
|
||||
* @endcode
|
||||
*
|
||||
* Translates to command (assuming obj's document name is 'DocName', obj's name
|
||||
* is 'ObjName', and visible is true):
|
||||
* @code{.py}
|
||||
* App.getDocument('DocName').getObject('ObjName').Visibility = True
|
||||
* @endcode
|
||||
*/
|
||||
#define FCMD_OBJ_CMD2(_cmd,_obj,...) do{\
|
||||
auto __obj = _obj;\
|
||||
if(__obj && __obj->getNameInDocument()) {\
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.getDocument('%s').getObject('%s')." _cmd,\
|
||||
__obj->getDocument()->getName(),__obj->getNameInDocument(),## __VA_ARGS__);\
|
||||
}\
|
||||
}while(0)
|
||||
|
||||
/** Runs a command for accessing a view object's attribute or method
|
||||
* @param _cmd: command string, supporting printf like formatter
|
||||
* @param _obj: pointer to a DocumentObject
|
||||
* @sa FCMD_OBJ_CMD2()
|
||||
*/
|
||||
#define FCMD_VOBJ_CMD2(_cmd,_obj,...) do{\
|
||||
auto __obj = _obj;\
|
||||
if(__obj && __obj->getNameInDocument()) {\
|
||||
Gui::Command::doCommand(Gui::Command::Gui,"Gui.getDocument('%s').getObject('%s')." _cmd,\
|
||||
__obj->getDocument()->getName(),__obj->getNameInDocument(),## __VA_ARGS__);\
|
||||
}\
|
||||
}while(0)
|
||||
|
||||
/** Runs a command to start editing a give object
|
||||
* @param _obj: pointer to a DocumentObject
|
||||
*
|
||||
* Unlike other FCMD macros, this macro editing the object using the current
|
||||
* active document, instead of the object's owner document. This allows
|
||||
* in-place editing an object, which may be brought in through linking to an
|
||||
* external group.
|
||||
*/
|
||||
#define FCMD_SET_EDIT(_obj) do{\
|
||||
auto __obj = _obj;\
|
||||
if(__obj && __obj->getNameInDocument()) {\
|
||||
Gui::Command::doCommand(Gui::Command::Gui,\
|
||||
"Gui.ActiveDocument.setEdit(App.getDocument('%s').getObject('%s'))",\
|
||||
__obj->getDocument()->getName(), __obj->getNameInDocument());\
|
||||
}\
|
||||
}while(0)
|
||||
|
||||
|
||||
/// Hides an object
|
||||
#define FCMD_OBJ_HIDE(_obj) FCMD_OBJ_CMD(_obj,"Visibility = False")
|
||||
|
||||
/// Shows an object
|
||||
#define FCMD_OBJ_SHOW(_obj) FCMD_OBJ_CMD(_obj,"Visibility = True")
|
||||
|
||||
//@}
|
||||
|
||||
class QWidget;
|
||||
class QByteArray;
|
||||
|
||||
@@ -63,6 +224,7 @@ void CreateViewStdCommands(void);
|
||||
void CreateWindowStdCommands(void);
|
||||
void CreateStructureCommands(void);
|
||||
void CreateTestCommands(void);
|
||||
void CreateLinkCommands(void);
|
||||
|
||||
|
||||
/** The CommandBase class
|
||||
@@ -135,6 +297,7 @@ protected:
|
||||
//@}
|
||||
protected:
|
||||
Action *_pcAction; /**< The Action item. */
|
||||
std::string displayText;
|
||||
};
|
||||
|
||||
/** The Command class.
|
||||
@@ -162,8 +325,6 @@ protected:
|
||||
//@{
|
||||
/// Methods which gets called when activated, needs to be reimplemented!
|
||||
virtual void activated(int iMsg)=0;
|
||||
/// Override this method if your Cmd is not always active
|
||||
virtual bool isActive(void){return true;}
|
||||
/// Creates the used Action
|
||||
virtual Action * createAction(void);
|
||||
/// Applies the menu text, tool and status tip to the passed action object
|
||||
@@ -176,15 +337,35 @@ public:
|
||||
//@{
|
||||
/// CommandManager is a friend
|
||||
friend class CommandManager;
|
||||
/// Override this method if your Cmd is not always active
|
||||
virtual bool isActive(void){return true;}
|
||||
/// Get somtile called to check the state of the command
|
||||
void testActive(void);
|
||||
/// Enables or disables the command
|
||||
void setEnabled(bool);
|
||||
/// get called by the QAction
|
||||
void invoke (int);
|
||||
/// Command trigger source
|
||||
enum TriggerSource {
|
||||
/// No external trigger, e.g. invoked through Python
|
||||
TriggerNone,
|
||||
/// Command triggered by an action
|
||||
TriggerAction,
|
||||
/// Command triggered by a child action inside an action group
|
||||
TriggerChildAction,
|
||||
};
|
||||
/// Return the current command trigger source
|
||||
TriggerSource triggerSource() const {return _trigger;}
|
||||
/** Called to invoke the command
|
||||
*
|
||||
* @param index: in case of group command, this is the index of the child
|
||||
* command. For checkable command, this indicates the
|
||||
* checkable state.
|
||||
* @param trigger: indicate the command triggering source, see TriggerSource.
|
||||
*/
|
||||
void invoke (int index, TriggerSource trigger=TriggerNone);
|
||||
/// adds this command to arbitrary widgets
|
||||
void addTo(QWidget *);
|
||||
void addToGroup(ActionGroup *, bool checkable);
|
||||
void addToGroup(ActionGroup *);
|
||||
//@}
|
||||
|
||||
|
||||
@@ -206,8 +387,19 @@ public:
|
||||
bool isViewOfType(Base::Type t) const;
|
||||
/// returns the named feature or the active one from the active document or NULL
|
||||
App::DocumentObject* getObject(const char* Name) const;
|
||||
/// Get unique Feature name from the active document
|
||||
std::string getUniqueObjectName(const char *BaseName) const;
|
||||
/// returns a python command string to retrieve an object from a document
|
||||
static std::string getObjectCmd(const char *Name, const App::Document *doc=0,
|
||||
const char *prefix=0, const char *postfix=0, bool gui=false);
|
||||
/// returns a python command string to retrieve the given object
|
||||
static std::string getObjectCmd(const App::DocumentObject *obj,
|
||||
const char *prefix=0, const char *postfix=0, bool gui=false);
|
||||
/** Get unique Feature name from the active document
|
||||
*
|
||||
* @param BaseName: the base name
|
||||
* @param obj: if not zero, then request the unique name in the document of
|
||||
* the given object.
|
||||
*/
|
||||
std::string getUniqueObjectName(const char *BaseName, const App::DocumentObject *obj=0) const;
|
||||
//@}
|
||||
|
||||
/** @name Helper methods for the Undo/Redo and Update handling */
|
||||
@@ -230,6 +422,8 @@ public:
|
||||
void languageChange();
|
||||
/// Updates the QAction with respect to the passed mode.
|
||||
void updateAction(int mode);
|
||||
/// Setup checkable actions based on current TriggerSource
|
||||
void setupCheckable(int iMsg);
|
||||
//@}
|
||||
|
||||
/** @name Helper methods for issuing commands to the Python interpreter */
|
||||
@@ -245,17 +439,89 @@ public:
|
||||
};
|
||||
/// Blocks all command objects
|
||||
static void blockCommand(bool);
|
||||
/// Run a App level Action
|
||||
static void doCommand(DoCmd_Type eType,const char* sCmd,...);
|
||||
static void runCommand(DoCmd_Type eType,const char* sCmd);
|
||||
static void runCommand(DoCmd_Type eType,const QByteArray& sCmd);
|
||||
/// Print to Python console the current Python calling source file and line number
|
||||
static void printPyCaller();
|
||||
/// Print to Python console the current calling source file and line number
|
||||
static void printCaller(const char *file, int line);
|
||||
|
||||
/** Convenience macro to run a command with printf like formatter
|
||||
*
|
||||
* @sa Command::_doCommand()
|
||||
*/
|
||||
#define doCommand(_type,_cmd,...) _doCommand(__FILE__,__LINE__,_type,_cmd,##__VA_ARGS__)
|
||||
|
||||
/** Run a command with printf like formatter
|
||||
*
|
||||
* @param file: the calling file path (for debugging purpose)
|
||||
* @param line: the calling line number (for debugging purpose)
|
||||
* @param eType: command type, See DoCmd_Type
|
||||
* @param sCmd: command string that supports printf like formatter
|
||||
*
|
||||
* You can use the convenience macro doCommand() to automate \c file and \c
|
||||
* line arguments. You may also want to use various helper @ref CommandMacros.
|
||||
*/
|
||||
static void _doCommand(const char *file, int line, DoCmd_Type eType,const char* sCmd,...);
|
||||
|
||||
/** Convenience macro to run a command
|
||||
*
|
||||
* @sa Command::_runCommand()
|
||||
*/
|
||||
#define runCommand(_type,_cmd) _runCommand(__FILE__,__LINE__,_type,_cmd)
|
||||
|
||||
/** Run a command
|
||||
*
|
||||
* @param file: the calling file path (for debugging purpose)
|
||||
* @param line: the calling line number (for debugging purpose)
|
||||
* @param eType: command type, See DoCmd_Type
|
||||
* @param sCmd: command string
|
||||
*
|
||||
* @sa _doCommand()
|
||||
*/
|
||||
static void _runCommand(const char *file, int line, DoCmd_Type eType,const char* sCmd);
|
||||
|
||||
/** Run a command
|
||||
*
|
||||
* @param file: the calling file path (for debugging purpose)
|
||||
* @param line: the calling line number (for debugging purpose)
|
||||
* @param eType: command type, See DoCmd_Type
|
||||
* @param sCmd: command string
|
||||
*
|
||||
* @sa _doCommand()
|
||||
*/
|
||||
static void _runCommand(const char *file, int line, DoCmd_Type eType,const QByteArray& sCmd);
|
||||
|
||||
/// import an external (or own) module only once
|
||||
static void addModule(DoCmd_Type eType,const char* sModuleName);
|
||||
/// assures the switch to a certain workbench, if already in the workbench, does nothing.
|
||||
static std::string assureWorkbench(const char * sName);
|
||||
|
||||
static void copyVisual(const char* to, const char* attr, const char* from);
|
||||
static void copyVisual(const char* to, const char* attr_to, const char* from, const char* attr_from);
|
||||
/** Convenience macro to assure the switch to a certain workbench
|
||||
*
|
||||
* @sa _assureWorkbench()
|
||||
*/
|
||||
#define assureWorkbench(_name) _assureWorkbench(__FILE__,__LINE__,_name)
|
||||
|
||||
/** Assures the switch to a certain workbench
|
||||
*
|
||||
* @param file: the calling file path (for debugging purpose)
|
||||
* @param line: the calling line number (for debugging purpose)
|
||||
* @param sName: workbench name
|
||||
*
|
||||
* @return Return the current active workbench name before switching.
|
||||
*
|
||||
* If already in the workbench, does nothing.
|
||||
*/
|
||||
static std::string _assureWorkbench(const char *file, int line, const char * sName);
|
||||
//@}
|
||||
|
||||
/** @name Methods for copying visiual properties */
|
||||
//@{
|
||||
/// Convenience macro to copy visual properties
|
||||
#define copyVisual(...) _copyVisual(__FILE__,__LINE__,## __VA_ARGS__)
|
||||
static void _copyVisual(const char *file, int line, const char* to, const char* attr, const char* from);
|
||||
static void _copyVisual(const char *file, int line, const char* to, const char* attr_to, const char* from, const char* attr_from);
|
||||
static void _copyVisual(const char *file, int line, const App::DocumentObject *to, const char *attr, const App::DocumentObject *from);
|
||||
static void _copyVisual(const char *file, int line, const App::DocumentObject *to, const char *attr_to, const App::DocumentObject *from, const char *attr_from);
|
||||
//@}
|
||||
|
||||
/// Get Python tuple from object and sub-elements
|
||||
static std::string getPythonTuple(const std::string& name, const std::vector<std::string>& subnames);
|
||||
/// translate a string to a python string literal (needed e.g. in file names for windows...)
|
||||
@@ -263,7 +529,6 @@ public:
|
||||
const std::string strToPython(const std::string &Str){
|
||||
return strToPython(Str.c_str());
|
||||
}
|
||||
//@}
|
||||
|
||||
/** @name Helper methods to generate help pages */
|
||||
//@{
|
||||
@@ -301,12 +566,25 @@ public:
|
||||
void adjustCameraPosition();
|
||||
//@}
|
||||
|
||||
/// Helper class to disable python console log
|
||||
class LogDisabler {
|
||||
public:
|
||||
LogDisabler() {
|
||||
++Command::_busy;
|
||||
}
|
||||
~LogDisabler() {
|
||||
--Command::_busy;
|
||||
}
|
||||
};
|
||||
friend class LogDisabler;
|
||||
|
||||
protected:
|
||||
enum CmdType {
|
||||
AlterDoc = 1, /**< Command change the Document */
|
||||
Alter3DView = 2, /**< Command change the Gui */
|
||||
AlterSelection = 4, /**< Command change the Selection */
|
||||
ForEdit = 8 /**< Command is in a special edit mode active */
|
||||
ForEdit = 8, /**< Command is in a special edit mode active */
|
||||
NoTransaction = 16, /**< Do not setup auto transaction */
|
||||
};
|
||||
|
||||
/** @name Attributes
|
||||
@@ -320,10 +598,15 @@ protected:
|
||||
const char* sName;
|
||||
const char* sHelpUrl;
|
||||
int eType;
|
||||
/// Indicate if the command shall log to MacroManager
|
||||
bool bCanLog;
|
||||
//@}
|
||||
private:
|
||||
static int _busy;
|
||||
bool bEnabled;
|
||||
static bool _blockCmd;
|
||||
/// For storing the current command trigger source
|
||||
TriggerSource _trigger = TriggerNone;
|
||||
};
|
||||
|
||||
/** The Python command class
|
||||
|
||||
@@ -146,7 +146,7 @@ StdCmdRecentFiles::StdCmdRecentFiles()
|
||||
sToolTipText = QT_TR_NOOP("Recent file list");
|
||||
sWhatsThis = "Std_RecentFiles";
|
||||
sStatusTip = QT_TR_NOOP("Recent file list");
|
||||
eType = 0;
|
||||
eType = NoTransaction;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -142,7 +142,7 @@ StdCmdCloseActiveWindow::StdCmdCloseActiveWindow()
|
||||
// collide with this shortcut. Thus the shortcut of QMdiSubWindow will be
|
||||
// reset in MainWindow::addWindow() (#0002631)
|
||||
sAccel = keySequenceToAccel(QKeySequence::Close);
|
||||
eType = 0;
|
||||
eType = NoTransaction;
|
||||
}
|
||||
|
||||
void StdCmdCloseActiveWindow::activated(int iMsg)
|
||||
|
||||
@@ -54,7 +54,8 @@ MacroManager::MacroManager()
|
||||
scriptToPyConsole(true),
|
||||
localEnv(true),
|
||||
pyConsole(0),
|
||||
pyDebugger(new PythonDebugger())
|
||||
pyDebugger(new PythonDebugger()),
|
||||
totalLines(0)
|
||||
{
|
||||
// Attach to the Parametergroup regarding macros
|
||||
this->params = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro");
|
||||
@@ -166,8 +167,25 @@ void MacroManager::cancel(void)
|
||||
this->openMacro = false;
|
||||
}
|
||||
|
||||
void MacroManager::addLine(LineType Type, const char* sLine)
|
||||
void MacroManager::addLine(LineType Type, const char* sLine, bool pending)
|
||||
{
|
||||
if(pending) {
|
||||
if(!sLine)
|
||||
pendingLine.clear();
|
||||
else {
|
||||
pendingType = Type;
|
||||
pendingLine = sLine;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(!sLine)
|
||||
return;
|
||||
if(pendingLine.size() && pendingLine.c_str()!=sLine) {
|
||||
addLine(pendingType,pendingLine.c_str());
|
||||
pendingLine.clear();
|
||||
}
|
||||
|
||||
++totalLines;
|
||||
if (this->openMacro) {
|
||||
bool comment = false;
|
||||
if (Type == Gui) {
|
||||
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
enum LineType {
|
||||
App, /**< The line effects only the document and Application (FreeCAD) */
|
||||
Gui, /**< The line effects the Gui (FreeCADGui) */
|
||||
Cmt /**< The line is handled as a comment */
|
||||
Cmt, /**< The line is handled as a comment */
|
||||
};
|
||||
|
||||
/** Opens a new Macro recording session
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
/// indicates if a macro recording in in progress
|
||||
bool isOpen(void) const {return openMacro;}
|
||||
/// insert a new line in the macro
|
||||
void addLine(LineType Type,const char* sLine);
|
||||
void addLine(LineType Type,const char* sLine,bool pending=false);
|
||||
/** Set the active module
|
||||
* This is normally done by the workbench switch. It sets
|
||||
* the actually active application module so when the macro
|
||||
@@ -93,6 +93,9 @@ public:
|
||||
/** Observes its parameter group. */
|
||||
void OnChange(Base::Subject<const char*> &rCaller, const char * sReason);
|
||||
|
||||
/// Return the added lines regardless of recording or not
|
||||
long getLines() const {return totalLines;}
|
||||
|
||||
protected:
|
||||
QStringList macroInProgress; /**< Container for the macro */
|
||||
QString macroName; /**< name of the macro */
|
||||
@@ -104,6 +107,9 @@ protected:
|
||||
PythonConsole* pyConsole; // link to the python console
|
||||
PythonDebugger* pyDebugger;
|
||||
Base::Reference<ParameterGrp> params; // link to the Macro parameter group
|
||||
long totalLines;
|
||||
std::string pendingLine;
|
||||
LineType pendingType;
|
||||
|
||||
friend struct ApplicationP;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user