From 32e7083e21bd558d3e5b128bd3ecfdfef0871a74 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 6 Jul 2021 14:21:02 +1000 Subject: [PATCH] Implement undo for pset template operators. See #1475. --- src/blenderbim/blenderbim/bim/handler.py | 2 + src/blenderbim/blenderbim/bim/ifc.py | 34 +++--- .../blenderbim/bim/module/project/operator.py | 12 ++- .../bim/module/pset_template/operator.py | 101 ++++++++++++++++++ 4 files changed, 123 insertions(+), 26 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/handler.py b/src/blenderbim/blenderbim/bim/handler.py index f4d9945518..65869099e0 100644 --- a/src/blenderbim/blenderbim/bim/handler.py +++ b/src/blenderbim/blenderbim/bim/handler.py @@ -124,6 +124,7 @@ def undo_post(scene): if IfcStore.last_transaction != bpy.context.scene.BIMProperties.last_transaction: IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction IfcStore.undo() + purge_module_data() IfcStore.update_undo_redo_stack_objects() IfcStore.reload_linked_elements(objects=[bpy.data.objects.get(o) for o in IfcStore.undo_redo_stack_objects]) @@ -138,6 +139,7 @@ def redo_post(scene): if IfcStore.last_transaction != bpy.context.scene.BIMProperties.last_transaction: IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction IfcStore.redo() + purge_module_data() IfcStore.update_undo_redo_stack_objects() IfcStore.reload_linked_elements(objects=[bpy.data.objects.get(o) for o in IfcStore.undo_redo_stack_objects]) diff --git a/src/blenderbim/blenderbim/bim/ifc.py b/src/blenderbim/blenderbim/bim/ifc.py index 2da38e04d5..320813844d 100644 --- a/src/blenderbim/blenderbim/bim/ifc.py +++ b/src/blenderbim/blenderbim/bim/ifc.py @@ -71,19 +71,19 @@ class IfcStore: def add_element_listener(callback): IfcStore.element_listeners.add(callback) - """Keeps track of selected object names, typically during undo and redo - - When any Blender object is stored outside a Blender PointerProperty, such as - in a regular Python list, there is the likely probability that the object - will be invalidated when undo or redo occurs. Object invalidation seems to - only occur for selected objects either pre/post undo/redo event, including - selected objects for consecutive undo/redos. - - So if I first select o1, then o2, then o3, then press undo, o3 will be - invalidated. If instead I press undo twice, o3 and o2 will be invalidated. - """ @staticmethod def update_undo_redo_stack_objects(): + """Keeps track of selected object names, typically during undo and redo + + When any Blender object is stored outside a Blender PointerProperty, such as + in a regular Python list, there is the likely probability that the object + will be invalidated when undo or redo occurs. Object invalidation seems to + only occur for selected objects either pre/post undo/redo event, including + selected objects for consecutive undo/redos. + + So if I first select o1, then o2, then o3, then press undo, o3 will be + invalidated. If instead I press undo twice, o3 and o2 will be invalidated. + """ if bpy.context.active_object: objects = set([o.name for o in bpy.context.selected_objects + [bpy.context.active_object]]) else: @@ -183,22 +183,12 @@ class IfcStore: if is_top_level_operator: IfcStore.get_file().end_transaction() IfcStore.add_transaction_operation( - operator, rollback=IfcStore.rollback_ifc_operator, commit=IfcStore.commit_ifc_operator + operator, rollback=lambda d: IfcStore.get_file().undo(), commit=lambda d: IfcStore.get_file().redo() ) IfcStore.end_transaction(operator) return result - @staticmethod - def rollback_ifc_operator(data): - IfcStore.get_file().undo() - blenderbim.bim.handler.purge_module_data() - - @staticmethod - def commit_ifc_operator(data): - IfcStore.get_file().redo() - blenderbim.bim.handler.purge_module_data() - @staticmethod def begin_transaction(operator): IfcStore.undo_redo_stack_objects = set() diff --git a/src/blenderbim/blenderbim/bim/module/project/operator.py b/src/blenderbim/blenderbim/bim/module/project/operator.py index f01e937fdb..9cf97be604 100644 --- a/src/blenderbim/blenderbim/bim/module/project/operator.py +++ b/src/blenderbim/blenderbim/bim/module/project/operator.py @@ -64,10 +64,8 @@ class CreateProject(bpy.types.Operator): def rollback(self, data): IfcStore.file = None - blenderbim.bim.handler.purge_module_data() def commit(self, data): - blenderbim.bim.handler.purge_module_data() IfcStore.file = data["file"] @@ -106,10 +104,8 @@ class CreateProjectLibrary(bpy.types.Operator): def rollback(self, data): IfcStore.file = None - blenderbim.bim.handler.purge_module_data() def commit(self, data): - blenderbim.bim.handler.purge_module_data() IfcStore.file = data["file"] @@ -121,10 +117,12 @@ class SelectLibraryFile(bpy.types.Operator): filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"}) def execute(self, context): + IfcStore.begin_transaction(self) old_filepath = IfcStore.library_path result = self._execute(context) self.transaction_data = {"old_filepath": old_filepath, "filepath": self.filepath} IfcStore.add_transaction_operation(self) + IfcStore.end_transaction(self) return result def _execute(self, context): @@ -229,10 +227,12 @@ class AssignLibraryDeclaration(bpy.types.Operator): definition: bpy.props.IntProperty() def execute(self, context): + IfcStore.begin_transaction(self) IfcStore.library_file.begin_transaction() result = self._execute(context) IfcStore.library_file.end_transaction() IfcStore.add_transaction_operation(self) + IfcStore.end_transaction(self) return result def _execute(self, context): @@ -263,10 +263,12 @@ class UnassignLibraryDeclaration(bpy.types.Operator): definition: bpy.props.IntProperty() def execute(self, context): + IfcStore.begin_transaction(self) IfcStore.library_file.begin_transaction() result = self._execute(context) IfcStore.library_file.end_transaction() IfcStore.add_transaction_operation(self) + IfcStore.end_transaction(self) return result def _execute(self, context): @@ -378,11 +380,13 @@ class EditHeader(bpy.types.Operator): bl_options = {"REGISTER", "UNDO"} def execute(self, context): + IfcStore.begin_transaction(self) self.transaction_data = {} self.transaction_data["old"] = self.record_state() result = self._execute(context) self.transaction_data["new"] = self.record_state() IfcStore.add_transaction_operation(self) + IfcStore.end_transaction(self) return result def _execute(self, context): diff --git a/src/blenderbim/blenderbim/bim/module/pset_template/operator.py b/src/blenderbim/blenderbim/bim/module/pset_template/operator.py index d83737be2e..13bf59933d 100644 --- a/src/blenderbim/blenderbim/bim/module/pset_template/operator.py +++ b/src/blenderbim/blenderbim/bim/module/pset_template/operator.py @@ -9,20 +9,46 @@ from blenderbim.bim.ifc import IfcStore class AddPsetTemplate(bpy.types.Operator): bl_idname = "bim.add_pset_template" bl_label = "Add Pset Template" + bl_options = {"REGISTER", "UNDO"} def execute(self, context): + IfcStore.begin_transaction(self) + IfcStore.pset_template_file.begin_transaction() + result = self._execute(context) + IfcStore.pset_template_file.end_transaction() + IfcStore.add_transaction_operation(self) + IfcStore.end_transaction(self) + return result + + def _execute(self, context): props = context.scene.BIMPsetTemplateProperties ifcopenshell.api.run("pset_template.add_pset_template", IfcStore.pset_template_file) Data.load(IfcStore.pset_template_file) updatePsetTemplates(self, context) return {"FINISHED"} + def rollback(self, data): + IfcStore.pset_template_file.undo() + + def commit(self, data): + IfcStore.pset_template_file.redo() + class RemovePsetTemplate(bpy.types.Operator): bl_idname = "bim.remove_pset_template" bl_label = "Remove Pset Template" + bl_options = {"REGISTER", "UNDO"} def execute(self, context): + IfcStore.begin_transaction(self) + IfcStore.pset_template_file.begin_transaction() + result = self._execute(context) + IfcStore.pset_template_file.end_transaction() + IfcStore.add_transaction_operation(self) + IfcStore.end_transaction(self) + return result + + def _execute(self, context): props = context.scene.BIMPsetTemplateProperties if props.active_pset_template_id == int(props.pset_templates): bpy.ops.bim.disable_editing_pset_template() @@ -33,10 +59,17 @@ class RemovePsetTemplate(bpy.types.Operator): updatePsetTemplates(self, context) return {"FINISHED"} + def rollback(self, data): + IfcStore.pset_template_file.undo() + + def commit(self, data): + IfcStore.pset_template_file.redo() + class EnableEditingPsetTemplate(bpy.types.Operator): bl_idname = "bim.enable_editing_pset_template" bl_label = "Enable Editing Pset Template" + bl_options = {"REGISTER", "UNDO"} def execute(self, context): props = context.scene.BIMPsetTemplateProperties @@ -53,6 +86,7 @@ class EnableEditingPsetTemplate(bpy.types.Operator): class DisableEditingPsetTemplate(bpy.types.Operator): bl_idname = "bim.disable_editing_pset_template" bl_label = "Disable Editing Pset Template" + bl_options = {"REGISTER", "UNDO"} def execute(self, context): props = context.scene.BIMPsetTemplateProperties @@ -63,6 +97,7 @@ class DisableEditingPsetTemplate(bpy.types.Operator): class EnableEditingPropTemplate(bpy.types.Operator): bl_idname = "bim.enable_editing_prop_template" bl_label = "Enable Editing Prop Template" + bl_options = {"REGISTER", "UNDO"} prop_template: bpy.props.IntProperty() def execute(self, context): @@ -78,6 +113,7 @@ class EnableEditingPropTemplate(bpy.types.Operator): class DisableEditingPropTemplate(bpy.types.Operator): bl_idname = "bim.disable_editing_prop_template" bl_label = "Disable Editing Prop Template" + bl_options = {"REGISTER", "UNDO"} def execute(self, context): props = context.scene.BIMPsetTemplateProperties @@ -88,8 +124,18 @@ class DisableEditingPropTemplate(bpy.types.Operator): class EditPsetTemplate(bpy.types.Operator): bl_idname = "bim.edit_pset_template" bl_label = "Edit Pset Template" + bl_options = {"REGISTER", "UNDO"} def execute(self, context): + IfcStore.begin_transaction(self) + IfcStore.pset_template_file.begin_transaction() + result = self._execute(context) + IfcStore.pset_template_file.end_transaction() + IfcStore.add_transaction_operation(self) + IfcStore.end_transaction(self) + return result + + def _execute(self, context): props = context.scene.BIMPsetTemplateProperties ifcopenshell.api.run("pset_template.edit_pset_template", IfcStore.pset_template_file, **{ "pset_template": IfcStore.pset_template_file.by_id(props.active_pset_template_id), @@ -105,6 +151,13 @@ class EditPsetTemplate(bpy.types.Operator): bpy.ops.bim.disable_editing_pset_template() return {"FINISHED"} + def rollback(self, data): + IfcStore.pset_template_file.undo() + + def commit(self, data): + IfcStore.pset_template_file.redo() + + class SavePsetTemplateFile(bpy.types.Operator): bl_idname = "bim.save_pset_template_file" @@ -118,8 +171,18 @@ class SavePsetTemplateFile(bpy.types.Operator): class AddPropTemplate(bpy.types.Operator): bl_idname = "bim.add_prop_template" bl_label = "Add Prop Template" + bl_options = {"REGISTER", "UNDO"} def execute(self, context): + IfcStore.begin_transaction(self) + IfcStore.pset_template_file.begin_transaction() + result = self._execute(context) + IfcStore.pset_template_file.end_transaction() + IfcStore.add_transaction_operation(self) + IfcStore.end_transaction(self) + return result + + def _execute(self, context): props = context.scene.BIMPsetTemplateProperties pset_template_id = props.active_pset_template_id or int(props.pset_templates) ifcopenshell.api.run("pset_template.add_prop_template", IfcStore.pset_template_file, **{ @@ -128,13 +191,29 @@ class AddPropTemplate(bpy.types.Operator): Data.load(IfcStore.pset_template_file) return {"FINISHED"} + def rollback(self, data): + IfcStore.pset_template_file.undo() + + def commit(self, data): + IfcStore.pset_template_file.redo() + class RemovePropTemplate(bpy.types.Operator): bl_idname = "bim.remove_prop_template" bl_label = "Remove Prop Template" + bl_options = {"REGISTER", "UNDO"} prop_template: bpy.props.IntProperty() def execute(self, context): + IfcStore.begin_transaction(self) + IfcStore.pset_template_file.begin_transaction() + result = self._execute(context) + IfcStore.pset_template_file.end_transaction() + IfcStore.add_transaction_operation(self) + IfcStore.end_transaction(self) + return result + + def _execute(self, context): props = context.scene.BIMPsetTemplateProperties ifcopenshell.api.run("pset_template.remove_prop_template", IfcStore.pset_template_file, **{ "prop_template": IfcStore.pset_template_file.by_id(self.prop_template) @@ -142,12 +221,28 @@ class RemovePropTemplate(bpy.types.Operator): Data.load(IfcStore.pset_template_file) return {"FINISHED"} + def rollback(self, data): + IfcStore.pset_template_file.undo() + + def commit(self, data): + IfcStore.pset_template_file.redo() + class EditPropTemplate(bpy.types.Operator): bl_idname = "bim.edit_prop_template" bl_label = "Edit Prop Template" + bl_options = {"REGISTER", "UNDO"} def execute(self, context): + IfcStore.begin_transaction(self) + IfcStore.pset_template_file.begin_transaction() + result = self._execute(context) + IfcStore.pset_template_file.end_transaction() + IfcStore.add_transaction_operation(self) + IfcStore.end_transaction(self) + return result + + def _execute(self, context): props = context.scene.BIMPsetTemplateProperties ifcopenshell.api.run("pset_template.edit_prop_template", IfcStore.pset_template_file, **{ "prop_template": IfcStore.pset_template_file.by_id(props.active_prop_template_id), @@ -160,3 +255,9 @@ class EditPropTemplate(bpy.types.Operator): Data.load(IfcStore.pset_template_file) bpy.ops.bim.disable_editing_prop_template() return {"FINISHED"} + + def rollback(self, data): + IfcStore.pset_template_file.undo() + + def commit(self, data): + IfcStore.pset_template_file.redo()