* Address the poor performance of the existing unique-name generation
As described in Issue 16849, the existing Tools::getUniqueName method
requires calling code to form a vector of existing names to be avoided.
This leads to poor performance both in the O(n) cost of building such a
vector and also getUniqueName's O(n) algorithm for actually generating
the unique name (where 'n' is the number of pre-existing names).
This has particularly noticeable cost in documents with large numbers
of DocumentObjects because generating both Names and Labels for each new
object incurs this cost. During an operation such as importing this
results in an O(n^2) time spent generating names.
The other major cost is in the saving of the temporary backup file,
which uses name generation for the "files" embedded in the Zip file.
Documents can easily need several such "files" for each object in the
document.
This update includes the following changes:
Create UniqueNameManager to keep a list of existing names organized in
a manner that eases unique-name generation. This class essentially acts
as a set of names, with the ability to add and remove names and check if
a name is already there, with the added ability to take a prototype name
and generate a unique form for it which is not already in the set.
Eliminate Tools::getUniqueName
Make DocumentObject naming use the new UniqueNameManager class
Make DocumentObject Label naming use the new UniqueNameManager class.
Labels are not always unique; unique labels are generated if the
settings at the time request it (and other conditions). Because of this
the Label management requires additionally keeping a map of counts
for labels which already exist more than once.
These collections are maintained via notifications of value changes on
the Label properties of the objects in the document.
Add Document::containsObject(DocumentObject*) for a definitive
test of an object being in a Document. This is needed because
DocumentObjects can be in a sort of limbo (e.g. when they are in the
Undo/Redo lists) where they have a parent linkage to the Document but
should not participate in Label collision checks.
Rename Document.getStandardObjectName to getStandardObjectLabel
to better represent what it does.
Use new UniqueNameManager for Writer internal filenames within the zip
file.
Eliminate unneeded Reader::FileNames collection. The file names
already exist in the FileList collection elements. The only existing
use for the FileNames collection was to determine if there were any
files at all, and with FileList and FileNames being parallel
vectors, they both had the same length so FileList could be used
for this test..
Use UniqueNameManager for document names and labels. This uses ad hoc
UniqueNameManager objects created on the spot on the assumption that
document creation is relatively rare and there are few documents, so
although the cost is O(n), n itself is small.
Use an ad hoc UniqueNameManager to name new DymanicProperty entries.
This is only done if a property of the proposed name already exists,
since such a check is more-or-less O(log(n)), almost never finds a
collision, and avoids the O(n) building of the UniqueNameManager.
If there is a collision an ad-hoc UniqueNameManager is built
and discarded after use.
The property management classes have a bit of a mess of methods
including several to populate various collection types with all
existing properties. Rather than introducing yet another such
collection-specific method to fill a UniqueNameManager, a
visitProperties method was added which calls a passed function for
each property. The existing code would be simpler if existing
fill-container methods all used this.
Ideally the PropertyContainer class would keep a central directory of
all properties ("static", Dynamic, and exposed by ExtensionContainer and
other derivations) and a permanent UniqueNameManager. However the
Property management is a bit of a mess making such a change a project
unto itself.
The unit tests for Tools:getUniqueName have been changed to test
UniqueNameManager.makeUniqueName instead.
This revealed a small regression insofar as passing a prototype name
like "xyz1234" to the old code would yield "xyz1235" whether or
not "xyz1234" already existed, while the new code will return the next
name above the currently-highest name on the "xyz" model, which could
be "xyz" or "xyz1".
* Correct wrong case on include path
* Implement suggested code changes
Also change the semantics of visitProperties to not have any short-circuit return
* Remove reference through undefined iterator
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Fix up some comments for DOxygen
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
* Add unit system to Project Information and store with document.
* Remove the project unit system
* Restore correct document activation signalling to fix test fail
* Remove commented out dead lines
* Restore ignore option for project unit schemas
* Whitespace fix
* Refresh after changing units
* Remove field label
* Property editor changes applied to unit system
===========================================================================
The former system of autoclosing messageboxes is removed in favour of the Notification Area.
=======================================
Document provides a new functionality, to signal subscribed user code of messages intended for the user.
The main motivation is critical messages of broken forward compatibility during restoring a document into a new version of FreeCAD.
However, the framework may be used in many other ways.
Found via `codespell -q 3 -L aci,ake,aline,alle,alledges,alocation,als,ang,anid,anormal,apoints,ba,beginn,behaviour,bloaded,bottome,byteorder,calculater,cancelled,cancelling,cas,cascade,centimetre,childrens,childs,colour,colours,commen,connexion,currenty,dof,doubleclick,dum,eiter,elemente,ende,feld,finde,findf,freez,hist,iff,indicies,initialisation,initialise,initialised,initialises,initialisiert,inout,ist,itsel,kilometre,lod,mantatory,methode,metres,millimetre,modell,nd,noe,normale,normaly,nto,numer,oce,oder,ontop,orgin,orginx,orginy,ot,pard,parm,parms,pres,programm,que,rady,recurrance,ro,rougly,seperator,serie,sinc,siz,strack,substraction,te,technic,thist,thru,tread,uint,unter,uptodate,vertexes,wallthickness,whitespaces -S ./.git,*.po,*.ts,./ChangeLog.txt,./src/3rdParty,./src/Mod/Assembly/App/opendcm,./src/CXX,./src/zipios++,./src/Base/swig*,./src/Mod/Robot/App/kdl_cp,./src/Mod/Import/App/SCL,./src/WindowsInstaller,./src/Doc/FreeCAD.uml,./src/Base/StackWalker.cpp,./build/doc/SourceDocu`
* Part: fix Placement/Shape onChanged() handling
* App: fix property ordering problem when undo/redo
See https://tracker.freecadweb.org/view.php?id=4265#c14271
* Gui: fix undo/redo signaling
Make sure to signal after all properties has been restored
The problem happens when partial loading is enabled. If document A
contains a link to some object in document B, it will load B as partial
document with only that object and its necessary dependencies. But if
document A contains another link to some object in document C which also
has a link to some object in document B, the link in document C may not
be restored, because document B is partially loaded without the linked
object. This patch will check for this case and reload document B for
more objects.
See an example reported in
https://forum.freecadweb.org/viewtopic.php?p=495078#p495078
========================================================
If the restore of Document.xml results in invalid Document.xml (because unhandled exceptions occurred), the
document status Document::restoreError is set. The GUI or Mod/Web if a link was clicked, show a pop-up indicating
this situation.
This commit also shows an appropriate pop-up for the partialRestore when opening from the menu, that before only
appeared when opening by clicking a link.
ONLY enabled if the Cloud Module is compiled into FreeCAD
ALL files must be saved into a Cloud Based storage
Add a Signal to the PropertyLink to enable support of external storage
Put the Assembly Document as active document
Signed-off-by: Jean-Marie Verdun <jmverdun3@gmail.com>
Add new argument to Application::newDocument() to create a temporary
document. Also exposed to Python API App.newDocument() with a named
argument 'temp'.
The temporary document is marked with status bit 'TempDoc'. The user
will not be prompt for saving when closing. The undo/redo is disabled.
The AutoSaver skips it. And the tree view will not show it.
PropertyXLink allows linking to/from object within a temporary document
without saving.
Found via `codespell -q 2 -L aci,ake,aline,alle,alledges,alocation,als,ang,anid,ba,beginn,behaviour,bloaded,byteorder,calculater,cancelled,cancelling,cas,cascade,centimetre,childs,colour,colours,commen,currenty,dof,doubleclick,dum,eiter,elemente,feld,freez,hist,iff,indicies,initialisation,initialise,initialised,initialises,initialisiert,ist,kilometre,lod,mantatory,methode,metres,millimetre,modell,nd,noe,normale,normaly,nto,numer,oder,orgin,orginx,orginy,ot,pard,pres,programm,que,recurrance,rougly,seperator,serie,sinc,strack,substraction,te,thist,thru,tread,uint,unter,vertexes,wallthickness,whitespaces -S ./.git,*.po,*.ts,./ChangeLog.txt,./src/3rdParty,./src/Mod/Assembly/App/opendcm,./src/CXX,./src/zipios++,./src/Base/swig*,./src/Mod/Robot/App/kdl_cp,./src/Mod/Import/App/SCL,./src/WindowsInstaller,./src/Doc/FreeCAD.uml`
This patch adds support of recomputation with external linked object,
as well as external document auto loading and partial loading.
Application:
* Modified new/openDocument()/signalNewDocument to choose whether to
signal GUI for creating a view for the document. This makes it possible
to suppress view creation when opening external documents.
* New API openDocuments() which does the actual job of loading the
document together with any external dependencies. There are afew
extra arguments to allow setting FileName property differently from
the actual file path, which are required when auto loading
dependencies during document recovery (with future patch to
Gui::DocumentRecovery)
* openDocumentPrivate() is an internal helper for opening individual
document.
* New signalStart/FinishOpenDocument to be signaled before and after
opening a document. There may be multiple depending documents actually
opened in between these two signals.
* New signalBeforeRecomputeDocument signaled before recompute a
document.
* New API addPendingDocument() for use by external capable link
properties' to queue up external documents.
* isRestoring/isClosingAll(), for convenience status reporting.
Document:
* signalFinishImport/RestoreObjects, new signal triggered after imported
or restored all input objects
* signalBeforeRecompute, signaled before start recomputing this document
* Modified signalRecomputed with additional recomputed objects, this is
to make it more efficient for Gui::TreeWidget to check recomputation
result.
* signalSkipRecompute, signal to inform which objects are skipped
during recomputation because of their owner document SkipRecompute
setting.
* restore/save/read/writeObjects() modified to suport partial
loading. See [here](https://git.io/fj6PY) for more information.
* afterRestore(), internal function called to finish restore. The
function is separated from restore() because there is quite a few
critical steps needed to fully restore a document with external
linking. See [here](https://git.io/fj6P4) for more information.
* DocumentP::_RecomputeLog is modified to store more accurate object
recomputation error, including those happened during restore/import.
* isExporting(), new API for checking if an object is exporting.
External linking properties will use this function to decide how to
export.
* copyObject(), modified to support external linking objects, and
accepts multiple input objects.
* moveObject(), modified to support arbitary object moves. The original
implementation may cause crash if undo/redo is enabled. Furthermore,
because the original information fakes the object's deletion to break
its dependency, it does not work for objects that may auto delete their
children when being deleted. The new implementation copy the object,
and than paste it to the other document. It then deletes the input
objects from the original document. In case of recursive move, it only
deletes the depending object if it has an empty in list.
* importLinks(), new API to import any external object linked by the
input objects into this document. It will auto correct all link
references after importing.
* getDependencyList/_rebuildDependencyList(), these two APIs are unified
and implemented by an internal function _buildDependencyList() with a
new algorithm to handle external objects. The returned dependency list
will now include objects from external documents. In case of cyclic
dependencies, getDpendencyList() will report the actual objects
involved in dependency loops.
* mustExecute(), new API to check if there are any object requires
recomputation. This function will call _buildDependencyList() and
check for external objects as well.
* addRecomputeObject(), new API for marking changes during document
restore. It only marks the object but does not actually recompute
them for performance reason. One use case is for geo feature to
request for recomputation to generate geometry topological names.
* recompute(), support partial, external, and inverse dependency
recomputation. Improve error handling during recomputation.
See [here](https://git.io/fj6PO) for more information.
* _recomputeFeature(), suppoert user abort.
* getDependentDocuments/getInList(), new API to obtain an optional
dependency sorted list of depending documents.
DocumentObject:
* Add various ObjectStatus flags
* isExporting/getExportName(), return a safe name for exporting, in the
form of <ObjName>@<DocName>, which is guarrenteed to be unique.
Various link property will save linked object using this name if the
the linked object is exported together with the owner object, see
[PropertyLinkBase::restoreLabelReference()](https://git.io/fj6XO)
for more information.
* recomputeFeature(), add option to recompute this object together with
all its dependent objects.
* canLoadPartial(), new API for [partial document loading](https://git.io/fj6PY).
MergeDocuments:
* Move object name mapping logic to various link properties. See
Base::Sequencer:
* Add new API checkAbort() for checking user abort.
Introduce a new concept of transaction ID. Each transaction must be
unique inside the document. Multiple transactions from different
documents can be grouped together with the same transaction ID.
This makes it possible to undo/redo single operation that contains
changes from multiple documents due to external linking.
Application:
* get/set/closeActiveTransaction() is used to setup potential
transactions with a given name. The transaction is only created when
there is actual changes. If objects from multiple documents are
changed under the same active transaction, they will have the same
trasnaction ID, and can be undo/redo togtether later.
* signalUndo/signalRedo, new signals triggered once after an undo/redo
operation. Unlike signalUndo/RedoDocument, these signals will only be
triggered once even if there may be multiple documents involved during
undo/redo.
* signal(Before)CloseTransaction, new signals triggered before/after an
actual transaction is created or aborted.
AutoTransaction:
* Helper class to enable automatic management of transactions. See class
document for more details. This class will be used by Gui::Command
in later patches to allow better automation of transactions in
command.
Document:
* open/commit/abortTransaction() are now redirected to call
Application::get/set/closeActiveTransaction() instead.
* _openTransaction() is added to do the real creation of transaction.
* _checkTransaction() is modified to create transaction on actual change
of any property.
* getTransactionID() is used to find out the position of a transaction
with a given ID. When triggering undo in external document, it may be
necessary to perform multi-step undo/redo in order to match for the
transaction ID.
Transaction/TransactionObject:
* Various changes for the new transaction ID concept.
* Support undo/redo add/remove dynamic property
Property:
* Extended property status bitset. Mirror most of PropertyType and
allow dynamic change property type.
* Cache property name and type to improve performance
* Centralize property status change signalling
* Change aboutToSetValue()/hasSetValue() to virtual
* Add new API getFullName() to obtain full quanlified name of the property
AtomicPropertyChangeInterface:
* Allow calling aboutToSetValue()/hasSetValue() when actually changed
PropertyLists:
* Refactor implementation by an abstract class PropertyListBase and a
template class PropertyListsT, to allow better code reuse.
PropertyListT is derived from AtomicPropertyChangeInterface to allow
more efficient change on individual elements.
* All list type property now accept setting python value as a dictionary
with index as key to set individual element of a list.
* Add touch list for more efficient handling of value changes. The list
contains the index of changed value. And empty touch list should be
treated as the entire list is changed. PropertyContainerPy expose this
functionality with getPropertyTouchList().
PropertyPersistentObject:
* New property to allow dynamic creation of any FreeCAD object derived
from Base::Persistence, and use it as a property.
DynamicProperty:
* Use boost multi_index_container for efficient property lookup while
keeping order.
* Modify to be allowed to use in PropertyContainer directly
PropertyContainer:
* Use boost multi_index_container for efficient property lookup while
keeping order.
* Allow adding/removing dynamic property on all property container
* Modify Save/Restore() to persist property status, and better handle
transient property which can now be dynamically enabled/disabled per
object.
* Add new API getFullName() to obtain full quanlified name of the property.
Implemented by Document, DocumentObject, and also
ViewProviderDocumentObject if future patch
DocumentObject and FeaturePython are modified to accommondate the
dynamic property changes.
Removed get/setCustomAttribute() implementation from DocumentObjectPy,
and rely on PropertyContainerPy for the implementation, because of the
additional dynamic property support in property container.
Gui::ViewProviderDocumentObject, which is derived from
PropertyContainer, is also modified accordingly