Skip to content

Commit

Permalink
DocumentObject: add allowDuplicateLabel/onBeforeChangeLabel()
Browse files Browse the repository at this point in the history
These two APIs allow an object to control its own labeling rules.
The previous auto re-labeling for uniqueness logic is moved from
ObjectLabelObserver (resides in Application.cpp) to PropertyString.

DocumentObject::allowDuplicateLabel() is used to inform PropertyString
that the object allows duplicate label regardless of application
setting. onBeforeChangeLabel() is called before actual label change to
give object a chance to override the new label.
  • Loading branch information
realthunder committed Jul 16, 2019
1 parent 8026ff1 commit 429c484
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 123 deletions.
106 changes: 0 additions & 106 deletions src/App/Application.cpp
Expand Up @@ -155,35 +155,6 @@ using namespace Base;
using namespace App;
using namespace std;

/** Observer that watches relabeled objects and make sure that the labels inside
* a document are unique.
* @note In the FreeCAD design it is explicitly allowed to have duplicate labels
* (i.e. the user visible text e.g. in the tree view) while the internal names
* are always guaranteed to be unique.
*/
class ObjectLabelObserver
{
public:
/// The one and only instance.
static ObjectLabelObserver* instance();
/// Destructs the sole instance.
static void destruct ();

/** Checks the new label of the object and relabel it if needed
* to make it unique document-wide
*/
void slotRelabelObject(const App::DocumentObject&, const App::Property&);

private:
static ObjectLabelObserver* _singleton;

ObjectLabelObserver();
~ObjectLabelObserver();
const App::DocumentObject* current;
ParameterGrp::handle _hPGrp;
};


//==========================================================================
// Application
//==========================================================================
Expand Down Expand Up @@ -1915,7 +1886,6 @@ void Application::initApplication(void)
try {
Interpreter().runString(Base::ScriptFactory().ProduceScript("CMakeVariables"));
Interpreter().runString(Base::ScriptFactory().ProduceScript("FreeCADInit"));
ObjectLabelObserver::instance();
}
catch (const Base::Exception& e) {
e.ReportException();
Expand Down Expand Up @@ -2862,79 +2832,3 @@ std::string Application::FindHomePath(const char* sCall)
# error "std::string Application::FindHomePath(const char*) not implemented"
#endif

ObjectLabelObserver* ObjectLabelObserver::_singleton = 0;

ObjectLabelObserver* ObjectLabelObserver::instance()
{
if (!_singleton)
_singleton = new ObjectLabelObserver;
return _singleton;
}

void ObjectLabelObserver::destruct ()
{
delete _singleton;
_singleton = 0;
}

void ObjectLabelObserver::slotRelabelObject(const App::DocumentObject& obj, const App::Property& prop)
{
// observe only the Label property
if (&prop == &obj.Label) {
// have we processed this (or another?) object right now?
if (current) {
return;
}

std::string label = obj.Label.getValue();
App::Document* doc = obj.getDocument();
if (doc && !_hPGrp->GetBool("DuplicateLabels")) {
std::vector<std::string> objectLabels;
std::vector<App::DocumentObject*>::const_iterator it;
std::vector<App::DocumentObject*> objs = doc->getObjects();
bool match = false;

for (it = objs.begin();it != objs.end();++it) {
if (*it == &obj)
continue; // don't compare object with itself
std::string objLabel = (*it)->Label.getValue();
if (!match && objLabel == label)
match = true;
objectLabels.push_back(objLabel);
}

// make sure that there is a name conflict otherwise we don't have to do anything
if (match && !label.empty()) {
// remove number from end to avoid lengthy names
size_t lastpos = label.length()-1;
while (label[lastpos] >= 48 && label[lastpos] <= 57) {
// if 'lastpos' becomes 0 then all characters are digits. In this case we use
// the complete label again
if (lastpos == 0) {
lastpos = label.length()-1;
break;
}
lastpos--;
}

label = label.substr(0, lastpos+1);
label = Base::Tools::getUniqueName(label, objectLabels, 3);
this->current = &obj;
const_cast<App::DocumentObject&>(obj).Label.setValue(label);
this->current = 0;
}
}
}
}

ObjectLabelObserver::ObjectLabelObserver() : current(0)
{
App::GetApplication().signalChangedObject.connect(boost::bind
(&ObjectLabelObserver::slotRelabelObject, this, _1, _2));
_hPGrp = App::GetApplication().GetUserParameter().GetGroup("BaseApp");
_hPGrp = _hPGrp->GetGroup("Preferences")->GetGroup("Document");
}

ObjectLabelObserver::~ObjectLabelObserver()
{
}
11 changes: 11 additions & 0 deletions src/App/DocumentObject.h
Expand Up @@ -341,6 +341,17 @@ class AppExport DocumentObject: public App::TransactionalObject
/* Return true to cause PropertyView to show linked object's property */
virtual bool canLinkProperties() const {return true;}

/* Return true to bypass duplicate label checking */
virtual bool allowDuplicateLabel() const {return false;}

/*** Called to let object itself control relabeling
*
* @param newLabel: input as the new label, which can be modified by object itself
*
* This function is is called before onBeforeChange()
*/
virtual void onBeforeChangeLabel(std::string &newLabel) {(void)newLabel;}

friend class Document;
friend class Transaction;
friend class ObjectExecution;
Expand Down
6 changes: 6 additions & 0 deletions src/App/DocumentObjectPy.xml
Expand Up @@ -262,5 +262,11 @@ or None if the GUI is not up</UserDocu>
</Documentation>
<Parameter Name="Parents" Type="List"/>
</Attribute>
<Attribute Name="OldLabel" ReadOnly="true">
<Documentation>
<UserDocu>Contains the old label before change</UserDocu>
</Documentation>
<Parameter Name="OldLabel" Type="String"/>
</Attribute>
</PythonExport>
</GenerateModel>
4 changes: 4 additions & 0 deletions src/App/DocumentObjectPyImp.cpp
Expand Up @@ -834,3 +834,7 @@ PyObject *DocumentObjectPy::adjustRelativeLinks(PyObject *args) {
PyObject_IsTrue(recursive)?&visited:nullptr)));
}PY_CATCH
}

Py::String DocumentObjectPy::getOldLabel() const {
return Py::String(getDocumentObjectPtr()->getOldLabel());
}

0 comments on commit 429c484

Please sign in to comment.