Fix error in rigify property generation
[blender-addons.git] / object_edit_linked.py
blobb644598115acda11f0c98080e9b55ae091dd7d85
1 # ***** BEGIN GPL LICENSE BLOCK *****
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # ***** END GPL LICENCE BLOCK *****
20 bl_info = {
21 "name": "Edit Linked Library",
22 "author": "Jason van Gumster (Fweeb), Bassam Kurdali, Pablo Vazquez, Rainer Trummer",
23 "version": (0, 9, 1),
24 "blender": (2, 80, 0),
25 "location": "File > External Data / View3D > Sidebar > Item Tab",
26 "description": "Allows editing of objects linked from a .blend library.",
27 "doc_url": "{BLENDER_MANUAL_URL}/addons/object/edit_linked_library.html",
28 "category": "Object",
31 import bpy
32 import logging
33 import os
35 from bpy.app.handlers import persistent
37 logger = logging.getLogger('object_edit_linked')
39 settings = {
40 "original_file": "",
41 "linked_file": "",
42 "linked_objects": [],
46 @persistent
47 def linked_file_check(context: bpy.context):
48 if settings["linked_file"] != "":
49 if os.path.samefile(settings["linked_file"], bpy.data.filepath):
50 logger.info("Editing a linked library.")
51 bpy.ops.object.select_all(action='DESELECT')
52 for ob_name in settings["linked_objects"]:
53 bpy.data.objects[ob_name].select_set(True) # XXX Assumes selected object is in the active scene
54 if len(settings["linked_objects"]) == 1:
55 context.view_layer.objects.active = bpy.data.objects[settings["linked_objects"][0]]
56 else:
57 # For some reason, the linked editing session ended
58 # (failed to find a file or opened a different file
59 # before returning to the originating .blend)
60 settings["original_file"] = ""
61 settings["linked_file"] = ""
64 class OBJECT_OT_EditLinked(bpy.types.Operator):
65 """Edit Linked Library"""
66 bl_idname = "object.edit_linked"
67 bl_label = "Edit Linked Library"
69 use_autosave: bpy.props.BoolProperty(
70 name="Autosave",
71 description="Save the current file before opening the linked library",
72 default=True)
73 use_instance: bpy.props.BoolProperty(
74 name="New Blender Instance",
75 description="Open in a new Blender instance",
76 default=False)
78 @classmethod
79 def poll(cls, context: bpy.context):
80 return settings["original_file"] == "" and context.active_object is not None and (
81 (context.active_object.instance_collection and
82 context.active_object.instance_collection.library is not None) or
83 (context.active_object.proxy and
84 context.active_object.proxy.library is not None) or
85 context.active_object.library is not None)
87 def execute(self, context: bpy.context):
88 target = context.active_object
90 if target.instance_collection and target.instance_collection.library:
91 targetpath = target.instance_collection.library.filepath
92 settings["linked_objects"].extend({ob.name for ob in target.instance_collection.objects})
93 elif target.library:
94 targetpath = target.library.filepath
95 settings["linked_objects"].append(target.name)
96 elif target.proxy:
97 target = target.proxy
98 targetpath = target.library.filepath
99 settings["linked_objects"].append(target.name)
101 if targetpath:
102 logger.debug(target.name + " is linked to " + targetpath)
104 if self.use_autosave:
105 if not bpy.data.filepath:
106 # File is not saved on disk, better to abort!
107 self.report({'ERROR'}, "Current file does not exist on disk, we cannot autosave it, aborting")
108 return {'CANCELLED'}
109 bpy.ops.wm.save_mainfile()
111 settings["original_file"] = bpy.data.filepath
112 settings["linked_file"] = bpy.path.abspath(targetpath)
114 if self.use_instance:
115 import subprocess
116 try:
117 subprocess.Popen([bpy.app.binary_path, settings["linked_file"]])
118 except:
119 logger.error("Error on the new Blender instance")
120 import traceback
121 logger.error(traceback.print_exc())
122 else:
123 bpy.ops.wm.open_mainfile(filepath=settings["linked_file"])
125 logger.info("Opened linked file!")
126 else:
127 self.report({'WARNING'}, target.name + " is not linked")
128 logger.warning(target.name + " is not linked")
130 return {'FINISHED'}
133 class WM_OT_ReturnToOriginal(bpy.types.Operator):
134 """Load the original file"""
135 bl_idname = "wm.return_to_original"
136 bl_label = "Return to Original File"
138 use_autosave: bpy.props.BoolProperty(
139 name="Autosave",
140 description="Save the current file before opening original file",
141 default=True)
143 @classmethod
144 def poll(cls, context: bpy.context):
145 return (settings["original_file"] != "")
147 def execute(self, context: bpy.context):
148 if self.use_autosave:
149 bpy.ops.wm.save_mainfile()
151 bpy.ops.wm.open_mainfile(filepath=settings["original_file"])
153 settings["original_file"] = ""
154 settings["linked_objects"] = []
155 logger.info("Back to the original!")
156 return {'FINISHED'}
159 class VIEW3D_PT_PanelLinkedEdit(bpy.types.Panel):
160 bl_label = "Edit Linked Library"
161 bl_space_type = "VIEW_3D"
162 bl_region_type = 'UI'
163 bl_category = "Item"
164 bl_context = 'objectmode'
165 bl_options = {'DEFAULT_CLOSED'}
167 @classmethod
168 def poll(cls, context: bpy.context):
169 return (context.active_object is not None) or (settings["original_file"] != "")
171 def draw_common(self, scene, layout, props):
172 props.use_autosave = scene.use_autosave
173 props.use_instance = scene.use_instance
175 layout.prop(scene, "use_autosave")
176 # layout.prop(scene, "use_instance")
178 def draw(self, context: bpy.context):
179 scene = context.scene
180 layout = self.layout
181 layout.use_property_split = True
182 layout.use_property_decorate = False
183 icon = "OUTLINER_DATA_" + context.active_object.type.replace("LIGHT_PROBE", "LIGHTPROBE")
185 target = None
187 if context.active_object.proxy:
188 target = context.active_object.proxy
189 else:
190 target = context.active_object.instance_collection
192 if settings["original_file"] == "" and (
193 (target and
194 target.library is not None) or
195 context.active_object.library is not None):
197 if (target is not None):
198 props = layout.operator("object.edit_linked", icon="LINK_BLEND",
199 text="Edit Library: %s" % target.name)
200 else:
201 props = layout.operator("object.edit_linked", icon="LINK_BLEND",
202 text="Edit Library: %s" % context.active_object.name)
204 self.draw_common(scene, layout, props)
206 if (target is not None):
207 layout.label(text="Path: %s" %
208 target.library.filepath)
209 else:
210 layout.label(text="Path: %s" %
211 context.active_object.library.filepath)
213 elif settings["original_file"] != "":
215 if scene.use_instance:
216 layout.operator("wm.return_to_original",
217 text="Reload Current File",
218 icon="FILE_REFRESH").use_autosave = False
220 layout.separator()
222 # XXX - This is for nested linked assets... but it only works
223 # when launching a new Blender instance. Nested links don't
224 # currently work when using a single instance of Blender.
225 props = layout.operator("object.edit_linked",
226 text="Edit Library: %s" % context.active_object.instance_collection.name,
227 icon="LINK_BLEND")
229 self.draw_common(scene, layout, props)
231 layout.label(text="Path: %s" %
232 context.active_object.instance_collection.library.filepath)
234 else:
235 props = layout.operator("wm.return_to_original", icon="LOOP_BACK")
236 props.use_autosave = scene.use_autosave
238 layout.prop(scene, "use_autosave")
240 else:
241 layout.label(text="%s is not linked" % context.active_object.name,
242 icon=icon)
245 class TOPBAR_MT_edit_linked_submenu(bpy.types.Menu):
246 bl_label = 'Edit Linked Library'
248 def draw(self, context):
249 self.layout.separator()
250 self.layout.operator(OBJECT_OT_EditLinked.bl_idname)
251 self.layout.operator(WM_OT_ReturnToOriginal.bl_idname)
254 addon_keymaps = []
255 classes = (
256 OBJECT_OT_EditLinked,
257 WM_OT_ReturnToOriginal,
258 VIEW3D_PT_PanelLinkedEdit,
259 TOPBAR_MT_edit_linked_submenu
263 def register():
264 bpy.app.handlers.load_post.append(linked_file_check)
266 for c in classes:
267 bpy.utils.register_class(c)
269 bpy.types.Scene.use_autosave = bpy.props.BoolProperty(
270 name="Autosave",
271 description="Save the current file before opening a linked file",
272 default=True)
274 bpy.types.Scene.use_instance = bpy.props.BoolProperty(
275 name="New Blender Instance",
276 description="Open in a new Blender instance",
277 default=False)
279 # add the function to the file menu
280 bpy.types.TOPBAR_MT_file_external_data.append(TOPBAR_MT_edit_linked_submenu.draw)
285 def unregister():
287 bpy.app.handlers.load_post.remove(linked_file_check)
288 bpy.types.TOPBAR_MT_file_external_data.remove(TOPBAR_MT_edit_linked_submenu)
290 del bpy.types.Scene.use_autosave
291 del bpy.types.Scene.use_instance
294 for c in reversed(classes):
295 bpy.utils.unregister_class(c)
298 if __name__ == "__main__":
299 register()