diff --git a/src/Gui/Action.cpp b/src/Gui/Action.cpp index 9fea44fa97..a8fa8503c3 100644 --- a/src/Gui/Action.cpp +++ b/src/Gui/Action.cpp @@ -227,7 +227,7 @@ void Action::setMenuRole(QAction::MenuRole menuRole) * to the command object. */ ActionGroup::ActionGroup ( Command* pcCmd,QObject * parent) - : Action(pcCmd, parent), _group(0), _dropDown(false),_external(false),_toggle(false) + : Action(pcCmd, parent), _group(0), _dropDown(false),_external(false),_toggle(false),_isMode(false) { _group = new QActionGroup(this); connect(_group, SIGNAL(triggered(QAction*)), this, SLOT(onActivated (QAction*))); @@ -336,7 +336,7 @@ void ActionGroup::setCheckedAction(int i) QAction* a = _group->actions()[i]; a->setChecked(true); this->setIcon(a->icon()); - this->setToolTip(a->toolTip()); + if (!this->_isMode) this->setToolTip(a->toolTip()); this->setProperty("defaultAction", QVariant(i)); } @@ -378,7 +378,7 @@ void ActionGroup::onActivated (QAction* a) } #endif this->setIcon(a->icon()); - this->setToolTip(a->toolTip()); + if (!this->_isMode) this->setToolTip(a->toolTip()); this->setProperty("defaultAction", QVariant(index)); _pcCmd->invoke(index, Command::TriggerChildAction); } diff --git a/src/Gui/Action.h b/src/Gui/Action.h index 5aa6f2c313..d50deb8cb0 100644 --- a/src/Gui/Action.h +++ b/src/Gui/Action.h @@ -107,6 +107,7 @@ public: void setExclusive (bool); bool isExclusive() const; void setVisible (bool); + void setIsMode(bool b) { _isMode = b; } void setDropDownMenu(bool b) { _dropDown = b; } QAction* addAction(QAction*); @@ -126,6 +127,7 @@ protected: bool _dropDown; bool _external; bool _toggle; + bool _isMode; }; // -------------------------------------------------------------------- diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index b4eb6fc118..c93b2b6b03 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -1244,6 +1244,50 @@ void Application::tryClose(QCloseEvent * e) } } +int Application::getUserEditMode(const std::string &mode) const +{ + if (mode.empty()) { + return userEditMode; + } + for (auto const &uem : userEditModes) { + if (uem.second == mode) { + return uem.first; + } + } + return -1; +} + +std::string Application::getUserEditModeName(int mode) const +{ + if (mode == -1) { + return userEditModes.at(userEditMode); + } + if (userEditModes.find(mode) != userEditModes.end()) { + return userEditModes.at(mode); + } + return ""; +} + +bool Application::setUserEditMode(int mode) +{ + if (userEditModes.find(mode) != userEditModes.end() && userEditMode != mode) { + userEditMode = mode; + this->signalUserEditModeChanged(userEditMode); + return true; + } + return false; +} + +bool Application::setUserEditMode(const std::string &mode) +{ + for (auto const &uem : userEditModes) { + if (uem.second == mode) { + return setUserEditMode(uem.first); + } + } + return false; +} + /** * Activate the matching workbench to the registered workbench handler with name \a name. * The handler must be an instance of a class written in Python. diff --git a/src/Gui/Application.h b/src/Gui/Application.h index 0f596d3276..6c436f97d9 100644 --- a/src/Gui/Application.h +++ b/src/Gui/Application.h @@ -27,6 +27,7 @@ #include #include #include +#include #define putpix() @@ -133,6 +134,8 @@ public: boost::signals2::signal signalInEdit; /// signal on leaving edit mode boost::signals2::signal signalResetEdit; + /// signal on changing user edit mode + boost::signals2::signal signalUserEditModeChanged; //@} /** @name methods for Document handling */ @@ -227,6 +230,28 @@ public: static void runApplication(void); void tryClose( QCloseEvent * e ); //@} + + /** @name User edit mode */ + //@{ +protected: + // the below std::map is a translation of 'EditMode' enum in ViewProvider.h + // to add a new edit mode, it should first be added there + // this is only used for GUI user interaction (menu, toolbar, Python API) + const std::map userEditModes { + {0, QT_TRANSLATE_NOOP("EditMode", "Default")}, + {1, QT_TRANSLATE_NOOP("EditMode", "Transform")}, + {2, QT_TRANSLATE_NOOP("EditMode", "Cutting")}, + {3, QT_TRANSLATE_NOOP("EditMode", "Color")} + }; + int userEditMode = userEditModes.begin()->first; + +public: + std::map listUserEditModes() const { return userEditModes; } + int getUserEditMode(const std::string &mode = "") const; + std::string getUserEditModeName(int mode = -1) const; + bool setUserEditMode(int mode); + bool setUserEditMode(const std::string &mode); + //@} public: //--------------------------------------------------------------------- @@ -293,6 +318,10 @@ public: static PyObject* sAddDocObserver (PyObject *self,PyObject *args); static PyObject* sRemoveDocObserver (PyObject *self,PyObject *args); + + static PyObject* sListUserEditModes (PyObject *self,PyObject *args); + static PyObject* sGetUserEditMode (PyObject *self,PyObject *args); + static PyObject* sSetUserEditMode (PyObject *self,PyObject *args); static PyMethodDef Methods[]; diff --git a/src/Gui/ApplicationPy.cpp b/src/Gui/ApplicationPy.cpp index 13016c977f..61d636a63b 100644 --- a/src/Gui/ApplicationPy.cpp +++ b/src/Gui/ApplicationPy.cpp @@ -205,6 +205,18 @@ PyMethodDef Application::Methods[] = { {"removeDocumentObserver", (PyCFunction) Application::sRemoveDocObserver, METH_VARARGS, "removeDocumentObserver() -> None\n\n" "Remove an added document observer."}, + + {"listUserEditModes", (PyCFunction) Application::sListUserEditModes, METH_VARARGS, + "listUserEditModes() -> list\n\n" + "List available user edit modes"}, + + {"getUserEditMode", (PyCFunction) Application::sGetUserEditMode, METH_VARARGS, + "getUserEditMode() -> string\n\n" + "Get current user edit mode"}, + + {"setUserEditMode", (PyCFunction) Application::sSetUserEditMode, METH_VARARGS, + "setUserEditMode(string=mode) -> Bool\n\n" + "Set user edit mode to 'mode', returns True if exists, false otherwise"}, {"reload", (PyCFunction) Application::sReload, METH_VARARGS, "reload(name) -> doc\n\n" @@ -1485,3 +1497,29 @@ PyObject* Application::sCoinRemoveAllChildren(PyObject * /*self*/, PyObject *arg }PY_CATCH; } +PyObject* Application::sListUserEditModes(PyObject * /*self*/, PyObject *args) +{ + Py::List ret; + if (!PyArg_ParseTuple(args, "")) + return NULL; + for (auto const &uem : Instance->listUserEditModes()) { + ret.append(Py::String(uem.second)); + } + return Py::new_reference_to(ret); +} + +PyObject* Application::sGetUserEditMode(PyObject * /*self*/, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + return Py::new_reference_to(Py::String(Instance->getUserEditModeName())); +} + +PyObject* Application::sSetUserEditMode(PyObject * /*self*/, PyObject *args) +{ + char *mode = ""; + if (!PyArg_ParseTuple(args, "s", &mode)) + return NULL; + bool ok = Instance->setUserEditMode(std::string(mode)); + return Py::new_reference_to(Py::Boolean(ok)); +} diff --git a/src/Gui/Command.h b/src/Gui/Command.h index 72190a2bc1..f03fd22de8 100644 --- a/src/Gui/Command.h +++ b/src/Gui/Command.h @@ -31,6 +31,7 @@ #include #include +#include /** @defgroup CommandMacros Helper macros for running commands through Python interpreter */ //@{ @@ -179,8 +180,8 @@ 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());\ + "Gui.ActiveDocument.setEdit(App.getDocument('%s').getObject('%s'), %i)",\ + __obj->getDocument()->getName(), __obj->getNameInDocument(), Gui::Application::Instance->getUserEditMode());\ }\ }while(0) diff --git a/src/Gui/CommandStd.cpp b/src/Gui/CommandStd.cpp index e62df2d7e8..a0ae9fdea4 100644 --- a/src/Gui/CommandStd.cpp +++ b/src/Gui/CommandStd.cpp @@ -29,6 +29,7 @@ # include # include # include +# include #endif #include @@ -63,6 +64,7 @@ using Base::Console; using Base::Sequencer; using namespace Gui; +namespace bp = boost::placeholders; //=========================================================================== @@ -813,6 +815,100 @@ void StdCmdUnitsCalculator::activated(int iMsg) dlg->show(); } +//=========================================================================== +// StdCmdUserEditMode +//=========================================================================== +class StdCmdUserEditMode : public Gui::Command +{ +public: + StdCmdUserEditMode(); + virtual ~StdCmdUserEditMode(){} + virtual void languageChange(); + virtual const char* className() const {return "StdCmdUserEditMode";} + void updateIcon(int mode); +protected: + virtual void activated(int iMsg); + virtual bool isActive(void); + virtual Gui::Action * createAction(void); +}; + +StdCmdUserEditMode::StdCmdUserEditMode() + : Command("Std_UserEditMode") +{ + sGroup = QT_TR_NOOP("Edit mode"); + sMenuText = QT_TR_NOOP("Edit mode"); + sToolTipText = QT_TR_NOOP("Defines behavior when editing an object from tree"); + sStatusTip = QT_TR_NOOP("Defines behavior when editing an object from tree"); + sWhatsThis = "Std_UserEditMode"; + sPixmap = "EditModeDefault"; + eType = ForEdit; + + this->getGuiApplication()->signalUserEditModeChanged.connect(boost::bind(&StdCmdUserEditMode::updateIcon, this, bp::_1)); +} + +Gui::Action * StdCmdUserEditMode::createAction(void) +{ + Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow()); + pcAction->setDropDownMenu(true); + pcAction->setIsMode(true); + applyCommandData(this->className(), pcAction); + + for (auto const &uem : Gui::Application::Instance->listUserEditModes()) { + QAction* act = pcAction->addAction(QString()); + auto modeName = QString::fromStdString(uem.second); + act->setCheckable(true); + act->setIcon(BitmapFactory().iconFromTheme(qPrintable(QString::fromLatin1("EditMode")+modeName))); + act->setObjectName(QString::fromLatin1("Std_EditMode")+modeName); + act->setWhatsThis(QString::fromLatin1(getWhatsThis())); + + if (uem.first == 0) { + pcAction->setIcon(act->icon()); + act->setChecked(true); + } + } + + _pcAction = pcAction; + languageChange(); + return pcAction; +} + +void StdCmdUserEditMode::languageChange() +{ + Command::languageChange(); + + if (!_pcAction) + return; + Gui::ActionGroup* pcAction = qobject_cast(_pcAction); + QList a = pcAction->actions(); + + for (int i = 0 ; i < a.count() ; i++) { + auto modeName = QString::fromStdString(Gui::Application::Instance->getUserEditModeName(i)); + a[i]->setText(QCoreApplication::translate( + "EditMode", qPrintable(modeName))); + a[i]->setToolTip(QCoreApplication::translate( + "EditMode", qPrintable(modeName+QString::fromLatin1(" mode")))); + } +} + +void StdCmdUserEditMode::updateIcon(int mode) +{ + Gui::ActionGroup *actionGroup = dynamic_cast(_pcAction); + if (!actionGroup) + return; + + actionGroup->setCheckedAction(mode); +} + +void StdCmdUserEditMode::activated(int iMsg) +{ + Gui::Application::Instance->setUserEditMode(iMsg); +} + +bool StdCmdUserEditMode::isActive(void) +{ + return true; +} + namespace Gui { void CreateStdCommands(void) @@ -842,6 +938,7 @@ void CreateStdCommands(void) rcCmdMgr.addCommand(new StdCmdPythonWebsite()); rcCmdMgr.addCommand(new StdCmdTextDocument()); rcCmdMgr.addCommand(new StdCmdUnitsCalculator()); + rcCmdMgr.addCommand(new StdCmdUserEditMode()); //rcCmdMgr.addCommand(new StdCmdMeasurementSimple()); //rcCmdMgr.addCommand(new StdCmdDownloadOnlineHelp()); //rcCmdMgr.addCommand(new StdCmdDescription()); diff --git a/src/Gui/Icons/EditModeColor.svg b/src/Gui/Icons/EditModeColor.svg new file mode 100644 index 0000000000..07d832dc28 --- /dev/null +++ b/src/Gui/Icons/EditModeColor.svg @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/Gui/Icons/EditModeCutting.svg b/src/Gui/Icons/EditModeCutting.svg new file mode 100644 index 0000000000..fb051257b8 --- /dev/null +++ b/src/Gui/Icons/EditModeCutting.svg @@ -0,0 +1,496 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Edit Cut + + + Garrett Le Sage + + + + + edit + cut + clipboard + + + + + + Jakub Steiner + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Gui/Icons/EditModeDefault.svg b/src/Gui/Icons/EditModeDefault.svg new file mode 100644 index 0000000000..a6183e8b0a --- /dev/null +++ b/src/Gui/Icons/EditModeDefault.svg @@ -0,0 +1,396 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + Preferences System + + + preferences + settings + control panel + tweaks + system + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Gui/Icons/EditModeTransform.svg b/src/Gui/Icons/EditModeTransform.svg new file mode 100644 index 0000000000..135ac9736e --- /dev/null +++ b/src/Gui/Icons/EditModeTransform.svg @@ -0,0 +1,231 @@ + + + Std_AxisCross + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Std_AxisCross + + + [bitacovir] + + + + + FreeCAD LGPL2+ + + + + + FreeCAD + + + 2020/12/20 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Gui/Icons/resource.qrc b/src/Gui/Icons/resource.qrc index 13ac71a492..541582a33a 100644 --- a/src/Gui/Icons/resource.qrc +++ b/src/Gui/Icons/resource.qrc @@ -241,6 +241,10 @@ document-package.svg Std_Alignment.svg Std_DuplicateSelection.svg + EditModeDefault.svg + EditModeTransform.svg + EditModeCutting.svg + EditModeColor.svg diff --git a/src/Gui/ViewProvider.h b/src/Gui/ViewProvider.h index 257f5a6167..1d5761a87e 100644 --- a/src/Gui/ViewProvider.h +++ b/src/Gui/ViewProvider.h @@ -420,6 +420,9 @@ public: * you can handle most of the events in the viewer by yourself */ //@{ + // the below enum is reflected in 'userEditModes' std::map in Application.h + // so it is possible for the user to choose a default one through GUI + // if you add a mode here, consider to make it accessible there too enum EditMode {Default = 0, Transform, Cutting, diff --git a/src/Gui/Workbench.cpp b/src/Gui/Workbench.cpp index 877f18d7df..46a39452cb 100644 --- a/src/Gui/Workbench.cpp +++ b/src/Gui/Workbench.cpp @@ -588,7 +588,7 @@ MenuItem* StdWorkbench::setupMenuBar() const << "Std_Refresh" << "Std_BoxSelection" << "Std_BoxElementSelection" << "Std_SelectAll" << "Std_Delete" << "Std_SendToPythonConsole" << "Separator" << "Std_Placement" << "Std_TransformManip" << "Std_Alignment" - << "Std_Edit" << "Separator" << "Std_DlgPreferences"; + << "Std_Edit" << "Separator" << "Std_UserEditMode" << "Separator" << "Std_DlgPreferences"; MenuItem* axoviews = new MenuItem; axoviews->setCommand("Axonometric"); @@ -713,7 +713,7 @@ ToolBarItem* StdWorkbench::setupToolBars() const file->setCommand("File"); *file << "Std_New" << "Std_Open" << "Std_Save" << "Std_Print" << "Separator" << "Std_Cut" << "Std_Copy" << "Std_Paste" << "Separator" << "Std_Undo" << "Std_Redo" << "Separator" - << "Std_Refresh" << "Separator" << "Std_WhatsThis"; + << "Std_UserEditMode" << "Separator" << "Std_Refresh" << "Separator" << "Std_WhatsThis"; // Workbench switcher ToolBarItem* wb = new ToolBarItem( root );