diff --git a/src/bonsai/bonsai/bim/module/project/__init__.py b/src/bonsai/bonsai/bim/module/project/__init__.py index 051769336f..ce77a0cd31 100644 --- a/src/bonsai/bonsai/bim/module/project/__init__.py +++ b/src/bonsai/bonsai/bim/module/project/__init__.py @@ -35,6 +35,7 @@ classes = ( operator.DisableCulling, operator.DisableEditingHeader, operator.EditHeader, + operator.EditProjectLibrary, operator.EnableCulling, operator.EnableEditingHeader, operator.ExportIFC, diff --git a/src/bonsai/bonsai/bim/module/project/data.py b/src/bonsai/bonsai/bim/module/project/data.py index 9a6c9855ab..a4f2f6793c 100644 --- a/src/bonsai/bonsai/bim/module/project/data.py +++ b/src/bonsai/bonsai/bim/module/project/data.py @@ -132,13 +132,15 @@ class ProjectLibraryData: return results @classmethod - def project_libraries_enum(cls) -> list[tuple[str, str, str]]: + def project_libraries_enum(cls) -> list[tuple[str, str, str, str, int]]: results = [ - ("*", "All Libraries", "Show all elements"), - ("-", "No Library", "Show elements without library assigned"), + ("*", "All Libraries", "Show all elements", "", 0), + ("-", "No Library", "Show elements without library assigned", "", 1), ] - for data in cls.data["project_libraries"].values(): - results.append((str(data["id"]), data["Name"] or "", data["Description"] or "")) + props = tool.Project.get_project_props() + for i, data in enumerate(cls.data["project_libraries"].values(), len(results)): + icon = "GREASEPENCIL" if props.editing_project_library_id == data["id"] else "" + results.append((str(data["id"]), data["Name"] or "", data["Description"] or "", icon, i)) return results diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index 60b263b6a7..24aef44dfd 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -25,6 +25,7 @@ import tempfile import traceback import subprocess import datetime +import ifcopenshell.api.attribute import numpy as np import ifcopenshell import ifcopenshell.api @@ -604,6 +605,40 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator): ifc_importer.create_style(element) +class EditProjectLibrary(bpy.types.Operator): + bl_idname = "bim.edit_project_library" + bl_label = "Edit Project Library" + bl_description = "Apply changes for currently edited library." + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + IfcStore.begin_transaction(self) + library_file = IfcStore.library_file + assert library_file + library_file.begin_transaction() + result = self._execute(context) + library_file.end_transaction() + IfcStore.add_transaction_operation(self) + IfcStore.end_transaction(self) + return result + + def _execute(self, context): + props = tool.Project.get_project_props() + library_file = IfcStore.library_file + assert library_file + project_library = library_file.by_id(props.editing_project_library_id) + attributes = bonsai.bim.helper.export_attributes(props.project_library_attributes) + ifcopenshell.api.attribute.edit_attributes(library_file, project_library, attributes) + props.is_editing_project_library = False + return {"FINISHED"} + + def rollback(self, data): + IfcStore.library_file.undo() + + def commit(self, data): + IfcStore.library_file.redo() + + class EnableEditingHeader(bpy.types.Operator): bl_idname = "bim.enable_editing_header" bl_label = "Enable Editing Header" diff --git a/src/bonsai/bonsai/bim/module/project/prop.py b/src/bonsai/bonsai/bim/module/project/prop.py index beed33db2c..f41cb949eb 100644 --- a/src/bonsai/bonsai/bim/module/project/prop.py +++ b/src/bonsai/bonsai/bim/module/project/prop.py @@ -21,9 +21,10 @@ import bpy import ifcopenshell.util.element import ifcopenshell.util.placement import bonsai.tool as tool +import bonsai.bim.helper from bonsai.bim.module.project.data import ProjectData, ProjectLibraryData from bonsai.bim.ifc import IfcStore -from bonsai.bim.prop import StrProperty, ObjProperty +from bonsai.bim.prop import StrProperty, ObjProperty, Attribute from bpy.types import PropertyGroup from bpy.props import ( PointerProperty, @@ -88,6 +89,22 @@ def filter_by_library_update(self: "BIMProjectProperties", context: bpy.types.Co bpy.ops.bim.refresh_library() +def is_editing_project_library_update(self: "BIMProjectProperties", context: bpy.types.Context) -> None: + if self.is_editing_project_library: + library_file = IfcStore.library_file + assert library_file + self.editing_project_library_id = int(self.selected_project_library) + project_library = library_file.by_id(int(self.selected_project_library)) + bonsai.bim.helper.import_attributes2(project_library, self.project_library_attributes) + ProjectLibraryData.load() # Show edit icon in enum. + return + + del self["is_editing_project_library"] + del self["editing_project_library_id"] + self.project_library_attributes.clear() + ProjectLibraryData.load() # Hide edit icon in enum. + + def update_filter_mode(self: "BIMProjectProperties", context: bpy.types.Context) -> None: self.filter_categories.clear() if self.filter_mode == "NONE": @@ -300,6 +317,15 @@ class BIMProjectProperties(PropertyGroup): default=True, update=filter_by_library_update, ) + is_editing_project_library: BoolProperty( + name="Is Editing Project Library", + description="Toggle editing for currently selected project library", + update=is_editing_project_library_update, + ) + editing_project_library_id: IntProperty( + description="Needed to keep track of currently edited library when user changes currently selected library in dropdown." + ) + project_library_attributes: CollectionProperty(name="Project Library Attributes", type=Attribute) use_relative_project_path: BoolProperty(name="Use Relative Project Path", default=False) queried_obj: bpy.props.PointerProperty(type=bpy.types.Object) @@ -367,6 +393,9 @@ class BIMProjectProperties(PropertyGroup): library_file: str selected_project_library: Union[Literal["*", "-"], str] filter_by_library: bool + is_editing_project_library: bool + editing_project_library_id: int + project_library_attributes: bpy.types.bpy_prop_collection_idprop[Attribute] use_relative_project_path: bool queried_obj: Union[bpy.types.Object, None] diff --git a/src/bonsai/bonsai/bim/module/project/ui.py b/src/bonsai/bonsai/bim/module/project/ui.py index d1c309df69..9c75d9b7cd 100644 --- a/src/bonsai/bonsai/bim/module/project/ui.py +++ b/src/bonsai/bonsai/bim/module/project/ui.py @@ -21,7 +21,7 @@ import bpy import os import bonsai.bim import bonsai.tool as tool -from bonsai.bim.helper import prop_with_search +from bonsai.bim.helper import prop_with_search, draw_attributes from bpy.types import Panel, Menu, UIList from bonsai.bim.ifc import IfcStore from bonsai.bim.module.project.data import ProjectData, LinksData @@ -344,7 +344,7 @@ class BIM_PT_project_library(Panel): def draw(self, context): self.layout.use_property_decorate = False self.layout.use_property_split = True - self.props = context.scene.BIMProjectProperties + self.props = tool.Project.get_project_props() row = self.layout.row(align=True) row.prop(self.props, "library_file", text="") if self.props.library_file == "0": @@ -364,16 +364,29 @@ class BIM_PT_project_library(Panel): def draw_library_ul(self): layout = self.layout + props = self.props if not self.props.library_elements: row = self.layout.row() row.label(text="No Assets Found", icon="ERROR") return + library_is_selected = props.selected_project_library not in ("*", "-") row = layout.row(align=True) row.prop(self.props, "selected_project_library", text="") + + if library_is_selected and not props.is_editing_project_library: + row.prop(props, "is_editing_project_library", text="", icon="GREASEPENCIL") + row.prop(self.props, "filter_by_library", text="", icon="FILTER") + if props.is_editing_project_library: + row = layout.row(align=True) + row.operator("bim.edit_project_library", text="Save Attributes", icon="GREASEPENCIL") + row.prop(props, "is_editing_project_library", text="", icon="CANCEL") + draw_attributes(props.project_library_attributes, layout) + layout.separator() + row = self.layout.row(align=True) row.label(text=self.props.active_library_element or "Top Level Assets") if self.props.active_library_element: